Explorar el Código

feat(app): feed customization options

Adam hace 1 mes
padre
commit
aaf8317c82

+ 35 - 1
packages/app/src/components/settings-general.tsx

@@ -267,18 +267,50 @@ export const SettingsGeneral: Component = () => {
             )}
             )}
           </Select>
           </Select>
         </SettingsRow>
         </SettingsRow>
+      </div>
+    </div>
+  )
 
 
+  const FeedSection = () => (
+    <div class="flex flex-col gap-1">
+      <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.feed")}</h3>
+
+      <div class="bg-surface-raised-base px-4 rounded-lg">
         <SettingsRow
         <SettingsRow
           title={language.t("settings.general.row.reasoningSummaries.title")}
           title={language.t("settings.general.row.reasoningSummaries.title")}
           description={language.t("settings.general.row.reasoningSummaries.description")}
           description={language.t("settings.general.row.reasoningSummaries.description")}
         >
         >
-          <div data-action="settings-reasoning-summaries">
+          <div data-action="settings-feed-reasoning-summaries">
             <Switch
             <Switch
               checked={settings.general.showReasoningSummaries()}
               checked={settings.general.showReasoningSummaries()}
               onChange={(checked) => settings.general.setShowReasoningSummaries(checked)}
               onChange={(checked) => settings.general.setShowReasoningSummaries(checked)}
             />
             />
           </div>
           </div>
         </SettingsRow>
         </SettingsRow>
+
+        <SettingsRow
+          title={language.t("settings.general.row.shellToolPartsExpanded.title")}
+          description={language.t("settings.general.row.shellToolPartsExpanded.description")}
+        >
+          <div data-action="settings-feed-shell-tool-parts-expanded">
+            <Switch
+              checked={settings.general.shellToolPartsExpanded()}
+              onChange={(checked) => settings.general.setShellToolPartsExpanded(checked)}
+            />
+          </div>
+        </SettingsRow>
+
+        <SettingsRow
+          title={language.t("settings.general.row.editToolPartsExpanded.title")}
+          description={language.t("settings.general.row.editToolPartsExpanded.description")}
+        >
+          <div data-action="settings-feed-edit-tool-parts-expanded">
+            <Switch
+              checked={settings.general.editToolPartsExpanded()}
+              onChange={(checked) => settings.general.setEditToolPartsExpanded(checked)}
+            />
+          </div>
+        </SettingsRow>
       </div>
       </div>
     </div>
     </div>
   )
   )
@@ -435,6 +467,8 @@ export const SettingsGeneral: Component = () => {
       <div class="flex flex-col gap-8 w-full">
       <div class="flex flex-col gap-8 w-full">
         <AppearanceSection />
         <AppearanceSection />
 
 
+        <FeedSection />
+
         <NotificationsSection />
         <NotificationsSection />
 
 
         <SoundsSection />
         <SoundsSection />

+ 18 - 0
packages/app/src/context/settings.tsx

@@ -23,6 +23,8 @@ export interface Settings {
     autoSave: boolean
     autoSave: boolean
     releaseNotes: boolean
     releaseNotes: boolean
     showReasoningSummaries: boolean
     showReasoningSummaries: boolean
+    shellToolPartsExpanded: boolean
+    editToolPartsExpanded: boolean
   }
   }
   updates: {
   updates: {
     startup: boolean
     startup: boolean
@@ -44,6 +46,8 @@ const defaultSettings: Settings = {
     autoSave: true,
     autoSave: true,
     releaseNotes: true,
     releaseNotes: true,
     showReasoningSummaries: false,
     showReasoningSummaries: false,
+    shellToolPartsExpanded: true,
+    editToolPartsExpanded: false,
   },
   },
   updates: {
   updates: {
     startup: true,
     startup: true,
@@ -129,6 +133,20 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
         setShowReasoningSummaries(value: boolean) {
         setShowReasoningSummaries(value: boolean) {
           setStore("general", "showReasoningSummaries", value)
           setStore("general", "showReasoningSummaries", value)
         },
         },
+        shellToolPartsExpanded: withFallback(
+          () => store.general?.shellToolPartsExpanded,
+          defaultSettings.general.shellToolPartsExpanded,
+        ),
+        setShellToolPartsExpanded(value: boolean) {
+          setStore("general", "shellToolPartsExpanded", value)
+        },
+        editToolPartsExpanded: withFallback(
+          () => store.general?.editToolPartsExpanded,
+          defaultSettings.general.editToolPartsExpanded,
+        ),
+        setEditToolPartsExpanded(value: boolean) {
+          setStore("general", "editToolPartsExpanded", value)
+        },
       },
       },
       updates: {
       updates: {
         startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup),
         startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup),

+ 7 - 0
packages/app/src/i18n/ar.ts

@@ -529,6 +529,7 @@ export const dict = {
   "settings.general.section.notifications": "إشعارات النظام",
   "settings.general.section.notifications": "إشعارات النظام",
   "settings.general.section.updates": "التحديثات",
   "settings.general.section.updates": "التحديثات",
   "settings.general.section.sounds": "المؤثرات الصوتية",
   "settings.general.section.sounds": "المؤثرات الصوتية",
+  "settings.general.section.feed": "الخلاصة",
   "settings.general.section.display": "شاشة العرض",
   "settings.general.section.display": "شاشة العرض",
   "settings.general.row.language.title": "اللغة",
   "settings.general.row.language.title": "اللغة",
   "settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode",
   "settings.general.row.language.description": "تغيير لغة العرض لـ OpenCode",
@@ -538,6 +539,12 @@ export const dict = {
   "settings.general.row.theme.description": "تخصيص سمة OpenCode.",
   "settings.general.row.theme.description": "تخصيص سمة OpenCode.",
   "settings.general.row.font.title": "الخط",
   "settings.general.row.font.title": "الخط",
   "settings.general.row.font.description": "تخصيص الخط الأحادي المستخدم في كتل التعليمات البرمجية",
   "settings.general.row.font.description": "تخصيص الخط الأحادي المستخدم في كتل التعليمات البرمجية",
+  "settings.general.row.shellToolPartsExpanded.title": "توسيع أجزاء أداة shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "إظهار أجزاء أداة shell موسعة بشكل افتراضي في الشريط الزمني",
+  "settings.general.row.editToolPartsExpanded.title": "توسيع أجزاء أداة edit",
+  "settings.general.row.editToolPartsExpanded.description":
+    "إظهار أجزاء أدوات edit و write و patch موسعة بشكل افتراضي في الشريط الزمني",
   "settings.general.row.wayland.title": "استخدام Wayland الأصلي",
   "settings.general.row.wayland.title": "استخدام Wayland الأصلي",
   "settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
   "settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/br.ts

@@ -535,6 +535,7 @@ export const dict = {
   "settings.general.section.notifications": "Notificações do sistema",
   "settings.general.section.notifications": "Notificações do sistema",
   "settings.general.section.updates": "Atualizações",
   "settings.general.section.updates": "Atualizações",
   "settings.general.section.sounds": "Efeitos sonoros",
   "settings.general.section.sounds": "Efeitos sonoros",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Tela",
   "settings.general.section.display": "Tela",
   "settings.general.row.language.title": "Idioma",
   "settings.general.row.language.title": "Idioma",
   "settings.general.row.language.description": "Alterar o idioma de exibição do OpenCode",
   "settings.general.row.language.description": "Alterar o idioma de exibição do OpenCode",
@@ -544,6 +545,12 @@ export const dict = {
   "settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.",
   "settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.",
   "settings.general.row.font.title": "Fonte",
   "settings.general.row.font.title": "Fonte",
   "settings.general.row.font.description": "Personalize a fonte monoespaçada usada em blocos de código",
   "settings.general.row.font.description": "Personalize a fonte monoespaçada usada em blocos de código",
+  "settings.general.row.shellToolPartsExpanded.title": "Expandir partes da ferramenta shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Mostrar partes da ferramenta shell expandidas por padrão na linha do tempo",
+  "settings.general.row.editToolPartsExpanded.title": "Expandir partes da ferramenta de edição",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Mostrar partes das ferramentas de edição, escrita e patch expandidas por padrão na linha do tempo",
   "settings.general.row.wayland.title": "Usar Wayland nativo",
   "settings.general.row.wayland.title": "Usar Wayland nativo",
   "settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.",
   "settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/bs.ts

@@ -599,6 +599,7 @@ export const dict = {
   "settings.general.section.notifications": "Sistemske obavijesti",
   "settings.general.section.notifications": "Sistemske obavijesti",
   "settings.general.section.updates": "Ažuriranja",
   "settings.general.section.updates": "Ažuriranja",
   "settings.general.section.sounds": "Zvučni efekti",
   "settings.general.section.sounds": "Zvučni efekti",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Prikaz",
   "settings.general.section.display": "Prikaz",
 
 
   "settings.general.row.language.title": "Jezik",
   "settings.general.row.language.title": "Jezik",
@@ -610,6 +611,12 @@ export const dict = {
   "settings.general.row.font.title": "Font",
   "settings.general.row.font.title": "Font",
   "settings.general.row.font.description": "Prilagodi monospace font koji se koristi u blokovima koda",
   "settings.general.row.font.description": "Prilagodi monospace font koji se koristi u blokovima koda",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "Proširi dijelove shell alata",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Prikaži dijelove shell alata podrazumijevano proširene na vremenskoj traci",
+  "settings.general.row.editToolPartsExpanded.title": "Proširi dijelove alata za uređivanje",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Prikaži dijelove alata za uređivanje, pisanje i patch podrazumijevano proširene na vremenskoj traci",
   "settings.general.row.wayland.title": "Koristi nativni Wayland",
   "settings.general.row.wayland.title": "Koristi nativni Wayland",
   "settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.",
   "settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 6 - 0
packages/app/src/i18n/da.ts

@@ -594,6 +594,7 @@ export const dict = {
   "settings.general.section.notifications": "Systemmeddelelser",
   "settings.general.section.notifications": "Systemmeddelelser",
   "settings.general.section.updates": "Opdateringer",
   "settings.general.section.updates": "Opdateringer",
   "settings.general.section.sounds": "Lydeffekter",
   "settings.general.section.sounds": "Lydeffekter",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Skærm",
   "settings.general.section.display": "Skærm",
 
 
   "settings.general.row.language.title": "Sprog",
   "settings.general.row.language.title": "Sprog",
@@ -605,6 +606,11 @@ export const dict = {
   "settings.general.row.font.title": "Skrifttype",
   "settings.general.row.font.title": "Skrifttype",
   "settings.general.row.font.description": "Tilpas mono-skrifttypen brugt i kodeblokke",
   "settings.general.row.font.description": "Tilpas mono-skrifttypen brugt i kodeblokke",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "Udvid shell-værktøjsdele",
+  "settings.general.row.shellToolPartsExpanded.description": "Vis shell-værktøjsdele udvidet som standard i tidslinjen",
+  "settings.general.row.editToolPartsExpanded.title": "Udvid edit-værktøjsdele",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Vis edit-, write- og patch-værktøjsdele udvidet som standard i tidslinjen",
   "settings.general.row.wayland.title": "Brug native Wayland",
   "settings.general.row.wayland.title": "Brug native Wayland",
   "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.",
   "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/de.ts

@@ -544,6 +544,7 @@ export const dict = {
   "settings.general.section.notifications": "Systembenachrichtigungen",
   "settings.general.section.notifications": "Systembenachrichtigungen",
   "settings.general.section.updates": "Updates",
   "settings.general.section.updates": "Updates",
   "settings.general.section.sounds": "Soundeffekte",
   "settings.general.section.sounds": "Soundeffekte",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Anzeige",
   "settings.general.section.display": "Anzeige",
   "settings.general.row.language.title": "Sprache",
   "settings.general.row.language.title": "Sprache",
   "settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern",
   "settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern",
@@ -553,6 +554,12 @@ export const dict = {
   "settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
   "settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
   "settings.general.row.font.title": "Schriftart",
   "settings.general.row.font.title": "Schriftart",
   "settings.general.row.font.description": "Die in Codeblöcken verwendete Monospace-Schriftart anpassen",
   "settings.general.row.font.description": "Die in Codeblöcken verwendete Monospace-Schriftart anpassen",
+  "settings.general.row.shellToolPartsExpanded.title": "Shell-Tool-Abschnitte ausklappen",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Shell-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen",
+  "settings.general.row.editToolPartsExpanded.title": "Edit-Tool-Abschnitte ausklappen",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Edit-, Write- und Patch-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen",
   "settings.general.row.wayland.title": "Natives Wayland verwenden",
   "settings.general.row.wayland.title": "Natives Wayland verwenden",
   "settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
   "settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/en.ts

@@ -600,6 +600,7 @@ export const dict = {
   "settings.general.section.notifications": "System notifications",
   "settings.general.section.notifications": "System notifications",
   "settings.general.section.updates": "Updates",
   "settings.general.section.updates": "Updates",
   "settings.general.section.sounds": "Sound effects",
   "settings.general.section.sounds": "Sound effects",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Display",
   "settings.general.section.display": "Display",
 
 
   "settings.general.row.language.title": "Language",
   "settings.general.row.language.title": "Language",
@@ -612,6 +613,12 @@ export const dict = {
   "settings.general.row.font.description": "Customise the mono font used in code blocks",
   "settings.general.row.font.description": "Customise the mono font used in code blocks",
   "settings.general.row.reasoningSummaries.title": "Show reasoning summaries",
   "settings.general.row.reasoningSummaries.title": "Show reasoning summaries",
   "settings.general.row.reasoningSummaries.description": "Display model reasoning summaries in the timeline",
   "settings.general.row.reasoningSummaries.description": "Display model reasoning summaries in the timeline",
+  "settings.general.row.shellToolPartsExpanded.title": "Expand shell tool parts",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Show shell tool parts expanded by default in the timeline",
+  "settings.general.row.editToolPartsExpanded.title": "Expand edit tool parts",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Show edit, write, and patch tool parts expanded by default in the timeline",
 
 
   "settings.general.row.wayland.title": "Use native Wayland",
   "settings.general.row.wayland.title": "Use native Wayland",
   "settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
   "settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",

+ 7 - 0
packages/app/src/i18n/es.ts

@@ -602,6 +602,7 @@ export const dict = {
   "settings.general.section.notifications": "Notificaciones del sistema",
   "settings.general.section.notifications": "Notificaciones del sistema",
   "settings.general.section.updates": "Actualizaciones",
   "settings.general.section.updates": "Actualizaciones",
   "settings.general.section.sounds": "Efectos de sonido",
   "settings.general.section.sounds": "Efectos de sonido",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Pantalla",
   "settings.general.section.display": "Pantalla",
 
 
   "settings.general.row.language.title": "Idioma",
   "settings.general.row.language.title": "Idioma",
@@ -613,6 +614,12 @@ export const dict = {
   "settings.general.row.font.title": "Fuente",
   "settings.general.row.font.title": "Fuente",
   "settings.general.row.font.description": "Personaliza la fuente monoespaciada usada en bloques de código",
   "settings.general.row.font.description": "Personaliza la fuente monoespaciada usada en bloques de código",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "Expandir partes de la herramienta shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Mostrar las partes de la herramienta shell expandidas por defecto en la línea de tiempo",
+  "settings.general.row.editToolPartsExpanded.title": "Expandir partes de la herramienta de edición",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Mostrar las partes de las herramientas de edición, escritura y parcheado expandidas por defecto en la línea de tiempo",
   "settings.general.row.wayland.title": "Usar Wayland nativo",
   "settings.general.row.wayland.title": "Usar Wayland nativo",
   "settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.",
   "settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/fr.ts

@@ -543,6 +543,7 @@ export const dict = {
   "settings.general.section.notifications": "Notifications système",
   "settings.general.section.notifications": "Notifications système",
   "settings.general.section.updates": "Mises à jour",
   "settings.general.section.updates": "Mises à jour",
   "settings.general.section.sounds": "Effets sonores",
   "settings.general.section.sounds": "Effets sonores",
+  "settings.general.section.feed": "Flux",
   "settings.general.section.display": "Affichage",
   "settings.general.section.display": "Affichage",
   "settings.general.row.language.title": "Langue",
   "settings.general.row.language.title": "Langue",
   "settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode",
   "settings.general.row.language.description": "Changer la langue d'affichage pour OpenCode",
@@ -552,6 +553,12 @@ export const dict = {
   "settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.",
   "settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.",
   "settings.general.row.font.title": "Police",
   "settings.general.row.font.title": "Police",
   "settings.general.row.font.description": "Personnaliser la police mono utilisée dans les blocs de code",
   "settings.general.row.font.description": "Personnaliser la police mono utilisée dans les blocs de code",
+  "settings.general.row.shellToolPartsExpanded.title": "Développer les parties de l'outil shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Afficher les parties de l'outil shell développées par défaut dans la chronologie",
+  "settings.general.row.editToolPartsExpanded.title": "Développer les parties de l'outil edit",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Afficher les parties des outils edit, write et patch développées par défaut dans la chronologie",
   "settings.general.row.wayland.title": "Utiliser Wayland natif",
   "settings.general.row.wayland.title": "Utiliser Wayland natif",
   "settings.general.row.wayland.description": "Désactiver le repli X11 sur Wayland. Nécessite un redémarrage.",
   "settings.general.row.wayland.description": "Désactiver le repli X11 sur Wayland. Nécessite un redémarrage.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/ja.ts

@@ -533,6 +533,7 @@ export const dict = {
   "settings.general.section.notifications": "システム通知",
   "settings.general.section.notifications": "システム通知",
   "settings.general.section.updates": "アップデート",
   "settings.general.section.updates": "アップデート",
   "settings.general.section.sounds": "効果音",
   "settings.general.section.sounds": "効果音",
+  "settings.general.section.feed": "フィード",
   "settings.general.section.display": "ディスプレイ",
   "settings.general.section.display": "ディスプレイ",
   "settings.general.row.language.title": "言語",
   "settings.general.row.language.title": "言語",
   "settings.general.row.language.description": "OpenCodeの表示言語を変更します",
   "settings.general.row.language.description": "OpenCodeの表示言語を変更します",
@@ -542,6 +543,12 @@ export const dict = {
   "settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。",
   "settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。",
   "settings.general.row.font.title": "フォント",
   "settings.general.row.font.title": "フォント",
   "settings.general.row.font.description": "コードブロックで使用する等幅フォントをカスタマイズします",
   "settings.general.row.font.description": "コードブロックで使用する等幅フォントをカスタマイズします",
+  "settings.general.row.shellToolPartsExpanded.title": "shell ツールパーツを展開",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "タイムラインで shell ツールパーツをデフォルトで展開して表示します",
+  "settings.general.row.editToolPartsExpanded.title": "edit ツールパーツを展開",
+  "settings.general.row.editToolPartsExpanded.description":
+    "タイムラインで edit、write、patch ツールパーツをデフォルトで展開して表示します",
   "settings.general.row.wayland.title": "ネイティブWaylandを使用",
   "settings.general.row.wayland.title": "ネイティブWaylandを使用",
   "settings.general.row.wayland.description": "WaylandでのX11フォールバックを無効にします。再起動が必要です。",
   "settings.general.row.wayland.description": "WaylandでのX11フォールバックを無効にします。再起動が必要です。",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/ko.ts

@@ -534,6 +534,7 @@ export const dict = {
   "settings.general.section.notifications": "시스템 알림",
   "settings.general.section.notifications": "시스템 알림",
   "settings.general.section.updates": "업데이트",
   "settings.general.section.updates": "업데이트",
   "settings.general.section.sounds": "효과음",
   "settings.general.section.sounds": "효과음",
+  "settings.general.section.feed": "피드",
   "settings.general.section.display": "디스플레이",
   "settings.general.section.display": "디스플레이",
   "settings.general.row.language.title": "언어",
   "settings.general.row.language.title": "언어",
   "settings.general.row.language.description": "OpenCode 표시 언어 변경",
   "settings.general.row.language.description": "OpenCode 표시 언어 변경",
@@ -543,6 +544,12 @@ export const dict = {
   "settings.general.row.theme.description": "OpenCode 테마 사용자 지정",
   "settings.general.row.theme.description": "OpenCode 테마 사용자 지정",
   "settings.general.row.font.title": "글꼴",
   "settings.general.row.font.title": "글꼴",
   "settings.general.row.font.description": "코드 블록에 사용되는 고정폭 글꼴 사용자 지정",
   "settings.general.row.font.description": "코드 블록에 사용되는 고정폭 글꼴 사용자 지정",
+  "settings.general.row.shellToolPartsExpanded.title": "shell 도구 파트 펼치기",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "타임라인에서 기본적으로 shell 도구 파트를 펼친 상태로 표시합니다",
+  "settings.general.row.editToolPartsExpanded.title": "edit 도구 파트 펼치기",
+  "settings.general.row.editToolPartsExpanded.description":
+    "타임라인에서 기본적으로 edit, write, patch 도구 파트를 펼친 상태로 표시합니다",
   "settings.general.row.wayland.title": "네이티브 Wayland 사용",
   "settings.general.row.wayland.title": "네이티브 Wayland 사용",
   "settings.general.row.wayland.description": "Wayland에서 X11 폴백을 비활성화합니다. 다시 시작해야 합니다.",
   "settings.general.row.wayland.description": "Wayland에서 X11 폴백을 비활성화합니다. 다시 시작해야 합니다.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 6 - 0
packages/app/src/i18n/no.ts

@@ -602,6 +602,7 @@ export const dict = {
   "settings.general.section.notifications": "Systemvarsler",
   "settings.general.section.notifications": "Systemvarsler",
   "settings.general.section.updates": "Oppdateringer",
   "settings.general.section.updates": "Oppdateringer",
   "settings.general.section.sounds": "Lydeffekter",
   "settings.general.section.sounds": "Lydeffekter",
+  "settings.general.section.feed": "Feed",
   "settings.general.section.display": "Skjerm",
   "settings.general.section.display": "Skjerm",
 
 
   "settings.general.row.language.title": "Språk",
   "settings.general.row.language.title": "Språk",
@@ -613,6 +614,11 @@ export const dict = {
   "settings.general.row.font.title": "Skrift",
   "settings.general.row.font.title": "Skrift",
   "settings.general.row.font.description": "Tilpass mono-skriften som brukes i kodeblokker",
   "settings.general.row.font.description": "Tilpass mono-skriften som brukes i kodeblokker",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "Utvid shell-verktøydeler",
+  "settings.general.row.shellToolPartsExpanded.description": "Vis shell-verktøydeler utvidet som standard i tidslinjen",
+  "settings.general.row.editToolPartsExpanded.title": "Utvid edit-verktøydeler",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Vis edit-, write- og patch-verktøydeler utvidet som standard i tidslinjen",
   "settings.general.row.wayland.title": "Bruk innebygd Wayland",
   "settings.general.row.wayland.title": "Bruk innebygd Wayland",
   "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Krever omstart.",
   "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Krever omstart.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/pl.ts

@@ -534,6 +534,7 @@ export const dict = {
   "settings.general.section.notifications": "Powiadomienia systemowe",
   "settings.general.section.notifications": "Powiadomienia systemowe",
   "settings.general.section.updates": "Aktualizacje",
   "settings.general.section.updates": "Aktualizacje",
   "settings.general.section.sounds": "Efekty dźwiękowe",
   "settings.general.section.sounds": "Efekty dźwiękowe",
+  "settings.general.section.feed": "Kanał",
   "settings.general.section.display": "Ekran",
   "settings.general.section.display": "Ekran",
   "settings.general.row.language.title": "Język",
   "settings.general.row.language.title": "Język",
   "settings.general.row.language.description": "Zmień język wyświetlania dla OpenCode",
   "settings.general.row.language.description": "Zmień język wyświetlania dla OpenCode",
@@ -543,6 +544,12 @@ export const dict = {
   "settings.general.row.theme.description": "Dostosuj motyw OpenCode.",
   "settings.general.row.theme.description": "Dostosuj motyw OpenCode.",
   "settings.general.row.font.title": "Czcionka",
   "settings.general.row.font.title": "Czcionka",
   "settings.general.row.font.description": "Dostosuj czcionkę mono używaną w blokach kodu",
   "settings.general.row.font.description": "Dostosuj czcionkę mono używaną w blokach kodu",
+  "settings.general.row.shellToolPartsExpanded.title": "Rozwijaj elementy narzędzia shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Domyślnie pokazuj rozwinięte elementy narzędzia shell na osi czasu",
+  "settings.general.row.editToolPartsExpanded.title": "Rozwijaj elementy narzędzia edit",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Domyślnie pokazuj rozwinięte elementy narzędzi edit, write i patch na osi czasu",
   "settings.general.row.wayland.title": "Użyj natywnego Wayland",
   "settings.general.row.wayland.title": "Użyj natywnego Wayland",
   "settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
   "settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 7 - 0
packages/app/src/i18n/ru.ts

@@ -600,6 +600,7 @@ export const dict = {
   "settings.general.section.notifications": "Системные уведомления",
   "settings.general.section.notifications": "Системные уведомления",
   "settings.general.section.updates": "Обновления",
   "settings.general.section.updates": "Обновления",
   "settings.general.section.sounds": "Звуковые эффекты",
   "settings.general.section.sounds": "Звуковые эффекты",
+  "settings.general.section.feed": "Лента",
   "settings.general.section.display": "Дисплей",
   "settings.general.section.display": "Дисплей",
 
 
   "settings.general.row.language.title": "Язык",
   "settings.general.row.language.title": "Язык",
@@ -611,6 +612,12 @@ export const dict = {
   "settings.general.row.font.title": "Шрифт",
   "settings.general.row.font.title": "Шрифт",
   "settings.general.row.font.description": "Настройте моноширинный шрифт для блоков кода",
   "settings.general.row.font.description": "Настройте моноширинный шрифт для блоков кода",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "Разворачивать элементы инструмента shell",
+  "settings.general.row.shellToolPartsExpanded.description":
+    "Показывать элементы инструмента shell в ленте развернутыми по умолчанию",
+  "settings.general.row.editToolPartsExpanded.title": "Разворачивать элементы инструмента edit",
+  "settings.general.row.editToolPartsExpanded.description":
+    "Показывать элементы инструментов edit, write и patch в ленте развернутыми по умолчанию",
   "settings.general.row.wayland.title": "Использовать нативный Wayland",
   "settings.general.row.wayland.title": "Использовать нативный Wayland",
   "settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.",
   "settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.",
   "settings.general.row.wayland.tooltip":
   "settings.general.row.wayland.tooltip":

+ 6 - 0
packages/app/src/i18n/th.ts

@@ -594,6 +594,7 @@ export const dict = {
   "settings.general.section.notifications": "การแจ้งเตือนระบบ",
   "settings.general.section.notifications": "การแจ้งเตือนระบบ",
   "settings.general.section.updates": "การอัปเดต",
   "settings.general.section.updates": "การอัปเดต",
   "settings.general.section.sounds": "เสียงเอฟเฟกต์",
   "settings.general.section.sounds": "เสียงเอฟเฟกต์",
+  "settings.general.section.feed": "ฟีด",
   "settings.general.section.display": "การแสดงผล",
   "settings.general.section.display": "การแสดงผล",
 
 
   "settings.general.row.language.title": "ภาษา",
   "settings.general.row.language.title": "ภาษา",
@@ -605,6 +606,11 @@ export const dict = {
   "settings.general.row.font.title": "ฟอนต์",
   "settings.general.row.font.title": "ฟอนต์",
   "settings.general.row.font.description": "ปรับแต่งฟอนต์โมโนที่ใช้ในบล็อกโค้ด",
   "settings.general.row.font.description": "ปรับแต่งฟอนต์โมโนที่ใช้ในบล็อกโค้ด",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "ขยายส่วนเครื่องมือ shell",
+  "settings.general.row.shellToolPartsExpanded.description": "แสดงส่วนเครื่องมือ shell แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
+  "settings.general.row.editToolPartsExpanded.title": "ขยายส่วนเครื่องมือ edit",
+  "settings.general.row.editToolPartsExpanded.description":
+    "แสดงส่วนเครื่องมือ edit, write และ patch แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
   "settings.general.row.wayland.title": "ใช้ Wayland แบบเนทีฟ",
   "settings.general.row.wayland.title": "ใช้ Wayland แบบเนทีฟ",
   "settings.general.row.wayland.description": "ปิดใช้งาน X11 fallback บน Wayland ต้องรีสตาร์ท",
   "settings.general.row.wayland.description": "ปิดใช้งาน X11 fallback บน Wayland ต้องรีสตาร์ท",
   "settings.general.row.wayland.tooltip": "บน Linux ที่มีจอภาพรีเฟรชเรตแบบผสม Wayland แบบเนทีฟอาจเสถียรกว่า",
   "settings.general.row.wayland.tooltip": "บน Linux ที่มีจอภาพรีเฟรชเรตแบบผสม Wayland แบบเนทีฟอาจเสถียรกว่า",

+ 5 - 0
packages/app/src/i18n/zh.ts

@@ -595,6 +595,7 @@ export const dict = {
   "settings.general.section.notifications": "系统通知",
   "settings.general.section.notifications": "系统通知",
   "settings.general.section.updates": "更新",
   "settings.general.section.updates": "更新",
   "settings.general.section.sounds": "音效",
   "settings.general.section.sounds": "音效",
+  "settings.general.section.feed": "动态",
   "settings.general.section.display": "显示",
   "settings.general.section.display": "显示",
   "settings.general.row.language.title": "语言",
   "settings.general.row.language.title": "语言",
   "settings.general.row.language.description": "更改 OpenCode 的显示语言",
   "settings.general.row.language.description": "更改 OpenCode 的显示语言",
@@ -604,6 +605,10 @@ export const dict = {
   "settings.general.row.theme.description": "自定义 OpenCode 的主题。",
   "settings.general.row.theme.description": "自定义 OpenCode 的主题。",
   "settings.general.row.font.title": "字体",
   "settings.general.row.font.title": "字体",
   "settings.general.row.font.description": "自定义代码块使用的等宽字体",
   "settings.general.row.font.description": "自定义代码块使用的等宽字体",
+  "settings.general.row.shellToolPartsExpanded.title": "展开 shell 工具部分",
+  "settings.general.row.shellToolPartsExpanded.description": "默认在时间线中展开 shell 工具部分",
+  "settings.general.row.editToolPartsExpanded.title": "展开编辑工具部分",
+  "settings.general.row.editToolPartsExpanded.description": "默认在时间线中展开 edit、write 和 patch 工具部分",
   "settings.general.row.wayland.title": "使用原生 Wayland",
   "settings.general.row.wayland.title": "使用原生 Wayland",
   "settings.general.row.wayland.description": "在 Wayland 上禁用 X11 回退。需要重启。",
   "settings.general.row.wayland.description": "在 Wayland 上禁用 X11 回退。需要重启。",
   "settings.general.row.wayland.tooltip": "在混合刷新率显示器的 Linux 系统上,原生 Wayland 可能更稳定。",
   "settings.general.row.wayland.tooltip": "在混合刷新率显示器的 Linux 系统上,原生 Wayland 可能更稳定。",

+ 5 - 0
packages/app/src/i18n/zht.ts

@@ -589,6 +589,7 @@ export const dict = {
   "settings.general.section.notifications": "系統通知",
   "settings.general.section.notifications": "系統通知",
   "settings.general.section.updates": "更新",
   "settings.general.section.updates": "更新",
   "settings.general.section.sounds": "音效",
   "settings.general.section.sounds": "音效",
+  "settings.general.section.feed": "資訊流",
   "settings.general.section.display": "顯示",
   "settings.general.section.display": "顯示",
 
 
   "settings.general.row.language.title": "語言",
   "settings.general.row.language.title": "語言",
@@ -600,6 +601,10 @@ export const dict = {
   "settings.general.row.font.title": "字型",
   "settings.general.row.font.title": "字型",
   "settings.general.row.font.description": "自訂程式碼區塊使用的等寬字型",
   "settings.general.row.font.description": "自訂程式碼區塊使用的等寬字型",
 
 
+  "settings.general.row.shellToolPartsExpanded.title": "展開 shell 工具區塊",
+  "settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊",
+  "settings.general.row.editToolPartsExpanded.title": "展開 edit 工具區塊",
+  "settings.general.row.editToolPartsExpanded.description": "在時間軸中預設展開 edit、write 和 patch 工具區塊",
   "settings.general.row.wayland.title": "使用原生 Wayland",
   "settings.general.row.wayland.title": "使用原生 Wayland",
   "settings.general.row.wayland.description": "在 Wayland 上停用 X11 後備模式。需要重新啟動。",
   "settings.general.row.wayland.description": "在 Wayland 上停用 X11 後備模式。需要重新啟動。",
   "settings.general.row.wayland.tooltip": "在混合更新率螢幕的 Linux 系統上,原生 Wayland 可能更穩定。",
   "settings.general.row.wayland.tooltip": "在混合更新率螢幕的 Linux 系統上,原生 Wayland 可能更穩定。",

+ 2 - 0
packages/app/src/pages/session/message-timeline.tsx

@@ -539,6 +539,8 @@ export function MessageTimeline(props: {
                     messageID={message.id}
                     messageID={message.id}
                     lastUserMessageID={props.lastUserMessageID}
                     lastUserMessageID={props.lastUserMessageID}
                     showReasoningSummaries={settings.general.showReasoningSummaries()}
                     showReasoningSummaries={settings.general.showReasoningSummaries()}
+                    shellToolDefaultOpen={settings.general.shellToolPartsExpanded()}
+                    editToolDefaultOpen={settings.general.editToolPartsExpanded()}
                     classes={{
                     classes={{
                       root: "min-w-0 w-full relative",
                       root: "min-w-0 w-full relative",
                       content: "flex flex-col justify-between !overflow-visible",
                       content: "flex flex-col justify-between !overflow-visible",

+ 10 - 18
packages/ui/src/components/message-part.css

@@ -332,14 +332,6 @@
   }
   }
 }
 }
 
 
-[data-slot="collapsible-content"]:has([data-component="edit-content"]),
-[data-slot="collapsible-content"]:has([data-component="write-content"]) {
-  border: 1px solid var(--border-weak-base);
-  border-radius: 6px;
-  background: transparent;
-  overflow: hidden;
-}
-
 [data-component="bash-output"] {
 [data-component="bash-output"] {
   width: 100%;
   width: 100%;
   border: 1px solid var(--border-weak-base);
   border: 1px solid var(--border-weak-base);
@@ -399,11 +391,6 @@
   }
   }
 }
 }
 
 
-[data-slot="collapsible-content"]:has([data-component="edit-content"]) [data-component="edit-content"],
-[data-slot="collapsible-content"]:has([data-component="write-content"]) [data-component="write-content"] {
-  border-top: none;
-}
-
 [data-component="edit-trigger"],
 [data-component="edit-trigger"],
 [data-component="write-trigger"] {
 [data-component="write-trigger"] {
   display: flex;
   display: flex;
@@ -492,9 +479,8 @@
 [data-component="edit-content"] {
 [data-component="edit-content"] {
   border-radius: inherit;
   border-radius: inherit;
   border-top: 1px solid var(--border-weaker-base);
   border-top: 1px solid var(--border-weaker-base);
-  max-height: 420px;
   overflow-x: hidden;
   overflow-x: hidden;
-  overflow-y: auto;
+  overflow-y: visible;
 
 
   scrollbar-width: none;
   scrollbar-width: none;
   -ms-overflow-style: none;
   -ms-overflow-style: none;
@@ -512,9 +498,8 @@
 [data-component="write-content"] {
 [data-component="write-content"] {
   border-radius: inherit;
   border-radius: inherit;
   border-top: 1px solid var(--border-weaker-base);
   border-top: 1px solid var(--border-weaker-base);
-  max-height: 240px;
   overflow-x: hidden;
   overflow-x: hidden;
-  overflow-y: auto;
+  overflow-y: visible;
 
 
   [data-component="code"] {
   [data-component="code"] {
     padding-bottom: 0 !important;
     padding-bottom: 0 !important;
@@ -1212,11 +1197,18 @@
   }
   }
 }
 }
 
 
+[data-component="edit-tool"],
+[data-component="write-tool"],
 [data-component="apply-patch-tool"] {
 [data-component="apply-patch-tool"] {
   > [data-component="collapsible"].tool-collapsible {
   > [data-component="collapsible"].tool-collapsible {
     gap: 0px;
     gap: 0px;
   }
   }
 
 
+  > [data-component="collapsible"] > [data-slot="collapsible-content"] {
+    border: none;
+    background: transparent;
+  }
+
   > [data-component="collapsible"] > [data-slot="collapsible-trigger"][aria-expanded="true"] {
   > [data-component="collapsible"] > [data-slot="collapsible-trigger"][aria-expanded="true"] {
     position: sticky;
     position: sticky;
     top: var(--sticky-accordion-top, 0px);
     top: var(--sticky-accordion-top, 0px);
@@ -1298,7 +1290,7 @@
 [data-component="apply-patch-file-diff"] {
 [data-component="apply-patch-file-diff"] {
   border-radius: inherit;
   border-radius: inherit;
   overflow-x: hidden;
   overflow-x: hidden;
-  overflow-y: auto;
+  overflow-y: visible;
   scrollbar-width: none;
   scrollbar-width: none;
   -ms-overflow-style: none;
   -ms-overflow-style: none;
 
 

+ 207 - 115
packages/ui/src/components/message-part.tsx

@@ -276,12 +276,24 @@ function renderable(part: PartType, showReasoningSummaries = true) {
   return !!PART_MAPPING[part.type]
   return !!PART_MAPPING[part.type]
 }
 }
 
 
+function toolDefaultOpen(tool: string, shell = false, edit = false) {
+  if (tool === "bash") return shell
+  if (tool === "edit" || tool === "write" || tool === "apply_patch") return edit
+}
+
+function partDefaultOpen(part: PartType, shell = false, edit = false) {
+  if (part.type !== "tool") return
+  return toolDefaultOpen(part.tool, shell, edit)
+}
+
 export function AssistantParts(props: {
 export function AssistantParts(props: {
   messages: AssistantMessage[]
   messages: AssistantMessage[]
   showAssistantCopyPartID?: string | null
   showAssistantCopyPartID?: string | null
   turnDurationMs?: number
   turnDurationMs?: number
   working?: boolean
   working?: boolean
   showReasoningSummaries?: boolean
   showReasoningSummaries?: boolean
+  shellToolDefaultOpen?: boolean
+  editToolDefaultOpen?: boolean
 }) {
 }) {
   const data = useData()
   const data = useData()
   const emptyParts: PartType[] = []
   const emptyParts: PartType[] = []
@@ -372,6 +384,7 @@ export function AssistantParts(props: {
                   message={entry().message}
                   message={entry().message}
                   showAssistantCopyPartID={props.showAssistantCopyPartID}
                   showAssistantCopyPartID={props.showAssistantCopyPartID}
                   turnDurationMs={props.turnDurationMs}
                   turnDurationMs={props.turnDurationMs}
+                  defaultOpen={partDefaultOpen(entry().part, props.shellToolDefaultOpen, props.editToolDefaultOpen)}
                 />
                 />
               )}
               )}
             </Show>
             </Show>
@@ -900,6 +913,42 @@ export const ToolRegistry = {
   render: getTool,
   render: getTool,
 }
 }
 
 
+function ToolFileAccordion(props: { path: string; actions?: JSX.Element; children: JSX.Element }) {
+  const value = createMemo(() => props.path || "tool-file")
+
+  return (
+    <Accordion
+      multiple
+      data-scope="apply-patch"
+      style={{ "--sticky-accordion-offset": "40px" }}
+      defaultValue={[value()]}
+    >
+      <Accordion.Item value={value()}>
+        <StickyAccordionHeader>
+          <Accordion.Trigger>
+            <div data-slot="apply-patch-trigger-content">
+              <div data-slot="apply-patch-file-info">
+                <FileIcon node={{ path: props.path, type: "file" }} />
+                <div data-slot="apply-patch-file-name-container">
+                  <Show when={props.path.includes("/")}>
+                    <span data-slot="apply-patch-directory">{`\u202A${getDirectory(props.path)}\u202C`}</span>
+                  </Show>
+                  <span data-slot="apply-patch-filename">{getFilename(props.path)}</span>
+                </div>
+              </div>
+              <div data-slot="apply-patch-trigger-actions">
+                {props.actions}
+                <Icon name="chevron-grabber-vertical" size="small" />
+              </div>
+            </div>
+          </Accordion.Trigger>
+        </StickyAccordionHeader>
+        <Accordion.Content>{props.children}</Accordion.Content>
+      </Accordion.Item>
+    </Accordion>
+  )
+}
+
 PART_MAPPING["tool"] = function ToolPartDisplay(props) {
 PART_MAPPING["tool"] = function ToolPartDisplay(props) {
   const data = useData()
   const data = useData()
   const i18n = useI18n()
   const i18n = useI18n()
@@ -1479,57 +1528,67 @@ ToolRegistry.register({
     const i18n = useI18n()
     const i18n = useI18n()
     const diffComponent = useDiffComponent()
     const diffComponent = useDiffComponent()
     const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
     const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
+    const path = createMemo(() => props.metadata?.filediff?.file || props.input.filePath || "")
     const filename = () => getFilename(props.input.filePath ?? "")
     const filename = () => getFilename(props.input.filePath ?? "")
     const pending = () => props.status === "pending" || props.status === "running"
     const pending = () => props.status === "pending" || props.status === "running"
     return (
     return (
-      <BasicTool
-        {...props}
-        icon="code-lines"
-        defer
-        trigger={
-          <div data-component="edit-trigger">
-            <div data-slot="message-part-title-area">
-              <div data-slot="message-part-title">
-                <span data-slot="message-part-title-text">
-                  <Show when={pending()} fallback={i18n.t("ui.messagePart.title.edit")}>
-                    <TextShimmer text={i18n.t("ui.messagePart.title.edit")} />
+      <div data-component="edit-tool">
+        <BasicTool
+          {...props}
+          icon="code-lines"
+          defer
+          trigger={
+            <div data-component="edit-trigger">
+              <div data-slot="message-part-title-area">
+                <div data-slot="message-part-title">
+                  <span data-slot="message-part-title-text">
+                    <Show when={pending()} fallback={i18n.t("ui.messagePart.title.edit")}>
+                      <TextShimmer text={i18n.t("ui.messagePart.title.edit")} />
+                    </Show>
+                  </span>
+                  <Show when={!pending()}>
+                    <span data-slot="message-part-title-filename">{filename()}</span>
                   </Show>
                   </Show>
-                </span>
-                <Show when={!pending()}>
-                  <span data-slot="message-part-title-filename">{filename()}</span>
+                </div>
+                <Show when={!pending() && props.input.filePath?.includes("/")}>
+                  <div data-slot="message-part-path">
+                    <span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
+                  </div>
+                </Show>
+              </div>
+              <div data-slot="message-part-actions">
+                <Show when={!pending() && props.metadata.filediff}>
+                  <DiffChanges changes={props.metadata.filediff} />
                 </Show>
                 </Show>
               </div>
               </div>
-              <Show when={!pending() && props.input.filePath?.includes("/")}>
-                <div data-slot="message-part-path">
-                  <span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
-                </div>
-              </Show>
-            </div>
-            <div data-slot="message-part-actions">
-              <Show when={!pending() && props.metadata.filediff}>
-                <DiffChanges changes={props.metadata.filediff} />
-              </Show>
             </div>
             </div>
-          </div>
-        }
-      >
-        <Show when={props.metadata.filediff?.path || props.input.filePath}>
-          <div data-component="edit-content">
-            <Dynamic
-              component={diffComponent}
-              before={{
-                name: props.metadata?.filediff?.file || props.input.filePath,
-                contents: props.metadata?.filediff?.before || props.input.oldString,
-              }}
-              after={{
-                name: props.metadata?.filediff?.file || props.input.filePath,
-                contents: props.metadata?.filediff?.after || props.input.newString,
-              }}
-            />
-          </div>
-        </Show>
-        <DiagnosticsDisplay diagnostics={diagnostics()} />
-      </BasicTool>
+          }
+        >
+          <Show when={path()}>
+            <ToolFileAccordion
+              path={path()}
+              actions={
+                <Show when={!pending() && props.metadata.filediff}>{(diff) => <DiffChanges changes={diff()} />}</Show>
+              }
+            >
+              <div data-component="edit-content">
+                <Dynamic
+                  component={diffComponent}
+                  before={{
+                    name: props.metadata?.filediff?.file || props.input.filePath,
+                    contents: props.metadata?.filediff?.before || props.input.oldString,
+                  }}
+                  after={{
+                    name: props.metadata?.filediff?.file || props.input.filePath,
+                    contents: props.metadata?.filediff?.after || props.input.newString,
+                  }}
+                />
+              </div>
+            </ToolFileAccordion>
+          </Show>
+          <DiagnosticsDisplay diagnostics={diagnostics()} />
+        </BasicTool>
+      </div>
     )
     )
   },
   },
 })
 })
@@ -1540,51 +1599,56 @@ ToolRegistry.register({
     const i18n = useI18n()
     const i18n = useI18n()
     const codeComponent = useCodeComponent()
     const codeComponent = useCodeComponent()
     const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
     const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
+    const path = createMemo(() => props.input.filePath || "")
     const filename = () => getFilename(props.input.filePath ?? "")
     const filename = () => getFilename(props.input.filePath ?? "")
     const pending = () => props.status === "pending" || props.status === "running"
     const pending = () => props.status === "pending" || props.status === "running"
     return (
     return (
-      <BasicTool
-        {...props}
-        icon="code-lines"
-        defer
-        trigger={
-          <div data-component="write-trigger">
-            <div data-slot="message-part-title-area">
-              <div data-slot="message-part-title">
-                <span data-slot="message-part-title-text">
-                  <Show when={pending()} fallback={i18n.t("ui.messagePart.title.write")}>
-                    <TextShimmer text={i18n.t("ui.messagePart.title.write")} />
+      <div data-component="write-tool">
+        <BasicTool
+          {...props}
+          icon="code-lines"
+          defer
+          trigger={
+            <div data-component="write-trigger">
+              <div data-slot="message-part-title-area">
+                <div data-slot="message-part-title">
+                  <span data-slot="message-part-title-text">
+                    <Show when={pending()} fallback={i18n.t("ui.messagePart.title.write")}>
+                      <TextShimmer text={i18n.t("ui.messagePart.title.write")} />
+                    </Show>
+                  </span>
+                  <Show when={!pending()}>
+                    <span data-slot="message-part-title-filename">{filename()}</span>
                   </Show>
                   </Show>
-                </span>
-                <Show when={!pending()}>
-                  <span data-slot="message-part-title-filename">{filename()}</span>
+                </div>
+                <Show when={!pending() && props.input.filePath?.includes("/")}>
+                  <div data-slot="message-part-path">
+                    <span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
+                  </div>
                 </Show>
                 </Show>
               </div>
               </div>
-              <Show when={!pending() && props.input.filePath?.includes("/")}>
-                <div data-slot="message-part-path">
-                  <span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
-                </div>
-              </Show>
+              <div data-slot="message-part-actions">{/* <DiffChanges diff={diff} /> */}</div>
             </div>
             </div>
-            <div data-slot="message-part-actions">{/* <DiffChanges diff={diff} /> */}</div>
-          </div>
-        }
-      >
-        <Show when={props.input.content}>
-          <div data-component="write-content">
-            <Dynamic
-              component={codeComponent}
-              file={{
-                name: props.input.filePath,
-                contents: props.input.content,
-                cacheKey: checksum(props.input.content),
-              }}
-              overflow="scroll"
-            />
-          </div>
-        </Show>
-        <DiagnosticsDisplay diagnostics={diagnostics()} />
-      </BasicTool>
+          }
+        >
+          <Show when={props.input.content && path()}>
+            <ToolFileAccordion path={path()}>
+              <div data-component="write-content">
+                <Dynamic
+                  component={codeComponent}
+                  file={{
+                    name: props.input.filePath,
+                    contents: props.input.content,
+                    cacheKey: checksum(props.input.content),
+                  }}
+                  overflow="scroll"
+                />
+              </div>
+            </ToolFileAccordion>
+          </Show>
+          <DiagnosticsDisplay diagnostics={diagnostics()} />
+        </BasicTool>
+      </div>
     )
     )
   },
   },
 })
 })
@@ -1731,45 +1795,73 @@ ToolRegistry.register({
         }
         }
       >
       >
         {(file) => (
         {(file) => (
-          <BasicTool
-            {...props}
-            icon="code-lines"
-            defer
-            trigger={
-              <div data-component="edit-trigger">
-                <div data-slot="message-part-title-area">
-                  <div data-slot="message-part-title">
-                    <span data-slot="message-part-title-text">
-                      <Show when={pending()} fallback={i18n.t("ui.tool.patch")}>
-                        <TextShimmer text={i18n.t("ui.tool.patch")} />
+          <div data-component="apply-patch-tool">
+            <BasicTool
+              {...props}
+              icon="code-lines"
+              defer
+              trigger={
+                <div data-component="edit-trigger">
+                  <div data-slot="message-part-title-area">
+                    <div data-slot="message-part-title">
+                      <span data-slot="message-part-title-text">
+                        <Show when={pending()} fallback={i18n.t("ui.tool.patch")}>
+                          <TextShimmer text={i18n.t("ui.tool.patch")} />
+                        </Show>
+                      </span>
+                      <Show when={!pending()}>
+                        <span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span>
                       </Show>
                       </Show>
-                    </span>
+                    </div>
+                    <Show when={!pending() && file().relativePath.includes("/")}>
+                      <div data-slot="message-part-path">
+                        <span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span>
+                      </div>
+                    </Show>
+                  </div>
+                  <div data-slot="message-part-actions">
                     <Show when={!pending()}>
                     <Show when={!pending()}>
-                      <span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span>
+                      <DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
                     </Show>
                     </Show>
                   </div>
                   </div>
-                  <Show when={!pending() && file().relativePath.includes("/")}>
-                    <div data-slot="message-part-path">
-                      <span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span>
-                    </div>
-                  </Show>
                 </div>
                 </div>
-                <div data-slot="message-part-actions">
-                  <Show when={!pending()}>
-                    <DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
-                  </Show>
+              }
+            >
+              <ToolFileAccordion
+                path={file().relativePath}
+                actions={
+                  <Switch>
+                    <Match when={file().type === "add"}>
+                      <span data-slot="apply-patch-change" data-type="added">
+                        {i18n.t("ui.patch.action.created")}
+                      </span>
+                    </Match>
+                    <Match when={file().type === "delete"}>
+                      <span data-slot="apply-patch-change" data-type="removed">
+                        {i18n.t("ui.patch.action.deleted")}
+                      </span>
+                    </Match>
+                    <Match when={file().type === "move"}>
+                      <span data-slot="apply-patch-change" data-type="modified">
+                        {i18n.t("ui.patch.action.moved")}
+                      </span>
+                    </Match>
+                    <Match when={true}>
+                      <DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
+                    </Match>
+                  </Switch>
+                }
+              >
+                <div data-component="apply-patch-file-diff">
+                  <Dynamic
+                    component={diffComponent}
+                    before={{ name: file().filePath, contents: file().before }}
+                    after={{ name: file().movePath ?? file().filePath, contents: file().after }}
+                  />
                 </div>
                 </div>
-              </div>
-            }
-          >
-            <div data-component="edit-content">
-              <Dynamic
-                component={diffComponent}
-                before={{ name: file().filePath, contents: file().before }}
-                after={{ name: file().movePath ?? file().filePath, contents: file().after }}
-              />
-            </div>
-          </BasicTool>
+              </ToolFileAccordion>
+            </BasicTool>
+          </div>
         )}
         )}
       </Show>
       </Show>
     )
     )

+ 4 - 0
packages/ui/src/components/session-turn.tsx

@@ -140,6 +140,8 @@ export function SessionTurn(
     messageID: string
     messageID: string
     lastUserMessageID?: string
     lastUserMessageID?: string
     showReasoningSummaries?: boolean
     showReasoningSummaries?: boolean
+    shellToolDefaultOpen?: boolean
+    editToolDefaultOpen?: boolean
     onUserInteracted?: () => void
     onUserInteracted?: () => void
     classes?: {
     classes?: {
       root?: string
       root?: string
@@ -369,6 +371,8 @@ export function SessionTurn(
                       turnDurationMs={turnDurationMs()}
                       turnDurationMs={turnDurationMs()}
                       working={working()}
                       working={working()}
                       showReasoningSummaries={showReasoningSummaries()}
                       showReasoningSummaries={showReasoningSummaries()}
+                      shellToolDefaultOpen={props.shellToolDefaultOpen}
+                      editToolDefaultOpen={props.editToolDefaultOpen}
                     />
                     />
                   </div>
                   </div>
                 </Show>
                 </Show>