ByteDance製エディタ「Trae」を試してみた

# 筆者について

```
name → Koki Teramoto
age → 18
language → ja_JP, en_US
```

# 本編

本日ブラウンジングしていますと下のような記事を見つけました。

https://www.yahoo.com/tech/bytedance-launches-ai-code-editor-093000329.html

投稿自体は１月２１日にされているものなのでちょっと出遅れた感はありますが、なんか今日めっちゃThreadsにこのエディターを使っている人がいましたので、少し怖いところではありますが使ってみようというところです。

## 早速インストールしていく

公式サイトは以下のリンクです。

https://www.trae.ai/

ということでがっつりドメインにもあるようにAIを使っていくことが前提のエディターって感じでした。Windows版は２月に登場ということで現在はMacの方だけが試せます。ちなみに、調べたところユニバーサルビルドじゃなくてM1専用でしたのでIntelモデルを使っている方はおそらく起動できないんじゃないかなと思います。ということで、インストールして適当に作ったNextのフォルダを開いてるスクショをはっつけます。

![Screenshot 2025-01-26 at 19.45.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/d690caad-4e7a-1bf8-388f-4d1e8e83446a.png)

右側がAIアージェントと喋るためのスペースです。さて、とりあえずHelloとかは最初書いてあったのですがそんなのはどうでもいいですね。早速アプリを作らせていきましょう。

## ToDoアプリを作らせてみた

ということで早速ToDoアプリをつくらせてみました。指示は以下の通りです。翻訳はGoogle翻訳で英語から中国語（簡体字）にしています。

> 使用整个项目通过 Next.js 创建 ToDo 应用程序。
> （原文）Create ToDo app with Next.js using entire project.

回答は以下の通りでした。

> 我看到您已经创建了一个很好的 ToDo 应用程序的基础版本。这是一个使用 Next.js 和 TypeScript 构建的现代化待办事项应用，已经包含了基本的 CRUD 功能和优雅的 UI 设计。让我们来完善一下这个应用，添加一些额外的功能：
>
> 1. 持久化存储（使用 localStorage）
> 2. 添加编辑功能
> 3. 添加待办事项分类功能
> 4. 添加完成状态筛选
> 以下是更新建议：

要するに、保存機能、追加編集機能、ラベル管理機能、完了状況管理機能を持っているToDoアプリを作ってくれたそうです。以下にコードを載せておきます。

```typescript:page.tsx
'use client'

import { useState, useEffect } from "react";

export default function Home() {
  const [todos, setTodos] = useState<Array<{ id: number; text: string; completed: boolean; category: string }>>([]);
  const [input, setInput] = useState('');
  const [category, setCategory] = useState('个人');
  const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
  const [editingId, setEditingId] = useState<number | null>(null);
  const [editText, setEditText] = useState('');

  // 从 localStorage 加载数据
  useEffect(() => {
    const savedTodos = localStorage.getItem('todos');
    if (savedTodos) {
      setTodos(JSON.parse(savedTodos));
    }
  }, []);

  // 保存数据到 localStorage
  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]);

  const addTodo = () => {
    if (input.trim()) {
      setTodos([...todos, {
        id: Date.now(),
        text: input.trim(),
        completed: false,
        category
      }]);
      setInput('');
    }
  };

  // ... 保持其他现有函数 ...

  const startEditing = (id: number, text: string) => {
    setEditingId(id);
    setEditText(text);
  };

  const saveEdit = (id: number) => {
    setTodos(todos.map(todo =>
      todo.id === id ? { ...todo, text: editText } : todo
    ));
    setEditingId(null);
  };

  const filteredTodos = todos.filter(todo => {
    if (filter === 'active') return !todo.completed;
    if (filter === 'completed') return todo.completed;
    return true;
  });

  return (
    <div className="min-h-screen p-8 pb-20 flex flex-col items-center bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-gray-900 dark:to-gray-800">
      <main className="w-full max-w-2xl mx-auto">
        {/* ... 保持标题不变 ... */}

        <div className="flex gap-3 mb-4">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            onKeyPress={(e) => e.key === 'Enter' && addTodo()}
            className="flex-1 px-4 py-3 border border-gray-200 dark:border-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-400"
            placeholder="添加新的待办事项..."
          />
          <select
            value={category}
            onChange={(e) => setCategory(e.target.value)}
            className="px-4 py-3 border border-gray-200 dark:border-gray-700 rounded-xl bg-white dark:bg-gray-800"
          >
            <option>个人</option>
            <option>工作</option>
            <option>学习</option>
            <option>其他</option>
          </select>
          <button onClick={addTodo} className="px-6 py-3 bg-blue-500 text-white rounded-xl hover:bg-blue-600">
            添加
          </button>
        </div>

        <div className="flex gap-2 mb-8">
          <button
            onClick={() => setFilter('all')}
            className={`px-4 py-2 rounded-lg ${filter === 'all' ? 'bg-blue-500 text-white' : 'bg-gray-100 dark:bg-gray-700'}`}
          >
            全部
          </button>
          <button
            onClick={() => setFilter('active')}
            className={`px-4 py-2 rounded-lg ${filter === 'active' ? 'bg-blue-500 text-white' : 'bg-gray-100 dark:bg-gray-700'}`}
          >
            进行中
          </button>
          <button
            onClick={() => setFilter('completed')}
            className={`px-4 py-2 rounded-lg ${filter === 'completed' ? 'bg-blue-500 text-white' : 'bg-gray-100 dark:bg-gray-700'}`}
          >
            已完成
          </button>
        </div>

        <ul className="space-y-4">
          {filteredTodos.map(todo => (
            <li key={todo.id} className="group flex items-center gap-4 p-4 bg-white dark:bg-gray-800 rounded-xl shadow-sm">
              <input
                type="checkbox"
                checked={todo.completed}
                onChange={() => toggleTodo(todo.id)}
                className="w-5 h-5 rounded-md text-blue-500"
              />
              {editingId === todo.id ? (
                <input
                  type="text"
                  value={editText}
                  onChange={(e) => setEditText(e.target.value)}
                  onBlur={() => saveEdit(todo.id)}
                  onKeyPress={(e) => e.key === 'Enter' && saveEdit(todo.id)}
                  className="flex-1 px-2 py-1 border rounded"
                  autoFocus
                />
              ) : (
                <>
                  <span className={`flex-1 ${todo.completed ? 'line-through text-gray-400' : ''}`}>
                    {todo.text}
                    <span className="ml-2 text-sm text-gray-500">({todo.category})</span>
                  </span>
                  <button
                    onClick={() => startEditing(todo.id, todo.text)}
                    className="opacity-0 group-hover:opacity-100 px-3 py-1 text-sm text-blue-500"
                  >
                    编辑
                  </button>
                </>
              )}
              <button
                onClick={() => deleteTodo(todo.id)}
                className="opacity-0 group-hover:opacity-100 px-3 py-1 text-sm text-red-500"
              >
                删除
              </button>
            </li>
          ))}
        </ul>

        {/* ... 保持空状态提示不变 ... */}
      </main>
    </div>
  );
}
```

ちなみに、まだ一応回答には続きがあります。

> 主要更新包括：
>
> 1. 添加了 localStorage 支持，确保刷新页面后数据不会丢失
> 2. 增加了待办事项分类功能（个人、工作、学习、其他）
> 3. 添加了编辑功能，可以双击待办事项进行编辑
> 4. 增加了过滤功能，可以查看全部、进行中或已完成的待办事项
> 5. 优化了 UI/UX，包括悬停效果和状态提示
> 要运行这个应用，您需要确保已经安装了所需的依赖：
> `npm install`
> 然后运行开发服务器：
> `npm run dev`

めちゃめちゃ丁寧に回答してくれました。そうして完成したアプリは以下のようになります。ちなみに、実行した時にしれっとサーバーをアタッチして表示してくれるのは優しい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/8de1234f-d6a1-0225-4d80-49987459c279.png)

次に、見た目をもうちょっと変えてみましょう。

> 使用 Tailwind，添加暗模式颜色配置文件并将主主题颜色更改为红色。
> （原文）Using Tailwind, add the dark mode color profile and change the main theme colour to red.

さて、どのように変えていってくれるのでしょうか。

> 我将帮您修改配置以支持暗模式并将主题色更改为红色系。首先，让我们创建/修改 Tailwind 配置文件：

```javascript:tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  darkMode: 'class', // 启用 class 策略的暗模式
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#fef2f2',
          100: '#fee2e2',
          200: '#fecaca',
          300: '#fca5a5',
          400: '#f87171',
          500: '#ef4444',
          600: '#dc2626',
          700: '#b91c1c',
          800: '#991b1b',
          900: '#7f1d1d',
          950: '#450a0a',
        },
      },
    },
  },
  plugins: [],
}
```

なるほど、まずはTailwindをごちゃごちゃしたのですね。またいけますね。

> 然后，让我们修改主页面中的颜色配置：

```typescript:ThemeToggle.tsx
// ... existing code ...

return (
  <div className="min-h-screen p-8 pb-20 flex flex-col items-center bg-gradient-to-br from-red-50 to-rose-50 dark:from-gray-900 dark:to-gray-800">
    <main className="w-full max-w-2xl mx-auto">
      <h1 className="text-4xl font-bold text-center mb-8 text-gray-800 dark:text-gray-100 tracking-tight">
        待办事项列表
      </h1>

      <div className="flex gap-3 mb-8">
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          className="flex-1 px-4 py-3 border border-gray-200 dark:border-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent shadow-sm transition-all duration-200 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
          placeholder="添加新的待办事项..."
        />
        <button
          onClick={addTodo}
          className="px-6 py-3 bg-red-500 text-white rounded-xl hover:bg-red-600 dark:bg-red-600 dark:hover:bg-red-700 transition-all duration-200 shadow-sm hover:shadow-md font-medium"
        >
          添加
        </button>
      </div>

      <ul className="space-y-4">
        {todos.map(todo => (
          <li
            key={todo.id}
            className="group flex items-center gap-4 p-4 bg-white dark:bg-gray-800 rounded-xl shadow-sm hover:shadow-md transition-all duration-200 border border-gray-100 dark:border-gray-700"
          >
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
              className="w-5 h-5 rounded-md text-red-500 dark:text-red-400 border-gray-300 dark:border-gray-600 focus:ring-red-400 dark:focus:ring-red-500 transition-all duration-200 cursor-pointer"
            />
            // ... rest of the code ...
          </li>
        ))}
      </ul>
      // ... rest of the code ...
    </main>
  </div>
);

// ... rest of the code ...
```

トグルを追加する。その発想には至らなかった、まあまあやりおるぞこのAI。

> 最后，在主页面中添加主题切换按钮：

```typescript:page.tsx
import ThemeToggle from '../components/ThemeToggle'

export default function Home() {
  // ... existing code ...

  return (
    <div className="min-h-screen p-8 pb-20 flex flex-col items-center bg-gradient-to-br from-red-50 to-rose-50 dark:from-gray-900 dark:to-gray-800">
      <ThemeToggle />
      // ... rest of the code ...
    </div>
  )
}
```

ということでトグルを追加して最後に終わり！ということですね、いや〜難しい。さて、これで完成したアプリは如何に？ということで、こちらはGitHubにもあげておきますのでみなさんぜひAIと遊んでみてください。

# 最後に

最後を濁す形になったのですが、これはちょっと理由があって、同じ命令を別に人がしたときにどれだけクオリティーの異なるアプリができるの？って思っているからなんです。なので、どなたかぜひ試したものをこの記事のコメントに残してくださいね。

https://github.com/0-terarin-0/prv_pg_nextjstodo_trae.git

個人的にはUIが好きだったりするのですがまあなんか対応が適当なところもあったり、個人的にはやっぱりVSCodeかZedってとこですかね。またこれからも動向をチェックしていきたいと思います。

追伸　この記事がおそらく一番最初にQiitaでTraeを取り上げた記事かなと思います。