@zerwiz/ymir 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/apps/README.md +4 -0
- package/apps/hlidskjalf/README.md +68 -0
- package/apps/hlidskjalf/android/app/build.gradle +73 -0
- package/apps/hlidskjalf/android/app/capacitor.build.gradle +19 -0
- package/apps/hlidskjalf/android/app/proguard-rules.pro +21 -0
- package/apps/hlidskjalf/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
- package/apps/hlidskjalf/android/app/src/main/AndroidManifest.xml +41 -0
- package/apps/hlidskjalf/android/app/src/main/java/org/zerwiz/ymir/MainActivity.java +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
- package/apps/hlidskjalf/android/app/src/main/res/layout/activity_main.xml +12 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/strings.xml +7 -0
- package/apps/hlidskjalf/android/app/src/main/res/values/styles.xml +22 -0
- package/apps/hlidskjalf/android/app/src/main/res/xml/file_paths.xml +5 -0
- package/apps/hlidskjalf/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
- package/apps/hlidskjalf/android/build.gradle +29 -0
- package/apps/hlidskjalf/android/capacitor.settings.gradle +3 -0
- package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/apps/hlidskjalf/android/gradle.properties +22 -0
- package/apps/hlidskjalf/android/gradlew +248 -0
- package/apps/hlidskjalf/android/gradlew.bat +92 -0
- package/apps/hlidskjalf/android/settings.gradle +5 -0
- package/apps/hlidskjalf/android/variables.gradle +16 -0
- package/apps/hlidskjalf/capacitor.config.ts +26 -0
- package/apps/hlidskjalf/dist/apple-touch-icon.png +0 -0
- package/apps/hlidskjalf/dist/assets/index-BWmNIyaG.css +1 -0
- package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js +88 -0
- package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js.map +1 -0
- package/apps/hlidskjalf/dist/icon.svg +7 -0
- package/apps/hlidskjalf/dist/index.html +64 -0
- package/apps/hlidskjalf/dist/manifest.webmanifest +31 -0
- package/apps/hlidskjalf/dist/og.jpg +0 -0
- package/apps/hlidskjalf/dist/og.png +0 -0
- package/apps/hlidskjalf/dist/og.svg +64 -0
- package/apps/hlidskjalf/dist/sw.js +39 -0
- package/apps/hlidskjalf/dist/ymir-icon.png +0 -0
- package/apps/hlidskjalf/dist/ymir-mark.svg +62 -0
- package/apps/hlidskjalf/electron/icon.png +0 -0
- package/apps/hlidskjalf/electron/main.cjs +236 -0
- package/apps/hlidskjalf/electron/preload.cjs +8 -0
- package/apps/hlidskjalf/electron/smidja-icon.png +0 -0
- package/apps/hlidskjalf/electron/ymir-hlidskjalf.desktop.in +12 -0
- package/apps/hlidskjalf/electron/ymir-smidja.desktop.in +12 -0
- package/apps/hlidskjalf/index.html +63 -0
- package/apps/hlidskjalf/package-lock.json +5398 -0
- package/apps/hlidskjalf/package.json +55 -0
- package/apps/hlidskjalf/public/apple-touch-icon.png +0 -0
- package/apps/hlidskjalf/public/icon.svg +7 -0
- package/apps/hlidskjalf/public/manifest.webmanifest +31 -0
- package/apps/hlidskjalf/public/og.jpg +0 -0
- package/apps/hlidskjalf/public/og.png +0 -0
- package/apps/hlidskjalf/public/og.svg +64 -0
- package/apps/hlidskjalf/public/sw.js +39 -0
- package/apps/hlidskjalf/public/ymir-icon.png +0 -0
- package/apps/hlidskjalf/public/ymir-mark.svg +62 -0
- package/apps/hlidskjalf/server/accounts.ts +159 -0
- package/apps/hlidskjalf/server/index.ts +2045 -0
- package/apps/hlidskjalf/src/app/App.tsx +116 -0
- package/apps/hlidskjalf/src/app/BottomStream.tsx +128 -0
- package/apps/hlidskjalf/src/app/MobileNav.tsx +87 -0
- package/apps/hlidskjalf/src/app/Rail.tsx +106 -0
- package/apps/hlidskjalf/src/app/Shell.tsx +135 -0
- package/apps/hlidskjalf/src/app/Topbar.tsx +130 -0
- package/apps/hlidskjalf/src/components/AccentPicker.tsx +207 -0
- package/apps/hlidskjalf/src/components/AccountMenu.tsx +115 -0
- package/apps/hlidskjalf/src/components/AgentCard.tsx +75 -0
- package/apps/hlidskjalf/src/components/EmberBackground.tsx +36 -0
- package/apps/hlidskjalf/src/components/Emblem.tsx +48 -0
- package/apps/hlidskjalf/src/components/Halls.tsx +155 -0
- package/apps/hlidskjalf/src/components/LoginModal.tsx +329 -0
- package/apps/hlidskjalf/src/components/Markdown.tsx +10 -0
- package/apps/hlidskjalf/src/components/MetricTile.tsx +40 -0
- package/apps/hlidskjalf/src/components/ModelPicker.tsx +100 -0
- package/apps/hlidskjalf/src/components/Overlay.tsx +134 -0
- package/apps/hlidskjalf/src/components/PRCard.tsx +154 -0
- package/apps/hlidskjalf/src/components/RecallPanel.tsx +116 -0
- package/apps/hlidskjalf/src/components/RuneTag.tsx +23 -0
- package/apps/hlidskjalf/src/components/Status.tsx +35 -0
- package/apps/hlidskjalf/src/components/TaskChip.tsx +23 -0
- package/apps/hlidskjalf/src/components/TraceRow.tsx +28 -0
- package/apps/hlidskjalf/src/data/lore.ts +127 -0
- package/apps/hlidskjalf/src/data/metadata.ts +147 -0
- package/apps/hlidskjalf/src/data/mythology.ts +95 -0
- package/apps/hlidskjalf/src/data/realms.ts +167 -0
- package/apps/hlidskjalf/src/gates/Cron.tsx +77 -0
- package/apps/hlidskjalf/src/gates/Decisions.tsx +67 -0
- package/apps/hlidskjalf/src/gates/Files.tsx +138 -0
- package/apps/hlidskjalf/src/gates/Fleet.tsx +184 -0
- package/apps/hlidskjalf/src/gates/Forge.tsx +486 -0
- package/apps/hlidskjalf/src/gates/OmniChat.tsx +217 -0
- package/apps/hlidskjalf/src/gates/Processes.tsx +162 -0
- package/apps/hlidskjalf/src/gates/Profile.tsx +225 -0
- package/apps/hlidskjalf/src/gates/Reviews.tsx +33 -0
- package/apps/hlidskjalf/src/gates/Runes.tsx +116 -0
- package/apps/hlidskjalf/src/gates/Runtime.tsx +106 -0
- package/apps/hlidskjalf/src/gates/Sessions.tsx +109 -0
- package/apps/hlidskjalf/src/gates/Stats.tsx +268 -0
- package/apps/hlidskjalf/src/gates/Tasks.tsx +173 -0
- package/apps/hlidskjalf/src/gates/Trace.tsx +155 -0
- package/apps/hlidskjalf/src/gates/Well.tsx +154 -0
- package/apps/hlidskjalf/src/gates/Worktrees.tsx +48 -0
- package/apps/hlidskjalf/src/hooks/useTenantDef.ts +13 -0
- package/apps/hlidskjalf/src/main.tsx +31 -0
- package/apps/hlidskjalf/src/services/api.ts +404 -0
- package/apps/hlidskjalf/src/services/auth.ts +107 -0
- package/apps/hlidskjalf/src/services/stream.ts +35 -0
- package/apps/hlidskjalf/src/state/store.ts +648 -0
- package/apps/hlidskjalf/src/state/ui.ts +62 -0
- package/apps/hlidskjalf/src/styles/auth.css +485 -0
- package/apps/hlidskjalf/src/styles/components.css +1259 -0
- package/apps/hlidskjalf/src/styles/forge.css +115 -0
- package/apps/hlidskjalf/src/styles/global.css +167 -0
- package/apps/hlidskjalf/src/styles/menus.css +175 -0
- package/apps/hlidskjalf/src/styles/mobile.css +298 -0
- package/apps/hlidskjalf/src/styles/overlays.css +205 -0
- package/apps/hlidskjalf/src/styles/shell.css +641 -0
- package/apps/hlidskjalf/src/types.ts +251 -0
- package/apps/hlidskjalf/src/vite-env.d.ts +9 -0
- package/apps/hlidskjalf/static/landing.md +6 -0
- package/apps/hlidskjalf/tsconfig.json +25 -0
- package/apps/hlidskjalf/vite.config.ts +49 -0
- package/apps/hlidskjalf-mobile/App.tsx +93 -0
- package/apps/hlidskjalf-mobile/app.json +13 -0
- package/apps/hlidskjalf-mobile/index.ts +4 -0
- package/apps/hlidskjalf-mobile/package.json +35 -0
- package/apps/hlidskjalf-mobile/public/icon.svg +7 -0
- package/apps/hlidskjalf-mobile/tsconfig.json +4 -0
- package/apps/odrerir/.astro/content.d.ts +159 -0
- package/apps/odrerir/.astro/dev.json +24 -0
- package/apps/odrerir/.astro/settings.json +5 -0
- package/apps/odrerir/.astro/types.d.ts +2 -0
- package/apps/odrerir/astro.config.mjs +79 -0
- package/apps/odrerir/dist/apple-touch-icon.png +0 -0
- package/apps/odrerir/dist/favicon-192.png +0 -0
- package/apps/odrerir/dist/favicon-32.png +0 -0
- package/apps/odrerir/dist/favicon-512.png +0 -0
- package/apps/odrerir/dist/favicon.svg +53 -0
- package/apps/odrerir/dist/icon.svg +7 -0
- package/apps/odrerir/dist/index.html +837 -0
- package/apps/odrerir/dist/og-square.png +0 -0
- package/apps/odrerir/dist/og-story.png +0 -0
- package/apps/odrerir/dist/og.png +0 -0
- package/apps/odrerir/dist/safari-pinned-tab.svg +7 -0
- package/apps/odrerir/dist/site.webmanifest +16 -0
- package/apps/odrerir/dist/styles/cloth.css +112 -0
- package/apps/odrerir/dist/styles/livehall.css +278 -0
- package/apps/odrerir/electron/icon.png +0 -0
- package/apps/odrerir/electron/main.cjs +183 -0
- package/apps/odrerir/electron/ymir-odrerir.desktop.in +12 -0
- package/apps/odrerir/package-lock.json +4195 -0
- package/apps/odrerir/package.json +30 -0
- package/apps/odrerir/public/apple-touch-icon.png +0 -0
- package/apps/odrerir/public/favicon-192.png +0 -0
- package/apps/odrerir/public/favicon-32.png +0 -0
- package/apps/odrerir/public/favicon-512.png +0 -0
- package/apps/odrerir/public/favicon.svg +53 -0
- package/apps/odrerir/public/icon.svg +7 -0
- package/apps/odrerir/public/og-square.png +0 -0
- package/apps/odrerir/public/og-story.png +0 -0
- package/apps/odrerir/public/og.png +0 -0
- package/apps/odrerir/public/safari-pinned-tab.svg +7 -0
- package/apps/odrerir/public/site.webmanifest +16 -0
- package/apps/odrerir/scripts/extract-cloth.mjs +101 -0
- package/apps/odrerir/src/index.html +837 -0
- package/apps/odrerir/src/styles/cloth.css +112 -0
- package/apps/odrerir/src/styles/livehall.css +278 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/bug_report.yml +99 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/apps/sessrumnir/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
- package/apps/sessrumnir/.github/PULL_REQUEST_TEMPLATE.md +56 -0
- package/apps/sessrumnir/.github/SUPPORT.md +18 -0
- package/apps/sessrumnir/.github/workflows/build.yml +133 -0
- package/apps/sessrumnir/AGENTS.md +540 -0
- package/apps/sessrumnir/CLA.md +63 -0
- package/apps/sessrumnir/CONTRIBUTING.md +233 -0
- package/apps/sessrumnir/LICENSE +200 -0
- package/apps/sessrumnir/NOTICE +33 -0
- package/apps/sessrumnir/README.md +329 -0
- package/apps/sessrumnir/bin/sessrumnir-web.sh +117 -0
- package/apps/sessrumnir/bin/sessrumnir.js +169 -0
- package/apps/sessrumnir/docs/CNAME +1 -0
- package/apps/sessrumnir/docs/favicon.ico +0 -0
- package/apps/sessrumnir/docs/icon-512.png +0 -0
- package/apps/sessrumnir/docs/index.html +441 -0
- package/apps/sessrumnir/docs/robots.txt +4 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_181929.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182005.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182020.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182039.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182117.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182225.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182248.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182322.png +0 -0
- package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182340.png +0 -0
- package/apps/sessrumnir/docs/sitemap.xml +9 -0
- package/apps/sessrumnir/electron.vite.config.ts +49 -0
- package/apps/sessrumnir/eslint.config.mjs +75 -0
- package/apps/sessrumnir/i18next.config.ts +33 -0
- package/apps/sessrumnir/install.sh +144 -0
- package/apps/sessrumnir/out/main/index.js +10881 -0
- package/apps/sessrumnir/out/preload/index.js +722 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-BcIsDXzC.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-CBymlcNF.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-Dr-wLyOm.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-dLR2-a_v.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-B254Xisw.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-DUW-TI76.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-CD-dlDpD.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-D3T6BBXG.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-Dar_cCT6.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-GIs8CF2x.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-Ba-HN6uq.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-ByB2hQUF.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-B8O3uHbr.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-BbtBtHoB.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BPU54Tw3.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BRS-C4KA.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-BEtQ2wm2.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-CQbCaAQ4.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-B6fSpYYV.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-CpdbWO_2.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/index-BxqsRq2x.css +4858 -0
- package/apps/sessrumnir/out/renderer/assets/index-TaD69rFk.js +99435 -0
- package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-italic-DFS986qL.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-italic-Bxi8ein9.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-italic-xAqemagA.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/openmoji-color-9qo_2DLG.woff2 +0 -0
- package/apps/sessrumnir/out/renderer/assets/pi-logo-B6SVnhuS.svg +7 -0
- package/apps/sessrumnir/out/renderer/index.html +19 -0
- package/apps/sessrumnir/out/renderer/theme-boot.js +30 -0
- package/apps/sessrumnir/package-lock.json +10636 -0
- package/apps/sessrumnir/package.json +183 -0
- package/apps/sessrumnir/public/apple-touch-icon.png +0 -0
- package/apps/sessrumnir/public/icon.svg +7 -0
- package/apps/sessrumnir/resources/icons/generate_icons.py +38 -0
- package/apps/sessrumnir/resources/icons/icon-128.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-16.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-256.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-32.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-48.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-512.png +0 -0
- package/apps/sessrumnir/resources/icons/icon-64.png +0 -0
- package/apps/sessrumnir/resources/icons/icon.icns +0 -0
- package/apps/sessrumnir/resources/icons/icon.ico +0 -0
- package/apps/sessrumnir/resources/icons/icon.png +0 -0
- package/apps/sessrumnir/resources/icons/icon.svg +7 -0
- package/apps/sessrumnir/resources/linux/after-install.sh +23 -0
- package/apps/sessrumnir/resources/linux/after-remove.sh +18 -0
- package/apps/sessrumnir/resources/locales/en/translation.json +1735 -0
- package/apps/sessrumnir/resources/permission-prompt-text.test.ts +111 -0
- package/apps/sessrumnir/resources/permission-prompt-text.ts +84 -0
- package/apps/sessrumnir/resources/permission-rules.test.ts +351 -0
- package/apps/sessrumnir/resources/permission-rules.ts +293 -0
- package/apps/sessrumnir/resources/pi-desktop-permissions.ts +62 -0
- package/apps/sessrumnir/resources/ymir-sessrumnir.desktop.in +12 -0
- package/apps/sessrumnir/scripts/check-semantic-colors.mjs +57 -0
- package/apps/sessrumnir/src/main/activity-stats.test.ts +240 -0
- package/apps/sessrumnir/src/main/activity-stats.ts +596 -0
- package/apps/sessrumnir/src/main/agent-detection.test.ts +25 -0
- package/apps/sessrumnir/src/main/agent-detection.ts +114 -0
- package/apps/sessrumnir/src/main/app-data-paths.test.ts +90 -0
- package/apps/sessrumnir/src/main/app-data-paths.ts +95 -0
- package/apps/sessrumnir/src/main/app-log.test.ts +114 -0
- package/apps/sessrumnir/src/main/app-log.ts +210 -0
- package/apps/sessrumnir/src/main/archived-sessions.test.ts +50 -0
- package/apps/sessrumnir/src/main/archived-sessions.ts +95 -0
- package/apps/sessrumnir/src/main/attachment-reader.test.ts +56 -0
- package/apps/sessrumnir/src/main/attachment-reader.ts +52 -0
- package/apps/sessrumnir/src/main/auto-tag.test.ts +38 -0
- package/apps/sessrumnir/src/main/auto-tag.ts +75 -0
- package/apps/sessrumnir/src/main/autostart-linux.test.ts +56 -0
- package/apps/sessrumnir/src/main/autostart-linux.ts +58 -0
- package/apps/sessrumnir/src/main/cmd-escape.test.ts +424 -0
- package/apps/sessrumnir/src/main/cmd-escape.ts +186 -0
- package/apps/sessrumnir/src/main/council-manager.test.ts +167 -0
- package/apps/sessrumnir/src/main/council-manager.ts +265 -0
- package/apps/sessrumnir/src/main/diagnostics-report.test.ts +140 -0
- package/apps/sessrumnir/src/main/diagnostics-report.ts +91 -0
- package/apps/sessrumnir/src/main/diagnostics.ts +111 -0
- package/apps/sessrumnir/src/main/editor-guard.test.ts +59 -0
- package/apps/sessrumnir/src/main/editor-guard.ts +50 -0
- package/apps/sessrumnir/src/main/extension-ui-ipc.test.ts +299 -0
- package/apps/sessrumnir/src/main/extension-ui-ipc.ts +117 -0
- package/apps/sessrumnir/src/main/file-service.test.ts +279 -0
- package/apps/sessrumnir/src/main/file-service.ts +652 -0
- package/apps/sessrumnir/src/main/fs-errors.ts +42 -0
- package/apps/sessrumnir/src/main/get-messages-trim.test.ts +77 -0
- package/apps/sessrumnir/src/main/get-messages-trim.ts +97 -0
- package/apps/sessrumnir/src/main/git-conveyor.test.ts +305 -0
- package/apps/sessrumnir/src/main/git-conveyor.ts +338 -0
- package/apps/sessrumnir/src/main/git-worktree.test.ts +101 -0
- package/apps/sessrumnir/src/main/git-worktree.ts +201 -0
- package/apps/sessrumnir/src/main/i18n.ts +31 -0
- package/apps/sessrumnir/src/main/index.ts +619 -0
- package/apps/sessrumnir/src/main/ipc/active-engine.ts +15 -0
- package/apps/sessrumnir/src/main/ipc/context.ts +58 -0
- package/apps/sessrumnir/src/main/ipc/council-handlers.ts +126 -0
- package/apps/sessrumnir/src/main/ipc/diagnostics-handlers.ts +8 -0
- package/apps/sessrumnir/src/main/ipc/file-handlers.ts +96 -0
- package/apps/sessrumnir/src/main/ipc/git-conveyor-handlers.ts +55 -0
- package/apps/sessrumnir/src/main/ipc/model-handlers.ts +33 -0
- package/apps/sessrumnir/src/main/ipc/models-config-handlers.ts +80 -0
- package/apps/sessrumnir/src/main/ipc/notes-handlers.ts +65 -0
- package/apps/sessrumnir/src/main/ipc/package-handlers.ts +330 -0
- package/apps/sessrumnir/src/main/ipc/permission-rules-handlers.ts +223 -0
- package/apps/sessrumnir/src/main/ipc/pi-handlers.ts +136 -0
- package/apps/sessrumnir/src/main/ipc/pi-start-options.ts +150 -0
- package/apps/sessrumnir/src/main/ipc/run-pi-cli.ts +53 -0
- package/apps/sessrumnir/src/main/ipc/session-handlers.ts +491 -0
- package/apps/sessrumnir/src/main/ipc/settings.ts +120 -0
- package/apps/sessrumnir/src/main/ipc/skills-mcp-handlers.ts +192 -0
- package/apps/sessrumnir/src/main/ipc/system-handlers.ts +131 -0
- package/apps/sessrumnir/src/main/ipc/tag-handlers.ts +69 -0
- package/apps/sessrumnir/src/main/ipc/terminal-handlers.ts +41 -0
- package/apps/sessrumnir/src/main/ipc/theme-handlers.ts +131 -0
- package/apps/sessrumnir/src/main/ipc/update-handlers.ts +61 -0
- package/apps/sessrumnir/src/main/ipc/validation.ts +45 -0
- package/apps/sessrumnir/src/main/ipc/workflow-handlers.ts +108 -0
- package/apps/sessrumnir/src/main/ipc/workspace-activity-wiring.ts +179 -0
- package/apps/sessrumnir/src/main/ipc/workspace-handlers.ts +133 -0
- package/apps/sessrumnir/src/main/ipc-handlers.ts +98 -0
- package/apps/sessrumnir/src/main/map-concurrent.test.ts +80 -0
- package/apps/sessrumnir/src/main/map-concurrent.ts +30 -0
- package/apps/sessrumnir/src/main/models-file.test.ts +114 -0
- package/apps/sessrumnir/src/main/models-file.ts +89 -0
- package/apps/sessrumnir/src/main/notes-manager.test.ts +64 -0
- package/apps/sessrumnir/src/main/notes-manager.ts +183 -0
- package/apps/sessrumnir/src/main/notify-decision.test.ts +39 -0
- package/apps/sessrumnir/src/main/notify-decision.ts +25 -0
- package/apps/sessrumnir/src/main/omp-fork-points.test.ts +119 -0
- package/apps/sessrumnir/src/main/omp-fork-points.ts +105 -0
- package/apps/sessrumnir/src/main/omp-plugin-list.test.ts +94 -0
- package/apps/sessrumnir/src/main/omp-plugin-list.ts +103 -0
- package/apps/sessrumnir/src/main/package-catalog.test.ts +157 -0
- package/apps/sessrumnir/src/main/package-catalog.ts +166 -0
- package/apps/sessrumnir/src/main/package-updates.test.ts +149 -0
- package/apps/sessrumnir/src/main/package-updates.ts +185 -0
- package/apps/sessrumnir/src/main/path-authorization.test.ts +64 -0
- package/apps/sessrumnir/src/main/path-authorization.ts +31 -0
- package/apps/sessrumnir/src/main/pi-binary-resolution.test.ts +582 -0
- package/apps/sessrumnir/src/main/pi-binary-resolution.ts +649 -0
- package/apps/sessrumnir/src/main/pi-event-router.test.ts +388 -0
- package/apps/sessrumnir/src/main/pi-event-router.ts +290 -0
- package/apps/sessrumnir/src/main/pi-paths.test.ts +134 -0
- package/apps/sessrumnir/src/main/pi-paths.ts +74 -0
- package/apps/sessrumnir/src/main/pi-rpc-manager.test.ts +467 -0
- package/apps/sessrumnir/src/main/pi-rpc-manager.ts +1319 -0
- package/apps/sessrumnir/src/main/process-tree.test.ts +94 -0
- package/apps/sessrumnir/src/main/renderer-origin.test.ts +33 -0
- package/apps/sessrumnir/src/main/renderer-origin.ts +35 -0
- package/apps/sessrumnir/src/main/session-lineage-reader.test.ts +317 -0
- package/apps/sessrumnir/src/main/session-lineage-reader.ts +111 -0
- package/apps/sessrumnir/src/main/session-metadata.test.ts +508 -0
- package/apps/sessrumnir/src/main/session-metadata.ts +526 -0
- package/apps/sessrumnir/src/main/session-name.test.ts +71 -0
- package/apps/sessrumnir/src/main/session-name.ts +69 -0
- package/apps/sessrumnir/src/main/session-paths.test.ts +110 -0
- package/apps/sessrumnir/src/main/session-paths.ts +88 -0
- package/apps/sessrumnir/src/main/session-tags.test.ts +61 -0
- package/apps/sessrumnir/src/main/session-tags.ts +253 -0
- package/apps/sessrumnir/src/main/session-trash.test.ts +66 -0
- package/apps/sessrumnir/src/main/session-trash.ts +65 -0
- package/apps/sessrumnir/src/main/settings-logic.ts +66 -0
- package/apps/sessrumnir/src/main/skills-discovery.test.ts +149 -0
- package/apps/sessrumnir/src/main/skills-discovery.ts +179 -0
- package/apps/sessrumnir/src/main/startup-launch.ts +63 -0
- package/apps/sessrumnir/src/main/terminal-service.ts +96 -0
- package/apps/sessrumnir/src/main/theme-store.test.ts +440 -0
- package/apps/sessrumnir/src/main/theme-store.ts +495 -0
- package/apps/sessrumnir/src/main/tray-decision.test.ts +46 -0
- package/apps/sessrumnir/src/main/tray-decision.ts +49 -0
- package/apps/sessrumnir/src/main/tray-manager.ts +220 -0
- package/apps/sessrumnir/src/main/tsconfig.json +20 -0
- package/apps/sessrumnir/src/main/window-background.test.ts +29 -0
- package/apps/sessrumnir/src/main/window-background.ts +16 -0
- package/apps/sessrumnir/src/main/workflow-discovery.test.ts +252 -0
- package/apps/sessrumnir/src/main/workflow-monitor.test.ts +371 -0
- package/apps/sessrumnir/src/main/workflow-monitor.ts +1020 -0
- package/apps/sessrumnir/src/main/workspace-activity.test.ts +227 -0
- package/apps/sessrumnir/src/main/workspace-activity.ts +215 -0
- package/apps/sessrumnir/src/main/workspace-manager.test.ts +680 -0
- package/apps/sessrumnir/src/main/workspace-manager.ts +1199 -0
- package/apps/sessrumnir/src/main/workspace-trust.test.ts +78 -0
- package/apps/sessrumnir/src/main/workspace-trust.ts +84 -0
- package/apps/sessrumnir/src/preload/index.ts +358 -0
- package/apps/sessrumnir/src/preload/tsconfig.json +20 -0
- package/apps/sessrumnir/src/renderer/index.html +18 -0
- package/apps/sessrumnir/src/renderer/public/theme-boot.js +30 -0
- package/apps/sessrumnir/src/renderer/src/app.tsx +194 -0
- package/apps/sessrumnir/src/renderer/src/assets/openmoji-color.woff2 +0 -0
- package/apps/sessrumnir/src/renderer/src/assets/pi-logo.svg +7 -0
- package/apps/sessrumnir/src/renderer/src/assets/ymir-mark.svg +55 -0
- package/apps/sessrumnir/src/renderer/src/assets.d.ts +7 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-code-highlight.ts +239 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-file-link.ts +140 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-input.tsx +729 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.test.ts +175 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.ts +100 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-panel.tsx +523 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-project-picker.tsx +193 -0
- package/apps/sessrumnir/src/renderer/src/components/chat-search.tsx +251 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-highlight.ts +85 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-language.test.ts +19 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor-language.ts +88 -0
- package/apps/sessrumnir/src/renderer/src/components/code-editor.tsx +128 -0
- package/apps/sessrumnir/src/renderer/src/components/command-palette.tsx +315 -0
- package/apps/sessrumnir/src/renderer/src/components/command-results.tsx +80 -0
- package/apps/sessrumnir/src/renderer/src/components/composer-permission-menu.tsx +91 -0
- package/apps/sessrumnir/src/renderer/src/components/context-menu.tsx +457 -0
- package/apps/sessrumnir/src/renderer/src/components/copy-button.tsx +36 -0
- package/apps/sessrumnir/src/renderer/src/components/council-panels.tsx +193 -0
- package/apps/sessrumnir/src/renderer/src/components/custom-models-editor.tsx +330 -0
- package/apps/sessrumnir/src/renderer/src/components/diagnostics-panel.tsx +380 -0
- package/apps/sessrumnir/src/renderer/src/components/diff-viewer.tsx +318 -0
- package/apps/sessrumnir/src/renderer/src/components/ember-background.tsx +113 -0
- package/apps/sessrumnir/src/renderer/src/components/error-boundary.tsx +38 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.test.ts +43 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.ts +51 -0
- package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog.tsx +486 -0
- package/apps/sessrumnir/src/renderer/src/components/file-tree.tsx +784 -0
- package/apps/sessrumnir/src/renderer/src/components/git-conveyor-actions.tsx +246 -0
- package/apps/sessrumnir/src/renderer/src/components/home-screen.tsx +396 -0
- package/apps/sessrumnir/src/renderer/src/components/image-viewer.tsx +100 -0
- package/apps/sessrumnir/src/renderer/src/components/line-numbered-code.tsx +58 -0
- package/apps/sessrumnir/src/renderer/src/components/markdown-renderer.tsx +237 -0
- package/apps/sessrumnir/src/renderer/src/components/message-bubble.tsx +1006 -0
- package/apps/sessrumnir/src/renderer/src/components/mission-control.tsx +341 -0
- package/apps/sessrumnir/src/renderer/src/components/model-selector.tsx +219 -0
- package/apps/sessrumnir/src/renderer/src/components/note-picker.tsx +155 -0
- package/apps/sessrumnir/src/renderer/src/components/notes-panel.tsx +437 -0
- package/apps/sessrumnir/src/renderer/src/components/package-browser.tsx +604 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-mode.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-mode.ts +49 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.test.ts +44 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor.tsx +226 -0
- package/apps/sessrumnir/src/renderer/src/components/permission-selector.tsx +104 -0
- package/apps/sessrumnir/src/renderer/src/components/resize-handle.tsx +51 -0
- package/apps/sessrumnir/src/renderer/src/components/review-rail.tsx +194 -0
- package/apps/sessrumnir/src/renderer/src/components/session-menu-position.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/components/session-menu-position.ts +34 -0
- package/apps/sessrumnir/src/renderer/src/components/session-panel.tsx +711 -0
- package/apps/sessrumnir/src/renderer/src/components/session-runtime-indicator.tsx +43 -0
- package/apps/sessrumnir/src/renderer/src/components/settings-panel.tsx +1360 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.test.ts +47 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.ts +76 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.test.ts +73 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.ts +47 -0
- package/apps/sessrumnir/src/renderer/src/components/sidebar.tsx +1045 -0
- package/apps/sessrumnir/src/renderer/src/components/skills-panel.tsx +159 -0
- package/apps/sessrumnir/src/renderer/src/components/stats-panel.tsx +368 -0
- package/apps/sessrumnir/src/renderer/src/components/status-bar.tsx +274 -0
- package/apps/sessrumnir/src/renderer/src/components/status-popover.tsx +481 -0
- package/apps/sessrumnir/src/renderer/src/components/streaming-bubble.tsx +128 -0
- package/apps/sessrumnir/src/renderer/src/components/subagent-progress.tsx +254 -0
- package/apps/sessrumnir/src/renderer/src/components/task-launcher.tsx +168 -0
- package/apps/sessrumnir/src/renderer/src/components/terminal.tsx +203 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.test.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.ts +35 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-editor.tsx +440 -0
- package/apps/sessrumnir/src/renderer/src/components/theme-gallery.tsx +373 -0
- package/apps/sessrumnir/src/renderer/src/components/thinking-level-selector.tsx +85 -0
- package/apps/sessrumnir/src/renderer/src/components/timeline.tsx +289 -0
- package/apps/sessrumnir/src/renderer/src/components/tool-call-icon.ts +32 -0
- package/apps/sessrumnir/src/renderer/src/components/workflow-navigator.tsx +901 -0
- package/apps/sessrumnir/src/renderer/src/components/workspace-tabs.tsx +241 -0
- package/apps/sessrumnir/src/renderer/src/global.d.ts +8 -0
- package/apps/sessrumnir/src/renderer/src/hooks/use-folder-drop.ts +101 -0
- package/apps/sessrumnir/src/renderer/src/hooks.test.ts +88 -0
- package/apps/sessrumnir/src/renderer/src/hooks.ts +574 -0
- package/apps/sessrumnir/src/renderer/src/i18n.test.ts +54 -0
- package/apps/sessrumnir/src/renderer/src/i18n.ts +89 -0
- package/apps/sessrumnir/src/renderer/src/index.css +425 -0
- package/apps/sessrumnir/src/renderer/src/main.tsx +42 -0
- package/apps/sessrumnir/src/renderer/src/message-grouping.test.ts +422 -0
- package/apps/sessrumnir/src/renderer/src/message-grouping.ts +410 -0
- package/apps/sessrumnir/src/renderer/src/message-parsing.test.ts +86 -0
- package/apps/sessrumnir/src/renderer/src/message-parsing.ts +166 -0
- package/apps/sessrumnir/src/renderer/src/store-external-prompts.test.ts +161 -0
- package/apps/sessrumnir/src/renderer/src/store-session-transitions.test.ts +2090 -0
- package/apps/sessrumnir/src/renderer/src/store-timeline.test.ts +52 -0
- package/apps/sessrumnir/src/renderer/src/store-turn-error.test.ts +136 -0
- package/apps/sessrumnir/src/renderer/src/store.ts +3726 -0
- package/apps/sessrumnir/src/renderer/src/subagent-detection.test.ts +55 -0
- package/apps/sessrumnir/src/renderer/src/theme/engine.test.ts +29 -0
- package/apps/sessrumnir/src/renderer/src/theme/engine.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-claudius.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-dark.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/breeze-light.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/builtin-themes.test.ts +95 -0
- package/apps/sessrumnir/src/renderer/src/themes/dark.json +46 -0
- package/apps/sessrumnir/src/renderer/src/themes/fensalir.json +82 -0
- package/apps/sessrumnir/src/renderer/src/themes/gruvbox.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/index.ts +60 -0
- package/apps/sessrumnir/src/renderer/src/themes/light.json +46 -0
- package/apps/sessrumnir/src/renderer/src/themes/nord.json +69 -0
- package/apps/sessrumnir/src/renderer/src/themes/sessrumnir.json +82 -0
- package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.test.ts +81 -0
- package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.ts +57 -0
- package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.test.ts +62 -0
- package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.ts +28 -0
- package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.test.ts +57 -0
- package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.ts +41 -0
- package/apps/sessrumnir/src/renderer/src/utils/ipc-error.test.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/utils/ipc-error.ts +12 -0
- package/apps/sessrumnir/src/renderer/src/utils/model-search.test.ts +63 -0
- package/apps/sessrumnir/src/renderer/src/utils/model-search.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.test.ts +31 -0
- package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.ts +27 -0
- package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.test.ts +15 -0
- package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.ts +22 -0
- package/apps/sessrumnir/src/renderer/src/utils/process-status-label.test.ts +13 -0
- package/apps/sessrumnir/src/renderer/src/utils/process-status-label.ts +19 -0
- package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.test.ts +68 -0
- package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.ts +52 -0
- package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.test.ts +46 -0
- package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.ts +23 -0
- package/apps/sessrumnir/src/renderer/src/utils/relative-time.tsx +25 -0
- package/apps/sessrumnir/src/renderer/src/utils/session-title.test.ts +98 -0
- package/apps/sessrumnir/src/renderer/src/utils/session-title.ts +58 -0
- package/apps/sessrumnir/src/renderer/src/utils/stale-guard.test.ts +29 -0
- package/apps/sessrumnir/src/renderer/src/utils/stale-guard.ts +20 -0
- package/apps/sessrumnir/src/renderer/src/utils/theme.test.ts +174 -0
- package/apps/sessrumnir/src/renderer/src/utils/theme.ts +159 -0
- package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.test.ts +130 -0
- package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.ts +84 -0
- package/apps/sessrumnir/src/renderer/tsconfig.json +24 -0
- package/apps/sessrumnir/src/shared/agent-engine-label.test.ts +45 -0
- package/apps/sessrumnir/src/shared/agent-engine-label.ts +53 -0
- package/apps/sessrumnir/src/shared/app-settings.test.ts +25 -0
- package/apps/sessrumnir/src/shared/app-settings.ts +23 -0
- package/apps/sessrumnir/src/shared/bridge-http.ts +341 -0
- package/apps/sessrumnir/src/shared/bridge.ts +337 -0
- package/apps/sessrumnir/src/shared/council-config.test.ts +300 -0
- package/apps/sessrumnir/src/shared/council-config.ts +356 -0
- package/apps/sessrumnir/src/shared/default-settings.ts +39 -0
- package/apps/sessrumnir/src/shared/folder-drop.test.ts +118 -0
- package/apps/sessrumnir/src/shared/folder-drop.ts +74 -0
- package/apps/sessrumnir/src/shared/fork-point.test.ts +30 -0
- package/apps/sessrumnir/src/shared/fork-point.ts +24 -0
- package/apps/sessrumnir/src/shared/i18n/i18next.d.ts +10 -0
- package/apps/sessrumnir/src/shared/i18n/index.test.ts +51 -0
- package/apps/sessrumnir/src/shared/i18n/index.ts +50 -0
- package/apps/sessrumnir/src/shared/i18n/languages.ts +12 -0
- package/apps/sessrumnir/src/shared/i18n/locale-checks.ts +98 -0
- package/apps/sessrumnir/src/shared/i18n/locales.test.ts +92 -0
- package/apps/sessrumnir/src/shared/i18n/pseudo.test.ts +39 -0
- package/apps/sessrumnir/src/shared/i18n/pseudo.ts +44 -0
- package/apps/sessrumnir/src/shared/i18n/resolve.test.ts +43 -0
- package/apps/sessrumnir/src/shared/i18n/resolve.ts +41 -0
- package/apps/sessrumnir/src/shared/i18n/resources.ts +11 -0
- package/apps/sessrumnir/src/shared/ipc-contracts.ts +1539 -0
- package/apps/sessrumnir/src/shared/models-config.test.ts +124 -0
- package/apps/sessrumnir/src/shared/models-config.ts +167 -0
- package/apps/sessrumnir/src/shared/package-filter.ts +19 -0
- package/apps/sessrumnir/src/shared/package-spec.test.ts +51 -0
- package/apps/sessrumnir/src/shared/package-spec.ts +25 -0
- package/apps/sessrumnir/src/shared/path-compare.test.ts +38 -0
- package/apps/sessrumnir/src/shared/path-compare.ts +76 -0
- package/apps/sessrumnir/src/shared/permission-mode.ts +8 -0
- package/apps/sessrumnir/src/shared/pi-command.test.ts +165 -0
- package/apps/sessrumnir/src/shared/pi-command.ts +139 -0
- package/apps/sessrumnir/src/shared/product-name.ts +7 -0
- package/apps/sessrumnir/src/shared/session-lineage.test.ts +103 -0
- package/apps/sessrumnir/src/shared/session-lineage.ts +50 -0
- package/apps/sessrumnir/src/shared/session-preview.test.ts +93 -0
- package/apps/sessrumnir/src/shared/session-preview.ts +56 -0
- package/apps/sessrumnir/src/shared/sidebar-width.test.ts +58 -0
- package/apps/sessrumnir/src/shared/sidebar-width.ts +37 -0
- package/apps/sessrumnir/src/shared/theme/builtin-ids.ts +16 -0
- package/apps/sessrumnir/src/shared/theme/resolve.test.ts +66 -0
- package/apps/sessrumnir/src/shared/theme/resolve.ts +29 -0
- package/apps/sessrumnir/src/shared/theme/syntax-defaults.ts +24 -0
- package/apps/sessrumnir/src/shared/theme/theme-file.test.ts +88 -0
- package/apps/sessrumnir/src/shared/theme/theme-file.ts +132 -0
- package/apps/sessrumnir/src/shared/theme/tokens.test.ts +35 -0
- package/apps/sessrumnir/src/shared/theme/tokens.ts +86 -0
- package/apps/sessrumnir/src/shared/tsconfig.json +17 -0
- package/apps/sessrumnir/src/shared/untrusted-data.test.ts +32 -0
- package/apps/sessrumnir/src/shared/untrusted-data.ts +22 -0
- package/apps/sessrumnir/src/shared/version-compare.test.ts +33 -0
- package/apps/sessrumnir/src/shared/version-compare.ts +50 -0
- package/apps/sessrumnir/src/shared/workflow-control.ts +33 -0
- package/apps/sessrumnir/src/web-server.ts +315 -0
- package/apps/sessrumnir/tsconfig.json +26 -0
- package/apps/smidja/smidja_build.py +45 -0
- package/apps/smidja/smidja_build_review.py +76 -0
- package/apps/smidja/smidja_build_test.py +79 -0
- package/apps/smidja/smidja_document.py +76 -0
- package/apps/smidja/smidja_modules/agent_cc.py +15 -0
- package/apps/smidja/smidja_modules/agent_opencode.py +194 -0
- package/apps/smidja/smidja_modules/agent_pi.py +392 -0
- package/apps/smidja/smidja_modules/agents.py +993 -0
- package/apps/smidja/smidja_modules/changes.py +103 -0
- package/apps/smidja/smidja_modules/console.py +131 -0
- package/apps/smidja/smidja_modules/data_types.py +447 -0
- package/apps/smidja/smidja_modules/gates.py +108 -0
- package/apps/smidja/smidja_modules/git_helper.py +120 -0
- package/apps/smidja/smidja_modules/lm_local.py +144 -0
- package/apps/smidja/smidja_modules/permissions.py +194 -0
- package/apps/smidja/smidja_modules/prompts.py +21 -0
- package/apps/smidja/smidja_modules/quality.py +240 -0
- package/apps/smidja/smidja_modules/runner.py +155 -0
- package/apps/smidja/smidja_modules/session.py +50 -0
- package/apps/smidja/smidja_modules/stall_watch.py +149 -0
- package/apps/smidja/smidja_modules/tracer.py +271 -0
- package/apps/smidja/smidja_modules/utils.py +77 -0
- package/apps/smidja/smidja_orchestrate.py +162 -0
- package/apps/smidja/smidja_plan.py +45 -0
- package/apps/smidja/smidja_plan_build.py +55 -0
- package/apps/smidja/smidja_plan_build_test.py +86 -0
- package/apps/smidja/smidja_plan_build_test_quality.py +98 -0
- package/apps/smidja/smidja_prompt.py +44 -0
- package/apps/smidja/smidja_quality.py +49 -0
- package/apps/smidja/smidja_recon_iv.py +202 -0
- package/apps/smidja/smidja_scout.py +45 -0
- package/apps/smidja/smidja_simple_sdlc.py +183 -0
- package/apps/smidja/smidja_smidja_config/smidja.config.yaml +143 -0
- package/apps/smidja-factory/AGENTS.md +165 -0
- package/apps/smidja-factory/README.md +3 -0
- package/apps/smidja-factory/SKILL.md +18 -0
- package/apps/smidja-factory/apps/visualizer/.oxlintrc.json +18 -0
- package/apps/smidja-factory/apps/visualizer/bun.lock +269 -0
- package/apps/smidja-factory/apps/visualizer/desktop/icon.png +0 -0
- package/apps/smidja-factory/apps/visualizer/desktop/main.js +140 -0
- package/apps/smidja-factory/apps/visualizer/desktop/package-lock.json +871 -0
- package/apps/smidja-factory/apps/visualizer/desktop/package.json +18 -0
- package/apps/smidja-factory/apps/visualizer/desktop/preload.js +19 -0
- package/apps/smidja-factory/apps/visualizer/index.html +28 -0
- package/apps/smidja-factory/apps/visualizer/package.json +31 -0
- package/apps/smidja-factory/apps/visualizer/public/icon.svg +7 -0
- package/apps/smidja-factory/apps/visualizer/public/models/claude.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/gemini.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/kimi.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/openai.png +0 -0
- package/apps/smidja-factory/apps/visualizer/public/models/zai.png +0 -0
- package/apps/smidja-factory/apps/visualizer/server/chat.ts +425 -0
- package/apps/smidja-factory/apps/visualizer/server/db.ts +1050 -0
- package/apps/smidja-factory/apps/visualizer/server/index.ts +648 -0
- package/apps/smidja-factory/apps/visualizer/server/model-catalog.ts +90 -0
- package/apps/smidja-factory/apps/visualizer/server/roster-api.ts +75 -0
- package/apps/smidja-factory/apps/visualizer/server/roster_resolve.py +170 -0
- package/apps/smidja-factory/apps/visualizer/server/settings.ts +106 -0
- package/apps/smidja-factory/apps/visualizer/shared/types.ts +630 -0
- package/apps/smidja-factory/apps/visualizer/src/App.vue +379 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatInput.vue +155 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatThread.vue +219 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ChatView.vue +335 -0
- package/apps/smidja-factory/apps/visualizer/src/components/DecisionsView.vue +177 -0
- package/apps/smidja-factory/apps/visualizer/src/components/DetailSection.vue +76 -0
- package/apps/smidja-factory/apps/visualizer/src/components/MemoryView.vue +397 -0
- package/apps/smidja-factory/apps/visualizer/src/components/PhaseDetail.vue +1354 -0
- package/apps/smidja-factory/apps/visualizer/src/components/PhaseDots.vue +55 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionCard.vue +583 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionMiniCard.vue +58 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionPanel.vue +272 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionStartForm.vue +152 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionTrace.vue +1157 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SessionsList.vue +96 -0
- package/apps/smidja-factory/apps/visualizer/src/components/SettingsView.vue +152 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatChip.vue +98 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatsView.vue +382 -0
- package/apps/smidja-factory/apps/visualizer/src/components/StatusChip.vue +73 -0
- package/apps/smidja-factory/apps/visualizer/src/components/ToolCallCard.vue +156 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/api.ts +177 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/chat-api.ts +95 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/chat-store.ts +351 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/events.ts +131 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/format.ts +92 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/highlight.ts +54 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/markdown.ts +120 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/models.ts +42 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/router.ts +67 -0
- package/apps/smidja-factory/apps/visualizer/src/lib/types.ts +61 -0
- package/apps/smidja-factory/apps/visualizer/src/main.ts +13 -0
- package/apps/smidja-factory/apps/visualizer/src/style.css +592 -0
- package/apps/smidja-factory/apps/visualizer/tsconfig.json +30 -0
- package/apps/smidja-factory/apps/visualizer/vite.config.js +32 -0
- package/apps/smidja-factory/apps/visualizer/vite.config.ts +31 -0
- package/apps/smidja-factory/cookbooks/create_config.md +61 -0
- package/apps/smidja-factory/cookbooks/create_smidja.md +120 -0
- package/apps/smidja-factory/cookbooks/how_to_prompt_for_the_eng.md +110 -0
- package/apps/smidja-factory/cookbooks/install.md +56 -0
- package/apps/smidja-factory/cookbooks/run_smidja.md +122 -0
- package/apps/smidja-factory/cookbooks/smidja_overview.md +88 -0
- package/apps/smidja-factory/cookbooks/update_config.md +103 -0
- package/apps/smidja-factory/cookbooks/update_modules.md +100 -0
- package/apps/smidja-factory/cookbooks/update_smidja.md +90 -0
- package/apps/smidja-factory/docs/enhancement-plan.md +420 -0
- package/apps/smidja-factory/references/config.md +202 -0
- package/apps/smidja-factory/references/handoff.md +161 -0
- package/apps/smidja-factory/references/observability.md +166 -0
- package/apps/smidja-factory/scripts/install.py +113 -0
- package/apps/smidja-factory/scripts/make_config.py +35 -0
- package/apps/smidja-factory/scripts/make_smidja.py +119 -0
- package/apps/smidja-factory/skills/add-or-edit-ai-models/SKILL.md +151 -0
- package/apps/smidja-factory/skills/create-new-agent/SKILL.md +125 -0
- package/apps/smidja-factory/skills/create-new-teams/SKILL.md +151 -0
- package/apps/smidja-factory/skills/git-ops/README.md +25 -0
- package/apps/smidja-factory/skills/git-ops/SKILL.md +18 -0
- package/apps/smidja-factory/skills/git-ops/create_branch.sh +79 -0
- package/apps/smidja-factory/skills/git-ops/safe_commit.sh +82 -0
- package/apps/smidja-factory/skills/git-ops/sync_upstream.sh +94 -0
- package/apps/smidja-factory/skills/how-to-run-agent-teams/SKILL.md +117 -0
- package/apps/smidja-factory/skills/smidja-instructions/SKILL.md +168 -0
- package/apps/smidja-factory/skills/smidja-launcher/SKILL.md +155 -0
- package/apps/smidja-factory/skills/smidja-start/SKILL.md +266 -0
- package/apps/smidja-factory/skills/volundr/SKILL.md +232 -0
- package/apps/smidja-factory/skills/volundr/prompt/dispatch-prompt.md +91 -0
- package/apps/smidja-factory/skills/volundr/prompt/volundr-system.md +63 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-observe.py +52 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-recall.py +54 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-status.py +51 -0
- package/apps/smidja-factory/skills/volundr/scripts/volundr-teach.py +47 -0
- package/apps/smidja-factory/templates/env.sample +28 -0
- package/apps/smidja-factory/templates/harness_engineering/subagents.ts +706 -0
- package/apps/smidja-factory/templates/harness_engineering/themeMap.ts +145 -0
- package/apps/smidja-factory/templates/justfile +89 -0
- package/apps/smidja-factory/templates/prompt_engineering/builder/system.md +13 -0
- package/apps/smidja-factory/templates/prompt_engineering/builder/user.md +34 -0
- package/apps/smidja-factory/templates/prompt_engineering/documenter/system.md +17 -0
- package/apps/smidja-factory/templates/prompt_engineering/documenter/user.md +48 -0
- package/apps/smidja-factory/templates/prompt_engineering/orchestrator/system.md +55 -0
- package/apps/smidja-factory/templates/prompt_engineering/orchestrator/user.md +39 -0
- package/apps/smidja-factory/templates/prompt_engineering/planner/system.md +21 -0
- package/apps/smidja-factory/templates/prompt_engineering/planner/user.md +45 -0
- package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/system.md +51 -0
- package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/user.md +26 -0
- package/apps/smidja-factory/templates/prompt_engineering/reviewer/system.md +16 -0
- package/apps/smidja-factory/templates/prompt_engineering/reviewer/user.md +44 -0
- package/apps/smidja-factory/templates/prompt_engineering/scout/system.md +32 -0
- package/apps/smidja-factory/templates/prompt_engineering/scout/user.md +34 -0
- package/apps/smidja-factory/templates/smidja/smidja_build.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_build_review.py +76 -0
- package/apps/smidja-factory/templates/smidja/smidja_build_test.py +79 -0
- package/apps/smidja-factory/templates/smidja/smidja_document.py +76 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_cc.py +15 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_opencode.py +194 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agent_pi.py +392 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/agents.py +993 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/changes.py +103 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/console.py +131 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/data_types.py +447 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/gates.py +108 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/git_helper.py +120 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/lm_local.py +144 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/permissions.py +194 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/prompts.py +21 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/quality.py +240 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/runner.py +155 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/session.py +50 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/stall_watch.py +149 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/tracer.py +271 -0
- package/apps/smidja-factory/templates/smidja/smidja_modules/utils.py +77 -0
- package/apps/smidja-factory/templates/smidja/smidja_orchestrate.py +162 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build.py +55 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build_test.py +86 -0
- package/apps/smidja-factory/templates/smidja/smidja_plan_build_test_quality.py +98 -0
- package/apps/smidja-factory/templates/smidja/smidja_prompt.py +44 -0
- package/apps/smidja-factory/templates/smidja/smidja_quality.py +49 -0
- package/apps/smidja-factory/templates/smidja/smidja_recon_iv.py +202 -0
- package/apps/smidja-factory/templates/smidja/smidja_scout.py +45 -0
- package/apps/smidja-factory/templates/smidja/smidja_simple_sdlc.py +183 -0
- package/apps/smidja-factory/templates/smidja.config.yaml +143 -0
- package/bin/app-build.sh +36 -0
- package/bin/electron-lib.sh +25 -0
- package/bin/sessrumnir.sh +1 -1
- package/bin/ymir-install.sh +7 -0
- package/install.sh +88 -0
- package/package.json +16 -7
- package/scripts/electron.sh +6 -2
- package/.agents/bus/messages.json +0 -1
- package/.agents/config/agents.machine.example.yaml +0 -16
- package/.pi/mcp.json +0 -16
- package/assets/reference/state/.branch-eligible-owner +0 -4
- package/assets/reference/state/.branch-mirror-cursor +0 -1
- package/assets/reference/state/.branch-outcomes-cursor +0 -1
- package/assets/reference/state/.branch-session +0 -1
- package/assets/reference/state/.count-default_wF_p5 +0 -1
- package/assets/reference/state/.count-default_wF_p6 +0 -1
- package/assets/reference/state/.count-default_wR_p2 +0 -1
- package/assets/reference/state/.count-default_wS_p2 +0 -1
- package/assets/reference/state/.count-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.hash-default_wF_p5 +0 -1
- package/assets/reference/state/.hash-default_wF_p6 +0 -1
- package/assets/reference/state/.hash-default_wR_p2 +0 -1
- package/assets/reference/state/.hash-default_wS_p2 +0 -1
- package/assets/reference/state/.hash-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.heartbeat-streak +0 -1
- package/assets/reference/state/.inactive-outcome-reconcile +0 -2
- package/assets/reference/state/.lock +0 -1
- package/assets/reference/state/.pi-branch-extension-loaded +0 -1
- package/assets/reference/state/.pi-turnend-extension-loaded +0 -2
- package/assets/reference/state/.pi-watch-extension-loaded +0 -2
- package/assets/reference/state/.seen-server-knowledge_turn-ended +0 -1
- package/assets/reference/state/.session-start-agents-baseline +0 -2
- package/assets/reference/state/.session-start-complete +0 -1
- package/assets/reference/state/.stale-default_wF_p5 +0 -1
- package/assets/reference/state/.stale-default_wR_p2 +0 -1
- package/assets/reference/state/.stale-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.stale-since-default_wF_p5 +0 -1
- package/assets/reference/state/.stale-since-default_wR_p2 +0 -1
- package/assets/reference/state/.stale-since-firstmate_fm-server-knowledge +0 -1
- package/assets/reference/state/.startup-network.delivered +0 -1
- package/assets/reference/state/.startup-network.report +0 -1
- package/assets/reference/state/.startup-network.status +0 -10
- package/assets/reference/state/.startup-network.timings +0 -6
- package/assets/reference/state/.trace-context-effective +0 -1
- package/assets/reference/state/.wake-queue +0 -0
- package/assets/reference/state/.wake-queue.seq +0 -1
- package/assets/reference/state/.wake-rows.consume.BcqXSq +0 -0
- package/assets/reference/state/.wake-rows.consume.NDZZei +0 -0
- package/assets/reference/state/.wake-rows.consume.VGkhYm +0 -0
- package/assets/reference/state/.wake-rows.consume.tR2rTy +0 -0
- package/assets/reference/state/.watch-arm-output.3581p6 +0 -0
- package/assets/reference/state/.watch-arm-output.3ciDNP +0 -0
- package/assets/reference/state/.watch-arm-output.SksR6k +0 -0
- package/assets/reference/state/.watch-cycle-exits.log +0 -22
- package/assets/reference/state/.watch-deliveries.log +0 -19
- package/assets/reference/state/.watch-triage.log +0 -38
- package/assets/reference/state/.watch.lock/fm-home +0 -1
- package/assets/reference/state/.watch.lock/pid +0 -1
- package/assets/reference/state/.watch.lock/pid-identity +0 -1
- package/assets/reference/state/.watch.lock/watcher-path +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/fm-home +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/pid +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/pid-identity +0 -1
- package/assets/reference/state/.watch.lock.owner.qqbxmS/watcher-path +0 -1
- package/assets/reference/state/.watcher-down +0 -1
- package/assets/reference/state/branch-outcomes.jsonl +0 -1
- package/assets/reference/state/branch-session/2026-09-04T16-49-38-671Z_01a06d53-6b6f-7619-930d-e6dfd9a4a29e.jsonl +0 -211
- package/assets/reference/state/home-summary.json +0 -42
- package/assets/reference/state/server-knowledge.busy-gen +0 -1
- package/assets/reference/state/server-knowledge.busy-state +0 -1
- package/assets/reference/state/server-knowledge.herdr-presentation +0 -12
- package/assets/reference/state/server-knowledge.meta +0 -18
- package/assets/reference/state/server-knowledge.pi-ext.ts +0 -26
- package/assets/reference/state/server-knowledge.turn-ended +0 -0
- /package/{assets/reference/state/.last-check → apps/hlidskjalf/src/app/.gitkeep} +0 -0
- /package/{assets/reference/state/.last-heartbeat → apps/hlidskjalf/src/components/.gitkeep} +0 -0
- /package/{assets/reference/state/.last-watcher-beat → apps/hlidskjalf/src/services/.gitkeep} +0 -0
- /package/{assets/reference/state/.main-eligible-rows.tmp.p2HMKo → apps/smidja/smidja_modules/__init__.py} +0 -0
- /package/{assets/reference/state/.main-eligible-rows.tmp.Exxm82 → apps/smidja-factory/templates/smidja/smidja_modules/__init__.py} +0 -0
|
@@ -0,0 +1,1006 @@
|
|
|
1
|
+
import { memo, useState, useRef } from 'react'
|
|
2
|
+
import { useTranslation } from 'react-i18next'
|
|
3
|
+
import { useAppStore, type DisplayMessage } from '../store'
|
|
4
|
+
import { modelDisplayName } from '../../../shared/models-config'
|
|
5
|
+
import { DEFAULT_SETTINGS } from '../../../shared/default-settings'
|
|
6
|
+
import {
|
|
7
|
+
toolCallLabel,
|
|
8
|
+
toolCallStatusLabel,
|
|
9
|
+
toolKind,
|
|
10
|
+
toolCallFile,
|
|
11
|
+
parseEdits,
|
|
12
|
+
editStats,
|
|
13
|
+
splitReadTruncationNote,
|
|
14
|
+
type EditBlock,
|
|
15
|
+
} from '../message-grouping'
|
|
16
|
+
import { toolCallIconFor } from './tool-call-icon'
|
|
17
|
+
import { getCodeEditorLanguageName } from './code-editor-language'
|
|
18
|
+
import { highlightCodeToHtml } from './chat-code-highlight'
|
|
19
|
+
import { LineNumberedCode } from './line-numbered-code'
|
|
20
|
+
import { MarkdownRenderer } from './markdown-renderer'
|
|
21
|
+
import { CopyButton } from './copy-button'
|
|
22
|
+
import { useContextMenu, buildMessageContextMenu } from './context-menu'
|
|
23
|
+
import { RelativeTime } from '../utils/relative-time'
|
|
24
|
+
import { clsx } from 'clsx'
|
|
25
|
+
import {
|
|
26
|
+
Copy,
|
|
27
|
+
Check,
|
|
28
|
+
ChevronDown,
|
|
29
|
+
ChevronRight,
|
|
30
|
+
Brain,
|
|
31
|
+
Bot,
|
|
32
|
+
Edit3,
|
|
33
|
+
GitBranch,
|
|
34
|
+
RotateCcw,
|
|
35
|
+
Download,
|
|
36
|
+
Send,
|
|
37
|
+
} from 'lucide-react'
|
|
38
|
+
|
|
39
|
+
function MessageBubbleImpl({
|
|
40
|
+
message,
|
|
41
|
+
onRetry,
|
|
42
|
+
hideModelHeader,
|
|
43
|
+
}: {
|
|
44
|
+
message: DisplayMessage
|
|
45
|
+
onRetry?: (messageId: string) => void
|
|
46
|
+
// When rendered inside a tool group that shows a single shared model header,
|
|
47
|
+
// suppress this message's own provider · model line to avoid repetition.
|
|
48
|
+
hideModelHeader?: boolean
|
|
49
|
+
}): React.JSX.Element {
|
|
50
|
+
const { t } = useTranslation()
|
|
51
|
+
const [copied, setCopied] = useState(false)
|
|
52
|
+
const [showThinking, setShowThinking] = useState(false)
|
|
53
|
+
const [isEditing, setIsEditing] = useState(false)
|
|
54
|
+
const [editContent, setEditContent] = useState(message.content)
|
|
55
|
+
|
|
56
|
+
const handleCopy = async () => {
|
|
57
|
+
await navigator.clipboard.writeText(message.content)
|
|
58
|
+
setCopied(true)
|
|
59
|
+
setTimeout(() => setCopied(false), 2000)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const handleEdit = () => {
|
|
63
|
+
setEditContent(message.content)
|
|
64
|
+
setIsEditing(true)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const handleSaveEdit = async () => {
|
|
68
|
+
if (editContent.trim() !== message.content) {
|
|
69
|
+
// Resend the edited message
|
|
70
|
+
await useAppStore.getState().sendPrompt(editContent.trim())
|
|
71
|
+
}
|
|
72
|
+
setIsEditing(false)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const handleCancelEdit = () => {
|
|
76
|
+
setIsEditing(false)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const handleBranch = () => {
|
|
80
|
+
// Branch from this message's position
|
|
81
|
+
const excerpt = message.content.slice(0, 100) + (message.content.length > 100 ? '...' : '')
|
|
82
|
+
useAppStore.getState().addMessage({
|
|
83
|
+
id: `branch-${Date.now()}`,
|
|
84
|
+
role: 'system',
|
|
85
|
+
content: t('chat.message.branchedFrom', { excerpt }),
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const handleExport = () => {
|
|
91
|
+
const blob = new Blob([message.content], { type: 'text/markdown' })
|
|
92
|
+
const url = URL.createObjectURL(blob)
|
|
93
|
+
const a = document.createElement('a')
|
|
94
|
+
a.href = url
|
|
95
|
+
const label = message.role === 'assistant' ? 'desktop' : message.role
|
|
96
|
+
const d = new Date(message.timestamp ?? Date.now())
|
|
97
|
+
const p = (n: number): string => String(n).padStart(2, '0')
|
|
98
|
+
// Format: yyyy-mm-ddThh-mm-ss (local time)
|
|
99
|
+
const stamp = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`
|
|
100
|
+
a.download = `pi-${label}-${stamp}.md`
|
|
101
|
+
document.body.appendChild(a)
|
|
102
|
+
a.click()
|
|
103
|
+
a.remove()
|
|
104
|
+
URL.revokeObjectURL(url)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Context menu for message-specific actions
|
|
108
|
+
const { show: showContextMenu, ContextMenuComponent: MessageContextMenu } = useContextMenu()
|
|
109
|
+
|
|
110
|
+
const startNoteFromText = useAppStore((state) => state.startNoteFromText)
|
|
111
|
+
|
|
112
|
+
const handleMessageContextMenu = (e: React.MouseEvent) => {
|
|
113
|
+
showContextMenu(e, buildMessageContextMenu(message.content, startNoteFromText))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (message.role === 'user') {
|
|
117
|
+
return (
|
|
118
|
+
<>
|
|
119
|
+
<div data-scroll-anchor={message.id} onContextMenu={handleMessageContextMenu}>
|
|
120
|
+
<UserMessage
|
|
121
|
+
message={message}
|
|
122
|
+
isEditing={isEditing}
|
|
123
|
+
editContent={editContent}
|
|
124
|
+
onEditContentChange={setEditContent}
|
|
125
|
+
onCopy={handleCopy}
|
|
126
|
+
onEdit={handleEdit}
|
|
127
|
+
onSaveEdit={handleSaveEdit}
|
|
128
|
+
onCancelEdit={handleCancelEdit}
|
|
129
|
+
onBranch={handleBranch}
|
|
130
|
+
onRetry={onRetry}
|
|
131
|
+
onExport={handleExport}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
{MessageContextMenu}
|
|
135
|
+
</>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (message.role === 'assistant') {
|
|
140
|
+
return (
|
|
141
|
+
<>
|
|
142
|
+
<div onContextMenu={handleMessageContextMenu}>
|
|
143
|
+
<AssistantMessage
|
|
144
|
+
message={message}
|
|
145
|
+
onCopy={handleCopy}
|
|
146
|
+
copied={copied}
|
|
147
|
+
showThinking={showThinking}
|
|
148
|
+
onToggleThinking={() => setShowThinking(!showThinking)}
|
|
149
|
+
onExport={handleExport}
|
|
150
|
+
hideModelHeader={hideModelHeader}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
{MessageContextMenu}
|
|
154
|
+
</>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (message.role === 'toolResult') {
|
|
159
|
+
return (
|
|
160
|
+
<>
|
|
161
|
+
<div onContextMenu={handleMessageContextMenu}>
|
|
162
|
+
<ToolResultMessage message={message} />
|
|
163
|
+
</div>
|
|
164
|
+
{MessageContextMenu}
|
|
165
|
+
</>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (message.role === 'system') {
|
|
170
|
+
return <SystemMessage message={message} />
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return <></>
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Memoized so finalized messages don't re-parse markdown on every store update
|
|
177
|
+
// (e.g. during streaming of a later message). Relies on stable `message`
|
|
178
|
+
// references and a stable `onRetry` callback from the caller.
|
|
179
|
+
export const MessageBubble = memo(MessageBubbleImpl)
|
|
180
|
+
|
|
181
|
+
// ─── User Message ────────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
function UserMessage({
|
|
184
|
+
message,
|
|
185
|
+
isEditing,
|
|
186
|
+
editContent,
|
|
187
|
+
onEditContentChange,
|
|
188
|
+
onCopy,
|
|
189
|
+
onEdit,
|
|
190
|
+
onSaveEdit,
|
|
191
|
+
onCancelEdit,
|
|
192
|
+
onBranch,
|
|
193
|
+
onRetry,
|
|
194
|
+
onExport,
|
|
195
|
+
}: {
|
|
196
|
+
message: DisplayMessage
|
|
197
|
+
isEditing: boolean
|
|
198
|
+
editContent: string
|
|
199
|
+
onEditContentChange: (v: string) => void
|
|
200
|
+
onCopy: () => void
|
|
201
|
+
onEdit: () => void
|
|
202
|
+
onSaveEdit: () => void
|
|
203
|
+
onCancelEdit: () => void
|
|
204
|
+
onBranch: () => void
|
|
205
|
+
onRetry?: (id: string) => void
|
|
206
|
+
onExport: () => void
|
|
207
|
+
}): React.JSX.Element {
|
|
208
|
+
const { t } = useTranslation()
|
|
209
|
+
const editRef = useRef<HTMLTextAreaElement>(null)
|
|
210
|
+
|
|
211
|
+
if (isEditing) {
|
|
212
|
+
return (
|
|
213
|
+
<div className="group mb-4 flex justify-end animate-fade-in">
|
|
214
|
+
<div className="w-full max-w-[80%]">
|
|
215
|
+
<textarea
|
|
216
|
+
ref={editRef}
|
|
217
|
+
value={editContent}
|
|
218
|
+
onChange={(e) => onEditContentChange(e.target.value)}
|
|
219
|
+
className="font-chat w-full rounded-2xl rounded-br-md bg-card px-4 py-2.5 text-sm text-primary resize-none min-h-[40px] max-h-48 outline-none"
|
|
220
|
+
rows={1}
|
|
221
|
+
onInput={(e) => {
|
|
222
|
+
const t = e.currentTarget
|
|
223
|
+
t.style.height = 'auto'
|
|
224
|
+
t.style.height = `${Math.min(t.scrollHeight, 192)}px`
|
|
225
|
+
}}
|
|
226
|
+
autoFocus
|
|
227
|
+
/>
|
|
228
|
+
<div className="flex items-center justify-end gap-1 mt-1">
|
|
229
|
+
<button
|
|
230
|
+
onClick={onCancelEdit}
|
|
231
|
+
className="rounded px-2 py-1 text-xs text-muted hover:text-primary transition-colors"
|
|
232
|
+
>
|
|
233
|
+
{t('common.cancel')}
|
|
234
|
+
</button>
|
|
235
|
+
<button
|
|
236
|
+
onClick={onSaveEdit}
|
|
237
|
+
className="flex items-center gap-1 rounded bg-accent px-2 py-1 text-xs text-inverse hover:bg-accent-hover transition-colors"
|
|
238
|
+
>
|
|
239
|
+
<Send size={10} />
|
|
240
|
+
{t('common.send')}
|
|
241
|
+
</button>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<div className="group mb-4 flex justify-end animate-fade-in">
|
|
250
|
+
<div className="relative max-w-[80%]">
|
|
251
|
+
<div className="rounded-2xl rounded-br-md bg-card px-4 py-2.5 text-sm text-primary">
|
|
252
|
+
{message.attachments && message.attachments.length > 0 && (
|
|
253
|
+
<div className={clsx('flex flex-wrap gap-2', message.content && 'mb-2')}>
|
|
254
|
+
{message.attachments.map((attachment, index) => (
|
|
255
|
+
<div
|
|
256
|
+
key={`${attachment.name}-${index}`}
|
|
257
|
+
className="overflow-hidden rounded-md border border-white/20 bg-black/10"
|
|
258
|
+
title={attachment.name}
|
|
259
|
+
>
|
|
260
|
+
<img
|
|
261
|
+
src={`data:${attachment.mimeType};base64,${attachment.data}`}
|
|
262
|
+
alt={attachment.name}
|
|
263
|
+
className="h-16 w-16 object-cover"
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
))}
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
<div className="font-chat whitespace-pre-wrap break-words">{message.content}</div>
|
|
270
|
+
</div>
|
|
271
|
+
{/* Actions */}
|
|
272
|
+
<div className="mt-1 flex items-center justify-end gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
273
|
+
<ActionButton icon={<Copy size={11} />} onClick={onCopy} title={t('common.copy')} />
|
|
274
|
+
<ActionButton icon={<Edit3 size={11} />} onClick={onEdit} title={t('chat.message.editAndResend')} />
|
|
275
|
+
<ActionButton icon={<GitBranch size={11} />} onClick={onBranch} title={t('chat.message.branchFromHere')} />
|
|
276
|
+
{onRetry && (
|
|
277
|
+
<ActionButton icon={<RotateCcw size={11} />} onClick={() => onRetry(message.id)} title={t('common.retry')} />
|
|
278
|
+
)}
|
|
279
|
+
<ActionButton icon={<Download size={11} />} onClick={onExport} title={t('common.export')} />
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ─── Assistant Message ───────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
function AssistantMessage({
|
|
289
|
+
message,
|
|
290
|
+
onCopy,
|
|
291
|
+
copied,
|
|
292
|
+
showThinking,
|
|
293
|
+
onToggleThinking,
|
|
294
|
+
onExport,
|
|
295
|
+
hideModelHeader,
|
|
296
|
+
}: {
|
|
297
|
+
message: DisplayMessage
|
|
298
|
+
onCopy: () => void
|
|
299
|
+
copied: boolean
|
|
300
|
+
showThinking: boolean
|
|
301
|
+
onToggleThinking: () => void
|
|
302
|
+
onExport: () => void
|
|
303
|
+
hideModelHeader?: boolean
|
|
304
|
+
}): React.JSX.Element {
|
|
305
|
+
const { t } = useTranslation()
|
|
306
|
+
const customModels = useAppStore((state) => state.customModels)
|
|
307
|
+
const thinkingEnabled = useAppStore(
|
|
308
|
+
(state) => state.settingsDraft.showThinking ?? state.settings?.showThinking ?? DEFAULT_SETTINGS.showThinking
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
// With thinking off, a thinking-only turn (no text, no tool calls) would
|
|
312
|
+
// collapse to just an orphaned model/provider header. Suppress it entirely so
|
|
313
|
+
// the header doesn't appear to repeat. Only applies when thinking is hidden —
|
|
314
|
+
// with thinking on, the block itself gives the header something to sit above.
|
|
315
|
+
const hasVisibleBody = message.content.trim().length > 0 || (message.toolCalls?.length ?? 0) > 0
|
|
316
|
+
if (!thinkingEnabled && !hasVisibleBody) {
|
|
317
|
+
return <></>
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// A turn whose only body is tool calls (no prose) is a pure tool operation, so
|
|
321
|
+
// its avatar mirrors the operation (globe for a fetch, document for a read, …)
|
|
322
|
+
// instead of the Bot avatar / group spacer.
|
|
323
|
+
const ToolIcon =
|
|
324
|
+
message.content.trim().length === 0 && message.toolCalls && message.toolCalls.length > 0
|
|
325
|
+
? toolCallIconFor(message.toolCalls[0].name)
|
|
326
|
+
: null
|
|
327
|
+
|
|
328
|
+
// Show the provider · model header whenever Pi attributes the turn — including
|
|
329
|
+
// a standalone tool call, so you can see who ran it. It's suppressed only
|
|
330
|
+
// inside a group (hideModelHeader), where the group's one shared header stands
|
|
331
|
+
// in and repeating it per row would just be noise.
|
|
332
|
+
const showModelHeader = !!message.model && !hideModelHeader
|
|
333
|
+
|
|
334
|
+
// A grouped thinking-only turn (thinking, no text, no tool calls). It sits
|
|
335
|
+
// between the call/result rows of a serial run; with the default mb-4 rhythm it
|
|
336
|
+
// ends up 16px from its neighbours, which reads as too airy for a bare
|
|
337
|
+
// "Thinking" toggle. Tighten it to 8px on both sides: pull it up 8px against the
|
|
338
|
+
// previous row's mb-4 (-mt-2) and give it an 8px bottom (mb-2) instead of mb-4.
|
|
339
|
+
const isGroupedPureThinking =
|
|
340
|
+
hideModelHeader &&
|
|
341
|
+
!!message.thinking &&
|
|
342
|
+
thinkingEnabled &&
|
|
343
|
+
message.content.trim().length === 0 &&
|
|
344
|
+
(message.toolCalls?.length ?? 0) === 0
|
|
345
|
+
|
|
346
|
+
// A pure-tool turn (body is only tool calls): render every call on its own row
|
|
347
|
+
// behind its own operation icon, so each call — including parallel calls in a
|
|
348
|
+
// single turn — is labelled with the right glyph. Standalone (attributed) turns
|
|
349
|
+
// also show a Bot avatar + provider·model header; grouped turns omit the header
|
|
350
|
+
// (the group's shared header stands in) but keep the same per-row-icon layout so
|
|
351
|
+
// a single-call and a multi-call turn align identically.
|
|
352
|
+
if (ToolIcon) {
|
|
353
|
+
const showThinkingBlock = !!message.thinking && thinkingEnabled
|
|
354
|
+
const hasHeaderArea = showModelHeader || showThinkingBlock
|
|
355
|
+
// The collapsible thinking block, reused in the attributed (header) layout and
|
|
356
|
+
// the grouped (padded-block) layout below.
|
|
357
|
+
const thinkingBlock = showThinkingBlock ? (
|
|
358
|
+
<div className="thinking-hover">
|
|
359
|
+
<div className="flex h-7 items-center gap-1">
|
|
360
|
+
<button
|
|
361
|
+
onClick={onToggleThinking}
|
|
362
|
+
className="flex items-center gap-1 text-sm text-dim hover:text-muted transition-colors"
|
|
363
|
+
>
|
|
364
|
+
<Brain size={12} />
|
|
365
|
+
{showThinking ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
|
366
|
+
{t('common.thinking')}
|
|
367
|
+
</button>
|
|
368
|
+
<CopyButton text={message.thinking!} className="thinking-copy-btn" />
|
|
369
|
+
</div>
|
|
370
|
+
{showThinking && (
|
|
371
|
+
<div className="markdown-body min-w-0 font-sans italic text-sm text-muted break-words [overflow-wrap:anywhere]">
|
|
372
|
+
<MarkdownRenderer content={message.thinking!} />
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
) : null
|
|
377
|
+
return (
|
|
378
|
+
<div className="group mb-4 animate-fade-in">
|
|
379
|
+
{/* Attributed (standalone) turn: Bot avatar + provider·model header, with
|
|
380
|
+
the thinking block tucked under it. */}
|
|
381
|
+
{showModelHeader && (
|
|
382
|
+
<div className="flex items-start gap-3">
|
|
383
|
+
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-card">
|
|
384
|
+
<Bot size={14} className="text-muted" />
|
|
385
|
+
</div>
|
|
386
|
+
<div className="min-w-0 flex-1">
|
|
387
|
+
<div className="flex h-7 items-center gap-2 text-sm text-dim">
|
|
388
|
+
<span>{message.provider}</span>
|
|
389
|
+
<span className="text-ghost">·</span>
|
|
390
|
+
<span>{modelDisplayName(message.model!, customModels)}</span>
|
|
391
|
+
{message.cost !== undefined && (
|
|
392
|
+
<>
|
|
393
|
+
<span className="text-ghost">·</span>
|
|
394
|
+
<span>${message.cost.toFixed(4)}</span>
|
|
395
|
+
</>
|
|
396
|
+
)}
|
|
397
|
+
<span className="text-ghost">·</span>
|
|
398
|
+
<RelativeTime timestamp={message.timestamp} />
|
|
399
|
+
</div>
|
|
400
|
+
{thinkingBlock && <div className="mt-2">{thinkingBlock}</div>}
|
|
401
|
+
</div>
|
|
402
|
+
</div>
|
|
403
|
+
)}
|
|
404
|
+
{/* Grouped turn: no header (the group's shared header stands in). Render the
|
|
405
|
+
thinking block as a plain padded block — pl-10 aligns it with the tool
|
|
406
|
+
badges (w-7 icon + gap-3), and controlling its own margins directly
|
|
407
|
+
(-mt-2 above vs the group's mt-4, the tool row's mt-2 below) keeps it
|
|
408
|
+
evenly 8px on both sides whether collapsed or expanded. Nesting it in the
|
|
409
|
+
icon-row flex (as the tool rows do) let the 28px icon spacer floor the
|
|
410
|
+
row height, so the gap below collapsed once the expanded text outgrew it. */}
|
|
411
|
+
{!showModelHeader && thinkingBlock && (
|
|
412
|
+
<div className="-mt-2 pl-10">{thinkingBlock}</div>
|
|
413
|
+
)}
|
|
414
|
+
{/* One row per tool-call box, each behind its own operation icon. mt-2 sets
|
|
415
|
+
the gap under the header/thinking area; space-y-4 keeps the box-to-box gap
|
|
416
|
+
equal to the mb-4 rhythm between separate call/result rows. */}
|
|
417
|
+
<div className={clsx('space-y-4', hasHeaderArea && 'mt-2')}>
|
|
418
|
+
{message.toolCalls!.map((tc) => {
|
|
419
|
+
const RowIcon = toolCallIconFor(tc.name)
|
|
420
|
+
return (
|
|
421
|
+
<div key={tc.id} className="flex items-start gap-3">
|
|
422
|
+
{/* Center the icon on the box's header row (h-8 ≈ py-2 + text-xs). */}
|
|
423
|
+
<div className="flex h-8 w-7 shrink-0 items-center justify-center">
|
|
424
|
+
<div className="flex h-7 w-7 items-center justify-center rounded-full bg-card">
|
|
425
|
+
<RowIcon size={14} className="text-dim" />
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
<div className="min-w-0 flex-1">
|
|
429
|
+
<ToolCallBadge toolCall={tc} />
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
)
|
|
433
|
+
})}
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return (
|
|
440
|
+
<div className={clsx('group animate-fade-in', isGroupedPureThinking ? '-mt-2 mb-2' : 'mb-4')}>
|
|
441
|
+
<div className="flex items-start gap-3">
|
|
442
|
+
{/* Avatar — the Bot avatar for a prose turn, except inside a tool group
|
|
443
|
+
(the group shows one shared header above) where it keeps an empty
|
|
444
|
+
spacer so its body stays aligned with the icon-avatared rows.
|
|
445
|
+
Pure-tool turns are handled earlier (each call gets its own icon). */}
|
|
446
|
+
{hideModelHeader ? (
|
|
447
|
+
<div className="h-7 w-7 shrink-0" />
|
|
448
|
+
) : (
|
|
449
|
+
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-card">
|
|
450
|
+
<Bot size={14} className="text-muted" />
|
|
451
|
+
</div>
|
|
452
|
+
)}
|
|
453
|
+
|
|
454
|
+
{/* Content */}
|
|
455
|
+
<div className="min-w-0 flex-1">
|
|
456
|
+
{/* Model info */}
|
|
457
|
+
{showModelHeader && (
|
|
458
|
+
<div className="flex h-7 items-center gap-2 text-sm text-dim">
|
|
459
|
+
<span>{message.provider}</span>
|
|
460
|
+
<span className="text-ghost">·</span>
|
|
461
|
+
<span>{modelDisplayName(message.model!, customModels)}</span>
|
|
462
|
+
{message.cost !== undefined && (
|
|
463
|
+
<>
|
|
464
|
+
<span className="text-ghost">·</span>
|
|
465
|
+
<span>${message.cost.toFixed(4)}</span>
|
|
466
|
+
</>
|
|
467
|
+
)}
|
|
468
|
+
<span className="text-ghost">·</span>
|
|
469
|
+
<RelativeTime timestamp={message.timestamp} />
|
|
470
|
+
</div>
|
|
471
|
+
)}
|
|
472
|
+
|
|
473
|
+
{/* Thinking block — gated by the Show Thinking setting. mb-2 only when a
|
|
474
|
+
body (text/tool calls) follows it; on a pure-thinking turn there's
|
|
475
|
+
nothing below, so the mb-2 would stack on the message's own mb-4 and
|
|
476
|
+
leave the block with a bigger gap below than above. */}
|
|
477
|
+
{message.thinking && thinkingEnabled && (
|
|
478
|
+
<div
|
|
479
|
+
className={clsx(
|
|
480
|
+
'thinking-hover',
|
|
481
|
+
(message.content.trim().length > 0 || (message.toolCalls?.length ?? 0) > 0) && 'mb-2'
|
|
482
|
+
)}
|
|
483
|
+
>
|
|
484
|
+
<div className="flex h-7 items-center gap-1">
|
|
485
|
+
<button
|
|
486
|
+
onClick={onToggleThinking}
|
|
487
|
+
className="flex items-center gap-1 text-sm text-dim hover:text-muted transition-colors"
|
|
488
|
+
>
|
|
489
|
+
<Brain size={12} />
|
|
490
|
+
{showThinking ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
|
491
|
+
{t('common.thinking')}
|
|
492
|
+
</button>
|
|
493
|
+
<CopyButton text={message.thinking} className="thinking-copy-btn" />
|
|
494
|
+
</div>
|
|
495
|
+
{showThinking && (
|
|
496
|
+
<div className="markdown-body min-w-0 font-sans italic text-sm text-muted break-words [overflow-wrap:anywhere]">
|
|
497
|
+
<MarkdownRenderer content={message.thinking} />
|
|
498
|
+
</div>
|
|
499
|
+
)}
|
|
500
|
+
</div>
|
|
501
|
+
)}
|
|
502
|
+
|
|
503
|
+
{/* Text content */}
|
|
504
|
+
{message.content.trim() && (
|
|
505
|
+
<div
|
|
506
|
+
data-scroll-anchor={message.id}
|
|
507
|
+
className={clsx(
|
|
508
|
+
'markdown-body text-sm',
|
|
509
|
+
// Sit just below the model/provider header — 6px, 2px tighter than
|
|
510
|
+
// the tool-call box's 8px top gap.
|
|
511
|
+
(message.model || message.thinking) && 'mt-1.5'
|
|
512
|
+
)}>
|
|
513
|
+
<MarkdownRenderer content={message.content} />
|
|
514
|
+
</div>
|
|
515
|
+
)}
|
|
516
|
+
|
|
517
|
+
{/* Actions — copy/export the response text. Rendered directly beneath
|
|
518
|
+
the text (before any tool calls) so on a turn that mixes prose with
|
|
519
|
+
tool calls the buttons stay attached to the prose instead of landing
|
|
520
|
+
under the tool-call box and splitting a run of grouped tool badges.
|
|
521
|
+
Only shown when there's real response text — a tool-only message's
|
|
522
|
+
content is just whitespace/newlines, which is truthy but has nothing
|
|
523
|
+
to copy/export. Shown on hover only, matching the user-message actions. */}
|
|
524
|
+
{message.content.trim() && (
|
|
525
|
+
<div className="mt-2 flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
526
|
+
<ActionButton
|
|
527
|
+
icon={copied ? <Check size={11} /> : <Copy size={11} />}
|
|
528
|
+
onClick={onCopy}
|
|
529
|
+
title={copied ? t('common.copied') : t('common.copy')}
|
|
530
|
+
label={copied ? t('common.copied') : t('common.copy')}
|
|
531
|
+
/>
|
|
532
|
+
<ActionButton icon={<Download size={11} />} onClick={onExport} title={t('common.export')} />
|
|
533
|
+
</div>
|
|
534
|
+
)}
|
|
535
|
+
|
|
536
|
+
{/* Tool calls */}
|
|
537
|
+
{message.toolCalls && message.toolCalls.length > 0 && (
|
|
538
|
+
<div className={clsx(
|
|
539
|
+
// space-y-4 so parallel calls in one turn (multiple badges here) keep
|
|
540
|
+
// the same 16px rhythm as separate call/result rows (each mb-4),
|
|
541
|
+
// rather than bunching up ~4px apart.
|
|
542
|
+
'space-y-4',
|
|
543
|
+
// Pad the top only when something actually renders above the tool
|
|
544
|
+
// box — a visible model header, a thinking block, or response text.
|
|
545
|
+
// A grouped tool row has no header (showModelHeader is false), so it
|
|
546
|
+
// top-aligns with its icon avatar; that also keeps the gap between
|
|
547
|
+
// grouped call/result pairs equal to the gap within a pair (no stray
|
|
548
|
+
// 8px from an mt-2 sitting under a suppressed header).
|
|
549
|
+
(showModelHeader ||
|
|
550
|
+
(message.thinking && thinkingEnabled) ||
|
|
551
|
+
message.content.trim().length > 0) && 'mt-2'
|
|
552
|
+
)}>
|
|
553
|
+
{message.toolCalls.map((tc) => (
|
|
554
|
+
<ToolCallBadge key={tc.id} toolCall={tc} />
|
|
555
|
+
))}
|
|
556
|
+
</div>
|
|
557
|
+
)}
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
</div>
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// ─── Tool Call Badge ─────────────────────────────────────────────────────────
|
|
565
|
+
|
|
566
|
+
function ToolCallBadge({
|
|
567
|
+
toolCall,
|
|
568
|
+
}: {
|
|
569
|
+
toolCall: NonNullable<DisplayMessage['toolCalls']>[number]
|
|
570
|
+
}): React.JSX.Element {
|
|
571
|
+
const { t } = useTranslation()
|
|
572
|
+
// Edit diffs open expanded so the change is visible without a second result pill.
|
|
573
|
+
const edits = toolKind(toolCall.name) === 'edit' ? parseEdits(toolCall.arguments) : null
|
|
574
|
+
const stats = edits ? editStats(edits) : null
|
|
575
|
+
const editFile = edits ? toolCallFile(toolCall.name, toolCall.arguments) : null
|
|
576
|
+
const editLang = editFile ? getCodeEditorLanguageName(editFile) : 'plain text'
|
|
577
|
+
const [expanded, setExpanded] = useState(() => Boolean(edits))
|
|
578
|
+
|
|
579
|
+
const status = toolCall.isExecuting
|
|
580
|
+
? 'running'
|
|
581
|
+
: toolCall.isError === true
|
|
582
|
+
? 'error'
|
|
583
|
+
: toolCall.isError === false || toolCall.result !== undefined
|
|
584
|
+
? 'done'
|
|
585
|
+
: null
|
|
586
|
+
|
|
587
|
+
return (
|
|
588
|
+
<div className="relative min-w-0 overflow-hidden rounded-lg border border-border bg-surface/50">
|
|
589
|
+
<CopyButton text={toolCallCopyText(toolCall)} className="absolute right-1.5 top-1.5 z-10" />
|
|
590
|
+
<button
|
|
591
|
+
type="button"
|
|
592
|
+
onClick={() => setExpanded(!expanded)}
|
|
593
|
+
className="flex w-full min-w-0 items-center gap-2 py-2 pl-3 pr-9 text-xs text-muted hover:text-secondary transition-colors"
|
|
594
|
+
>
|
|
595
|
+
<span className="font-jetbrains min-w-0 truncate">
|
|
596
|
+
{toolCallLabel(toolCall.name, toolCall.arguments, t)}
|
|
597
|
+
</span>
|
|
598
|
+
{stats && (
|
|
599
|
+
<span className="shrink-0 font-jetbrains">
|
|
600
|
+
<span className="text-success">+{stats.added}</span>{' '}
|
|
601
|
+
<span className="text-error">-{stats.removed}</span>
|
|
602
|
+
</span>
|
|
603
|
+
)}
|
|
604
|
+
{toolCall.durationMs !== undefined && !toolCall.isExecuting && (
|
|
605
|
+
<span className="shrink-0 text-faint">{formatDuration(toolCall.durationMs)}</span>
|
|
606
|
+
)}
|
|
607
|
+
{status && (
|
|
608
|
+
<span
|
|
609
|
+
className={clsx(
|
|
610
|
+
'shrink-0 capitalize',
|
|
611
|
+
status === 'running' && 'text-warning animate-pulse',
|
|
612
|
+
status === 'error' && 'text-error',
|
|
613
|
+
status === 'done' && 'text-success'
|
|
614
|
+
)}
|
|
615
|
+
>
|
|
616
|
+
{toolCallStatusLabel(status, t)}
|
|
617
|
+
</span>
|
|
618
|
+
)}
|
|
619
|
+
{expanded ? (
|
|
620
|
+
<ChevronDown size={12} className="ml-auto shrink-0" />
|
|
621
|
+
) : (
|
|
622
|
+
<ChevronRight size={12} className="ml-auto shrink-0" />
|
|
623
|
+
)}
|
|
624
|
+
</button>
|
|
625
|
+
{expanded && (
|
|
626
|
+
<div className="border-t border-border px-3 py-2">
|
|
627
|
+
{edits ? (
|
|
628
|
+
<EditDiff blocks={edits} lang={editLang} />
|
|
629
|
+
) : (
|
|
630
|
+
<pre className="font-jetbrains overflow-x-auto text-xs text-dim">
|
|
631
|
+
{formatToolCallArgs(toolCall.arguments)}
|
|
632
|
+
</pre>
|
|
633
|
+
)}
|
|
634
|
+
{toolCall.result && (
|
|
635
|
+
<div className="mt-2 border-t border-border pt-2">
|
|
636
|
+
<pre className="font-jetbrains overflow-x-auto text-xs text-muted">
|
|
637
|
+
{toolCall.result}
|
|
638
|
+
</pre>
|
|
639
|
+
</div>
|
|
640
|
+
)}
|
|
641
|
+
</div>
|
|
642
|
+
)}
|
|
643
|
+
</div>
|
|
644
|
+
)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Renders an edit call's blocks as a diff: each block shows its old lines removed
|
|
648
|
+
// (red) then its new lines added (green), with the code syntax-highlighted. A
|
|
649
|
+
// block replacement, not a line-level diff — enough to read the change at a glance.
|
|
650
|
+
function EditDiff({ blocks, lang }: { blocks: EditBlock[]; lang: string }): React.JSX.Element {
|
|
651
|
+
return (
|
|
652
|
+
<div className="overflow-x-auto font-jetbrains text-xs leading-relaxed text-secondary">
|
|
653
|
+
{blocks.map((b, i) => (
|
|
654
|
+
<div key={i} className={i > 0 ? 'mt-2 border-t border-border pt-2' : ''}>
|
|
655
|
+
<DiffLines text={b.oldText} lang={lang} kind="remove" />
|
|
656
|
+
<DiffLines text={b.newText} lang={lang} kind="add" />
|
|
657
|
+
</div>
|
|
658
|
+
))}
|
|
659
|
+
</div>
|
|
660
|
+
)
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// One side of a diff block (all removed, or all added), syntax-highlighted.
|
|
664
|
+
// highlightCodeToHtml emits newlines separately, so its output splits cleanly
|
|
665
|
+
// into per-line HTML; falls back to plain text when no parser matches.
|
|
666
|
+
function DiffLines({
|
|
667
|
+
text,
|
|
668
|
+
lang,
|
|
669
|
+
kind,
|
|
670
|
+
}: {
|
|
671
|
+
text: string
|
|
672
|
+
lang: string
|
|
673
|
+
kind: 'add' | 'remove'
|
|
674
|
+
}): React.JSX.Element | null {
|
|
675
|
+
if (text === '') return null
|
|
676
|
+
const html = highlightCodeToHtml(text, lang)
|
|
677
|
+
const lines = (html ?? text).split('\n')
|
|
678
|
+
const rowBg = kind === 'add' ? 'bg-success-bg' : 'bg-error-bg'
|
|
679
|
+
const markColor = kind === 'add' ? 'text-success' : 'text-error'
|
|
680
|
+
const sign = kind === 'add' ? '+ ' : '- '
|
|
681
|
+
return (
|
|
682
|
+
<>
|
|
683
|
+
{lines.map((line, j) => (
|
|
684
|
+
<div key={j} className={clsx('whitespace-pre', rowBg)}>
|
|
685
|
+
<span className={clsx('select-none', markColor)}>{sign}</span>
|
|
686
|
+
{html !== null ? (
|
|
687
|
+
<span dangerouslySetInnerHTML={{ __html: line || ' ' }} />
|
|
688
|
+
) : (
|
|
689
|
+
<span>{line || ' '}</span>
|
|
690
|
+
)}
|
|
691
|
+
</div>
|
|
692
|
+
))}
|
|
693
|
+
</>
|
|
694
|
+
)
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// ─── Tool Result Message ─────────────────────────────────────────────────────
|
|
698
|
+
|
|
699
|
+
function ToolResultMessage({ message }: { message: DisplayMessage }): React.JSX.Element {
|
|
700
|
+
const { t } = useTranslation()
|
|
701
|
+
// Collapsed by default — tool results can be huge and otherwise dominate the
|
|
702
|
+
// scrollback. Mirrors ToolCallBadge's expand/collapse affordance.
|
|
703
|
+
const [expanded, setExpanded] = useState(false)
|
|
704
|
+
const newlineIdx = message.content.indexOf('\n')
|
|
705
|
+
const firstLine = newlineIdx === -1 ? message.content : message.content.slice(0, newlineIdx)
|
|
706
|
+
// Everything after the first line; that line already shows in the header row.
|
|
707
|
+
const rest = newlineIdx === -1 ? '' : message.content.slice(newlineIdx + 1)
|
|
708
|
+
// Nothing beyond the first line → nothing to expand, so no chevron.
|
|
709
|
+
const expandable = rest.trim() !== ''
|
|
710
|
+
|
|
711
|
+
// Only a file-read result is the file's content, so only it renders as
|
|
712
|
+
// line-numbered, syntax-highlighted code. Everything else stays plain text —
|
|
713
|
+
// notably writes/creates, whose result is a "wrote N bytes" success line (not
|
|
714
|
+
// the file), plus CSV, command output, and fetches.
|
|
715
|
+
const codeLang =
|
|
716
|
+
message.toolName && toolKind(message.toolName) === 'read' && message.toolFile
|
|
717
|
+
? getCodeEditorLanguageName(message.toolFile)
|
|
718
|
+
: 'plain text'
|
|
719
|
+
const isCode = codeLang !== 'plain text'
|
|
720
|
+
|
|
721
|
+
return (
|
|
722
|
+
<div className="mb-4 animate-fade-in">
|
|
723
|
+
<div className="flex items-start gap-3">
|
|
724
|
+
{/* No result icon — an empty spacer (same width as a tool-call avatar)
|
|
725
|
+
keeps the result box left-aligned with the tool-call box above it. */}
|
|
726
|
+
<div className="w-7 shrink-0" />
|
|
727
|
+
<div className="min-w-0 flex-1">
|
|
728
|
+
<div className="relative rounded-lg border border-border bg-surface/50">
|
|
729
|
+
<CopyButton text={message.content} className="absolute right-1.5 top-1.5" />
|
|
730
|
+
{!expandable ? (
|
|
731
|
+
// Single line of content — nothing to expand, so no chevron.
|
|
732
|
+
<div className="flex w-full items-center py-2 pl-3 pr-9 text-xs text-muted">
|
|
733
|
+
<span className="font-jetbrains min-w-0 flex-1 truncate text-left">{firstLine}</span>
|
|
734
|
+
</div>
|
|
735
|
+
) : expanded && isCode ? (
|
|
736
|
+
// Code view: no header row (its first line repeats as line 1 of the
|
|
737
|
+
// body). Float the collapse control top-right, beside copy, so the
|
|
738
|
+
// code sits flush at the top.
|
|
739
|
+
<button
|
|
740
|
+
onClick={() => setExpanded(false)}
|
|
741
|
+
className="absolute right-8 top-1.5 rounded p-1 text-dim transition-colors hover:text-secondary"
|
|
742
|
+
title={t('common.collapse')}
|
|
743
|
+
aria-label={t('common.collapse')}
|
|
744
|
+
>
|
|
745
|
+
<ChevronDown size={12} />
|
|
746
|
+
</button>
|
|
747
|
+
) : (
|
|
748
|
+
<button
|
|
749
|
+
onClick={() => setExpanded(!expanded)}
|
|
750
|
+
className="flex w-full items-center gap-2 py-2 pl-3 pr-9 text-xs text-muted hover:text-secondary transition-colors"
|
|
751
|
+
>
|
|
752
|
+
<span className="font-jetbrains min-w-0 flex-1 truncate text-left">{firstLine}</span>
|
|
753
|
+
{expanded ? (
|
|
754
|
+
<ChevronDown size={12} className="shrink-0" />
|
|
755
|
+
) : (
|
|
756
|
+
<ChevronRight size={12} className="shrink-0" />
|
|
757
|
+
)}
|
|
758
|
+
</button>
|
|
759
|
+
)}
|
|
760
|
+
{expandable &&
|
|
761
|
+
expanded &&
|
|
762
|
+
(isCode ? (
|
|
763
|
+
<div className="px-3 py-2">
|
|
764
|
+
<CodeResultView
|
|
765
|
+
content={message.content}
|
|
766
|
+
lang={codeLang}
|
|
767
|
+
onCollapse={() => setExpanded(false)}
|
|
768
|
+
/>
|
|
769
|
+
</div>
|
|
770
|
+
) : (
|
|
771
|
+
rest.trim() && (
|
|
772
|
+
<div className="px-3 pb-2">
|
|
773
|
+
<pre className="font-jetbrains overflow-x-auto text-xs text-muted">
|
|
774
|
+
{rest.slice(0, 2000)}
|
|
775
|
+
{rest.length > 2000 && '\n…'}
|
|
776
|
+
</pre>
|
|
777
|
+
</div>
|
|
778
|
+
)
|
|
779
|
+
))}
|
|
780
|
+
</div>
|
|
781
|
+
</div>
|
|
782
|
+
</div>
|
|
783
|
+
</div>
|
|
784
|
+
)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Guard against re-parsing a whole huge file for highlighting; the source is
|
|
788
|
+
// already truncated by Pi, this is just a ceiling.
|
|
789
|
+
const MAX_CODE_RESULT_CHARS = 20000
|
|
790
|
+
|
|
791
|
+
// Renders file content as line-numbered, syntax-highlighted code. highlightCodeToHtml
|
|
792
|
+
// emits newlines separately from its <span> runs, so splitting on '\n' yields
|
|
793
|
+
// self-contained per-line HTML; when no parser exists it falls back to plain text.
|
|
794
|
+
function CodeResultView({
|
|
795
|
+
content,
|
|
796
|
+
lang,
|
|
797
|
+
onCollapse,
|
|
798
|
+
}: {
|
|
799
|
+
content: string
|
|
800
|
+
lang: string
|
|
801
|
+
onCollapse: () => void
|
|
802
|
+
}): React.JSX.Element {
|
|
803
|
+
const clipped = content.length > MAX_CODE_RESULT_CHARS
|
|
804
|
+
const clippedContent = clipped ? content.slice(0, MAX_CODE_RESULT_CHARS) : content
|
|
805
|
+
// Peel off Pi's "[N more lines in file…]" footer so it renders as a note, not code.
|
|
806
|
+
const { code, note } = splitReadTruncationNote(clippedContent)
|
|
807
|
+
|
|
808
|
+
return (
|
|
809
|
+
// The first line doubles as a collapse trigger (a large click target, like the
|
|
810
|
+
// collapsed header). A drag to select text isn't a click, so selection works.
|
|
811
|
+
<div className="overflow-x-auto font-jetbrains text-xs leading-relaxed text-secondary">
|
|
812
|
+
<LineNumberedCode content={code} lang={lang} onFirstLineClick={onCollapse} />
|
|
813
|
+
{note && <div className="mt-2 italic text-dim">{note}</div>}
|
|
814
|
+
{clipped && <div className="mt-1 text-faint">…</div>}
|
|
815
|
+
</div>
|
|
816
|
+
)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// ─── Tool Group ──────────────────────────────────────────────────────────────
|
|
820
|
+
|
|
821
|
+
// A run of consecutive tool-activity messages (tool calls + their results, plus
|
|
822
|
+
// any thinking-only turns among them) folded into one collapsed block. Collapsed
|
|
823
|
+
// by default to keep repetitive runs from dominating the scrollback; expanding
|
|
824
|
+
// reveals the original messages rendered exactly as they would appear ungrouped.
|
|
825
|
+
function ToolGroupBubbleImpl({
|
|
826
|
+
title,
|
|
827
|
+
messages,
|
|
828
|
+
onRetry,
|
|
829
|
+
}: {
|
|
830
|
+
title: string
|
|
831
|
+
messages: DisplayMessage[]
|
|
832
|
+
onRetry?: (messageId: string) => void
|
|
833
|
+
}): React.JSX.Element {
|
|
834
|
+
const [expanded, setExpanded] = useState(false)
|
|
835
|
+
const customModels = useAppStore((state) => state.customModels)
|
|
836
|
+
|
|
837
|
+
// If every assistant turn in the group used the same model, show one shared
|
|
838
|
+
// provider · model header above the body and suppress the per-turn headers. If
|
|
839
|
+
// they differ (a mid-run model switch), keep each turn's own header so the
|
|
840
|
+
// distinction isn't lost.
|
|
841
|
+
const modelKeys = new Set<string>()
|
|
842
|
+
let sharedModel: string | undefined
|
|
843
|
+
let sharedProvider: string | undefined
|
|
844
|
+
for (const m of messages) {
|
|
845
|
+
if (m.role === 'assistant' && m.model) {
|
|
846
|
+
modelKeys.add(`${m.provider ?? ''}|${m.model}`)
|
|
847
|
+
if (sharedModel === undefined) {
|
|
848
|
+
sharedModel = m.model
|
|
849
|
+
sharedProvider = m.provider
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const showSharedHeader = modelKeys.size === 1 && sharedModel !== undefined
|
|
854
|
+
const groupTimestamp = messages[messages.length - 1]?.timestamp
|
|
855
|
+
|
|
856
|
+
return (
|
|
857
|
+
<div className="group mb-4 animate-fade-in">
|
|
858
|
+
<div className="flex items-start gap-3">
|
|
859
|
+
{/* Bot avatar + model header so a grouped run reads like any other
|
|
860
|
+
assistant message, not a distinct kind of block. */}
|
|
861
|
+
<div className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-card">
|
|
862
|
+
<Bot size={14} className="text-muted" />
|
|
863
|
+
</div>
|
|
864
|
+
<div className="min-w-0 flex-1">
|
|
865
|
+
{showSharedHeader && (
|
|
866
|
+
<div className="flex h-7 items-center gap-2 text-sm text-dim">
|
|
867
|
+
<span>{sharedProvider}</span>
|
|
868
|
+
<span className="text-ghost">·</span>
|
|
869
|
+
<span>{modelDisplayName(sharedModel as string, customModels)}</span>
|
|
870
|
+
{groupTimestamp !== undefined && (
|
|
871
|
+
<>
|
|
872
|
+
<span className="text-ghost">·</span>
|
|
873
|
+
<RelativeTime timestamp={groupTimestamp} />
|
|
874
|
+
</>
|
|
875
|
+
)}
|
|
876
|
+
</div>
|
|
877
|
+
)}
|
|
878
|
+
<div
|
|
879
|
+
className={clsx(
|
|
880
|
+
'relative rounded-lg border border-border bg-surface/50',
|
|
881
|
+
showSharedHeader && 'mt-1.5'
|
|
882
|
+
)}
|
|
883
|
+
>
|
|
884
|
+
<CopyButton text={groupCopyText(messages)} className="absolute right-1.5 top-1.5" />
|
|
885
|
+
<button
|
|
886
|
+
onClick={() => setExpanded(!expanded)}
|
|
887
|
+
className="flex w-full items-center gap-2 py-2 pl-3 pr-9 text-xs text-muted hover:text-secondary transition-colors"
|
|
888
|
+
>
|
|
889
|
+
<span className="font-jetbrains">{title}</span>
|
|
890
|
+
{expanded ? (
|
|
891
|
+
<ChevronDown size={12} className="ml-auto shrink-0" />
|
|
892
|
+
) : (
|
|
893
|
+
<ChevronRight size={12} className="ml-auto shrink-0" />
|
|
894
|
+
)}
|
|
895
|
+
</button>
|
|
896
|
+
</div>
|
|
897
|
+
{/* Expanded body: the original messages, slightly indented to signal
|
|
898
|
+
they belong to the group. Per-turn model headers are suppressed since
|
|
899
|
+
the group already shows one above. mt-4 matches the mb-4 gap between
|
|
900
|
+
the rows inside, so the header→first-row gap equals the row-to-row
|
|
901
|
+
gap. Last child's bottom margin trimmed so it doesn't double up on
|
|
902
|
+
the group's own mb-4. */}
|
|
903
|
+
{expanded && (
|
|
904
|
+
<div className="mt-4 pl-3 [&>*:last-child]:mb-0">
|
|
905
|
+
{messages.map((m) => (
|
|
906
|
+
<MessageBubble
|
|
907
|
+
key={m.id}
|
|
908
|
+
message={m}
|
|
909
|
+
onRetry={onRetry}
|
|
910
|
+
hideModelHeader={showSharedHeader}
|
|
911
|
+
/>
|
|
912
|
+
))}
|
|
913
|
+
</div>
|
|
914
|
+
)}
|
|
915
|
+
</div>
|
|
916
|
+
</div>
|
|
917
|
+
</div>
|
|
918
|
+
)
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
export const ToolGroupBubble = memo(ToolGroupBubbleImpl)
|
|
922
|
+
|
|
923
|
+
// ─── System Message ──────────────────────────────────────────────────────────
|
|
924
|
+
|
|
925
|
+
function SystemMessage({ message }: { message: DisplayMessage }): React.JSX.Element {
|
|
926
|
+
return (
|
|
927
|
+
<div className="mb-4 flex justify-center animate-fade-in">
|
|
928
|
+
<div className="rounded-full bg-surface px-3 py-1 text-xs text-dim">
|
|
929
|
+
{message.content}
|
|
930
|
+
</div>
|
|
931
|
+
</div>
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// ─── Action Button ───────────────────────────────────────────────────────────
|
|
936
|
+
|
|
937
|
+
function ActionButton({
|
|
938
|
+
icon,
|
|
939
|
+
onClick,
|
|
940
|
+
title,
|
|
941
|
+
label,
|
|
942
|
+
}: {
|
|
943
|
+
icon: React.ReactNode
|
|
944
|
+
onClick: () => void
|
|
945
|
+
title: string
|
|
946
|
+
label?: string
|
|
947
|
+
}): React.JSX.Element {
|
|
948
|
+
return (
|
|
949
|
+
<button
|
|
950
|
+
onClick={onClick}
|
|
951
|
+
className="flex items-center gap-1 rounded px-1.5 py-1 text-xs text-dim hover:bg-surface-hover hover:text-secondary transition-colors"
|
|
952
|
+
title={title}
|
|
953
|
+
aria-label={title}
|
|
954
|
+
>
|
|
955
|
+
{icon}
|
|
956
|
+
{label && <span>{label}</span>}
|
|
957
|
+
</button>
|
|
958
|
+
)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
962
|
+
|
|
963
|
+
function formatDuration(ms: number): string {
|
|
964
|
+
if (ms < 1000) return `${ms}ms`
|
|
965
|
+
return `${(ms / 1000).toFixed(ms < 10000 ? 1 : 0)}s`
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function formatToolCallArgs(args: string): string {
|
|
969
|
+
try {
|
|
970
|
+
const parsed = JSON.parse(args)
|
|
971
|
+
return JSON.stringify(parsed, null, 2)
|
|
972
|
+
} catch {
|
|
973
|
+
return args
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// The group's copy button yields every call and result in order: each tool
|
|
978
|
+
// call's copy text (raw command / formatted args) followed by its result.
|
|
979
|
+
function groupCopyText(messages: DisplayMessage[]): string {
|
|
980
|
+
const parts: string[] = []
|
|
981
|
+
for (const m of messages) {
|
|
982
|
+
if (m.role === 'assistant') {
|
|
983
|
+
for (const tc of m.toolCalls ?? []) parts.push(toolCallCopyText(tc))
|
|
984
|
+
} else if (m.role === 'toolResult') {
|
|
985
|
+
parts.push(m.content)
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return parts.join('\n\n')
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// What the copy button on a tool-call box yields: the raw command for
|
|
992
|
+
// shell-style tools (so it pastes cleanly), otherwise the formatted arguments.
|
|
993
|
+
function toolCallCopyText(toolCall: NonNullable<DisplayMessage['toolCalls']>[number]): string {
|
|
994
|
+
try {
|
|
995
|
+
const parsed = JSON.parse(toolCall.arguments)
|
|
996
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
997
|
+
for (const key of ['command', 'cmd', 'script']) {
|
|
998
|
+
const value = (parsed as Record<string, unknown>)[key]
|
|
999
|
+
if (typeof value === 'string' && value.length > 0) return value
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
} catch {
|
|
1003
|
+
// fall through to formatted args
|
|
1004
|
+
}
|
|
1005
|
+
return formatToolCallArgs(toolCall.arguments)
|
|
1006
|
+
}
|