ИИ для веб-разработки
- четверг, 23 января 2025 г. в 00:00:06
Привет, друзья!
В праздники я провел небольшое исследование представленных сегодня на рынке бесплатных инструментов с участием "искусственного интеллекта" для помощи в веб-разработке, результатами которого хочу с вами поделиться, авось кому-нибудь пригодится.
Обратите внимание, что я акцентировал внимание на бесплатных решениях для написания кода клиентской части веб-приложений.
Список протестированных инструментов:
Далее я подробнее расскажу о первых трех (Codeium, Devv, V0) и немного о двух следующих за ними (ChatGPT, GPT4All) решениях из представленных в списке, как наиболее "выдающихся" с моей точки зрения. Остальные сервисы (начиная с cursor
и ниже) в той или иной степени похожи на codeium
и `devv`, но показывают более плохие результаты генерации кода по запросу, исправления ошибок и документирования кода, а также менее удобны в использовании и иногда требуют дополнительных настроек, например, указания ключа OpenAI, установки лишних инструментов (тулкитов), являются условно бесплатными и т.п.
Сodeium — это набор инструментов для помощи в написании нового и улучшении существующего кода, документации к нему и т.д. Работает в контексте кодовой базы, т.е. предложения по дополнению/модификации основаны на коде проекта, его стиле (используемых подходах и принципах), форматировании и т.п. Поставляется в виде расширения для VSCode, плагина для JetBrains и других редакторов. Поддерживает 70+ языков программирования. Полностью бесплатный.
В настоящее время команда codeium
разрабатывает собственный редактор кода — Windsurf, где разработчик и ИИ занимаются парным программированием (затея сомнительная, но окэй :))
Контекстуальные предложения и дополнения кода
codeium
анализирует кодовую базу целиком, включая стиль, форматирование и используемые подходы. Это обеспечивает согласованность и единообразие во всех компонентах и функцияхПоддержка документации
codeium
помогает как в генерации новой, так и в улучшении существующей документацииМасштабируемость для больших проектов
codeium
поддерживает работу с крупными проектами и эффективно обрабатывает большие кодовые базыПоддержка 70+ языков программирования
codeium
поддерживает все эти "языки" и позволяет гибко переключаться между разными частями проектаИнтеграция с популярными IDE
codeium
удобным инструментом для команд, использующих разные инструменты разработкиcodeium
упрощает повторное использование кода, что критично при создании универсальных компонентовПопросим codeium
написать TS-функцию для извлечения свойств объекта по ключам:
Я бы вместо T extends object
использовал T extends Record<string, unknown>
, в остальном к сгенерированному коду вопросов нет, что просили, то и получили.
Специально для адептов функционального программирования ;):
const extractProps = <
T extends Record<string, unknown>,
K extends keyof T,
>(
props: T,
...keys: K[]
) =>
Object.fromEntries(
keys.filter((key) => key in props).map((key) => [key, props[key]]),
) as Pick<T, K>
Devv_ — это "поисковик следующего поколения для разработчиков". Показывает отличные результаты в генерации кода по запросу, в частности на JavaScript/TypeScript/React. Дает развернутые и понятные комментарии к сгенерированному коду. Полностью бесплатный.
Высокое качество генерируемого кода
devv_
очень эффективен при работе с JavaScript/TypeScript/React, которые являются основой многих веб-приложенийРазвернутые комментарии
devv_
генерирует понятные и развернутые комментарии, что улучшает читаемость кода и упрощает развитие проектаРешение узких задач и разработка небольших компонентов
devv_
позволяет быстро генерировать код по конкретному запросуДоступность
devv_
работает бесплатно в браузере, не требует дополнительных настроек и может быть использован на любом этапе разработкиdevv_
хорошо подходит для быстрого прототипирования компонентов и генерации примеров использованияdevv_
отличным инструментом для небольших и средних команд, которые могут использовать его для проверки идей и разработки MVPПопросим devv_
реализовать алгоритм Кнута-Морриса-Пратта (здесь про алгоритм можно почитать подробнее и найти пример его реализации):
/**
* The Knuth-Morris-Pratt (KMP) algorithm is a string-searching algorithm that searches for occurrences of a "word" W within a main "text string" S.
* It utilizes a pre-computed table (LPS array - Longest Proper Prefix Suffix) to optimize the search process.
*
* @param text The main text string to search within.
* @param pattern The word (pattern) to search for in the text.
* @returns An array of indices representing the starting positions of all occurrences of the pattern within the text.
*/
function knuthMorrisPratt(text: string, pattern: string): number[] {
const textLength = text.length;
const patternLength = pattern.length;
// 1. Build LPS (Longest Proper Prefix Suffix) array
const lps = buildLPS(pattern);
// 2. Search for pattern in text
const occurrences: number[] = [];
let textIndex = 0;
let patternIndex = 0;
while (textIndex < textLength) {
if (text[textIndex] === pattern[patternIndex]) {
textIndex++;
patternIndex++;
if (patternIndex === patternLength) {
// Pattern found!
occurrences.push(textIndex - patternLength);
patternIndex = lps[patternIndex - 1]; // Reset patternIndex using LPS
}
} else {
if (patternIndex !== 0) {
patternIndex = lps[patternIndex - 1]; // Shift pattern based on LPS
} else {
textIndex++; // Move to the next character in the text
}
}
}
return occurrences;
}
/**
* Builds the LPS (Longest Proper Prefix Suffix) array for a given pattern.
*
* @param pattern The pattern string.
* @returns The LPS array.
*/
function buildLPS(pattern: string): number[] {
const patternLength = pattern.length;
const lps: number[] = new Array(patternLength).fill(0);
let length = 0; // Length of the previous longest prefix suffix
lps[0] = 0; // lps[0] is always 0
let i = 1;
while (i < patternLength) {
if (pattern[i] === pattern[length]) {
length++;
lps[i] = length;
i++;
} else {
if (length !== 0) {
length = lps[length - 1]; // Fallback to previous LPS value
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
// Example usage:
const text = "ABC ABCDAB ABCDABCDABDE";
const pattern = "ABCDABD";
const indices = knuthMorrisPratt(text, pattern);
console.log(`Pattern found at indices: ${indices}`); // Output: Pattern found at indices: 15
const text2 = "ABABDABACDABABCABAB";
const pattern2 = "ABABCABAB";
const indices2 = knuthMorrisPratt(text2, pattern2);
console.log(`Pattern found at indices: ${indices2}`); // Output: Pattern found at indices: 10
const text3 = "AAAAABAAABA";
const pattern3 = "AAAA";
const indices3 = knuthMorrisPratt(text3, pattern3);
console.log(`Pattern found at indices: ${indices3}`); // Output: Pattern found at indices: 0,1
const text4 = "THIS IS A TEST TEXT";
const pattern4 = "TEST";
const indices4 = knuthMorrisPratt(text4, pattern4);
console.log(`Pattern found at indices: ${indices4}`); // Output: Pattern found at indices: 10
Сгенерированный код работает примерно так, как и должен.
Попросим devv_
реализовать хук React/TS для извлечения ширины и высоты элемента при изменении его размеров:
import { useState, useRef, useLayoutEffect, useCallback } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
interface Size {
width: number | undefined;
height: number | undefined;
}
type UseResizeObserverOptions = {
box?: 'border-box' | 'content-box' | 'device-pixel-content-box';
};
function useResizeObserver<T extends HTMLElement = HTMLElement>(
options: UseResizeObserverOptions = {}
): [React.RefObject<T>, Size] {
const { box = 'content-box' } = options;
const ref = useRef<T>(null);
const [size, setSize] = useState<Size>({ width: undefined, height: undefined });
const handleResize = useCallback(
(entries: ResizeObserverEntry[]) => {
if (!Array.isArray(entries)) {
return;
}
const entry = entries[0];
const newWidth = entry.contentRect.width;
const newHeight = entry.contentRect.height;
setSize({ width: newWidth, height: newHeight });
},
[]
);
useLayoutEffect(() => {
if (!ref.current) return;
const resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(ref.current, { box });
return () => {
resizeObserver.disconnect();
};
}, [ref.current, box, handleResize]);
return [ref, size];
}
export default useResizeObserver;
import React from 'react';
import useResizeObserver from './useResizeObserver';
const MyComponent: React.FC = () => {
const [ref, size] = useResizeObserver();
return (
<div ref={ref} style={{ width: '50%', height: '200px', border: '1px solid black' }}>
{size.width && size.height && (
<p>Width: {size.width}px, Height: {size.height}px</p>
)}
</div>
);
};
export default MyComponent;
К этому коду есть несколько вопросов, например:
ref.current
в зависимостях хука useLayoutEffect()
не нуженuseLayoutEffect()
я бы использовал useEffect()
и т.д.Но, в целом, код довольно качественный и с ним можно работать.
v0 — AI-помощник, специализирующийся на веб-разработке. Показывает отличные результаты в написании кода на JavaScript/TypeScript по запросу. Генерирует компоненты React с использованием современных подходов и технологий. Дает развернутые и понятные комментарии к написанному коду. Полностью бесплатный.
Фокус на веб-разработке
v0
специально заточен под React и TypeScript, которые сегодня фактически являются стандартом разработки веб-приложенийАвтоматизация создания компонентов
Развернутые комментарии и объяснения
v0
генерирует развернутые пояснения к сгенерированному коду. Это помогает документировать кодовую базу и обеспечивает прозрачность логики компонентовБыстрая интеграция в проекты
Простота использования
v0
работает бесплатно в браузере, не требует установки дополнительных инструментов и предоставляет простой, понятный и удобный интерфейсv0
отлично подходит для генерации и оптимизации компонентов, что ускоряет разработку веб-приложенийПопросим v0
сгенерировать код компонента React "Модальное окно" с помощью элемента HTML dialog
:
// Предполагается использование серверных компонентов Next.js
'use client'
import React, { useRef, useEffect, ReactNode } from 'react'
import { X } from 'lucide-react'
import { Button } from '@/components/ui/button'
interface ModalProps {
isOpen: boolean
onClose: () => void
children: ReactNode
title: string
}
export function Modal({ isOpen, onClose, children, title }: ModalProps) {
const dialogRef = useRef<HTMLDialogElement>(null)
useEffect(() => {
const dialogElement = dialogRef.current
if (!dialogElement) return
if (isOpen) {
dialogElement.showModal()
} else {
dialogElement.close()
}
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose()
}
}
if (isOpen) {
document.addEventListener('keydown', handleKeyDown)
}
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}, [isOpen, onClose])
const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {
if (e.target === dialogRef.current) {
onClose()
}
}
return (
{/* dialog, как мы и просили */}
<dialog
ref={dialogRef}
// Предполагается использование tailwind
className="p-0 bg-background rounded-lg shadow-lg backdrop:bg-black/50"
onClick={handleBackdropClick}
>
<div className="min-w-[300px] max-w-md">
<div className="flex justify-between items-center p-4 border-b">
<h2 className="text-lg font-semibold">{title}</h2>
<Button variant="ghost" size="icon" onClick={onClose} aria-label="Close">
<X className="h-4 w-4" />
</Button>
</div>
<div className="p-4">{children}</div>
</div>
</dialog>
)
}
'use client'
import { useState } from 'react'
import { Modal } from '@/components/Modal'
import { Button } from '@/components/ui/button'
export default function Home() {
const [isModalOpen, setIsModalOpen] = useState(false)
return (
<div className="flex flex-col items-center justify-center min-h-screen p-4">
<h1 className="text-2xl font-bold mb-4">Modal Demo</h1>
<Button onClick={() => setIsModalOpen(true)}>Open Modal</Button>
<Modal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Example Modal"
>
<p>This is the content of the modal. You can put any React components or HTML here.</p>
<div className="mt-4 flex justify-end">
<Button onClick={() => setIsModalOpen(false)}>Close</Button>
</div>
</Modal>
</div>
)
}
I'll create a React modal component using the HTML <dialog>
element. This approach provides a native, accessible modal solution with minimal JavaScript. Let's build this component step by step.
This implementation creates a reusable Modal
component that uses the HTML <dialog>
element. Here's a breakdown of the key features:
Modal
component accepts isOpen
, onClose
, children
, and title
as props.useRef
hook to get a reference to the <dialog>
element.useEffect
hook is used to control the opening and closing of the modal based on the isOpen
prop.Button
component from the UI library for consistency.In the Home
component, we demonstrate how to use the Modal
:
useState
hook to manage the open state of the modal.Modal
component is rendered with the necessary props, including the content as children.This implementation provides a flexible, accessible, and easy-to-use modal component that leverages the native <dialog>
element for better performance and built-in accessibility features.
Не то, чтобы к сгенерированному коду не было вопросов, но в качестве отправной точки вполне подойдет.
ChatGPT — это написанное на Rust десктопное приложение для Mac, Windows и Linux. О ChatGPT написано млн статей, так что, с вашего позволения, я сразу перейду к примеру использования приложения.
Скормим ChatGPT
скриншот кода, сгенерированного codeium
и попросим его улучшить:
Yes, I can improve this code snippet. I'll refine it to make it more concise and efficient while adhering to best practices. Here's the updated code:
Improved Code
function extractPropertiesByKeys<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
return keys.reduce((result, key) => {
if (key in obj) {
result[key] = obj[key];
}
return result;
}, {} as Pick<T, K>);
}
Improvements Made
Using Array.prototype.reduce is more functional and concise for building an object iteratively.
The result is initialized as {} as Pick<T, K> to ensure proper typing without needing assertions later.
The Pick type is sufficient for ensuring that only the specified keys are included in the resulting object. Using Partial was unnecessary and could lead to confusion.
This version is shorter and aligns with modern JavaScript/TypeScript functional programming practices.
Example Usage
interface Person {
name: string;
age: number;
address: string;
}
const person: Person = {
name: "Alice",
age: 30,
address: "123 Main St"
};
const selectedProperties = extractPropertiesByKeys(person, ["name", "age"]);
console.log(selectedProperties); // Output: { name: "Alice", age: 30 }
Let me know if you’d like further refinements or additional explanations!
Попросим ChatGPT
придать коду более функциональный вид:
Где-то такой вариант утилиты мы уже видели :)
Certainly! A functional programming approach emphasizes immutability and the use of higher-order functions. Here's the rewritten code using a functional programming paradigm:
Functional Programming Version
function extractPropertiesByKeys<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
return Object.fromEntries(
keys
.filter((key) => key in obj)
.map((key) => [key, obj[key]])
) as Pick<T, K>;
}
Explanation of Functional Approach
filter
:
Filters the keys array to include only keys that exist in the object.
map
:
Transforms each valid key into a [key, value] pair (tuples) suitable for creating an object.
Object.fromEntries
:
Converts the array of key-value pairs into an object.
Example Usage
interface Person {
name: string;
age: number;
address: string;
}
const person: Person = {
name: "Alice",
age: 30,
address: "123 Main St"
};
const selectedProperties = extractPropertiesByKeys(person, ["name", "age"]);
console.log(selectedProperties); // Output: { name: "Alice", age: 30 }
Why This Is Functional
Let me know if you’d like more refinements or explanations!
В завершение кратко рассмотрим еще одно интересное решение — GPT4All.
GPT4All
— это десктопное приложение для работы с локальными (!) большими языковыми моделями. Показывает хорошие результаты в написании кода по запросу, в частности на JavaScript/TypeScript/React. Дает развернутые и понятные комментарии к сгенерированному коду. Бесплатный, но для работы требуется БЯМ (например, модель Llama 3 весит 4,34 ГБ и требует 8 ГБ оперативной памяти, минимум). Работает как онлайн, так и оффлайн.
Это единственное готовое бесплатное решение, которое я обнаружил, позволяющее использовать всю мощь ИИ без доступа к Интернету, что может быть важно в некоторых ситуациях, например, когда вы опасаетесь, что ваши данные без вашего ведома могут оказаться в распоряжении сервиса, предоставляющего услуги.
В заключение отмечу, что в своей работе я часто использую codeium
и ChatGPT
и иногда прибегаю к помощи v0
.
Делитесь вашими находками в комментариях.
Happy coding!
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩