synabun 2.1.0 → 2.2.1
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 +139 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +504 -39
- package/neural-interface/i18n/pt-BR.json +504 -39
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/codex.js +6 -1
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +211 -50
- package/neural-interface/lib/assistant-envelope.js +153 -1
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +40 -7
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +423 -23
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +165 -22
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +80 -6
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +15 -3
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
- package/neural-interface/public/shared/assistant/asst-render.js +112 -18
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
- package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +1 -0
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +21 -4
- package/neural-interface/public/shared/loading-teardown.js +34 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/storage-retry.js +47 -0
- package/neural-interface/public/shared/storage.js +25 -4
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +362 -35
- package/neural-interface/public/shared/synabun-mascot.js +27 -8
- package/neural-interface/public/shared/synabun-ticker.js +147 -12
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +374 -134
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-loading.js +5 -4
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +731 -466
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -90,6 +90,7 @@ export const OPEN_ENTRIES = [
|
|
|
90
90
|
{ id: 'open-claude-panel', key: 'claudePanel', action: 'toggle-claude-panel', active: 'topright-claude-panel-btn', appIcon: 'menu-terminal-claude', icon: 'panel' },
|
|
91
91
|
{ id: 'open-codex-panel', key: 'codexPanel', press: 'topright-codex-panel-btn', active: 'topright-codex-panel-btn', appIcon: 'menu-terminal-codex', icon: 'panel' },
|
|
92
92
|
{ id: 'open-opencode-panel', key: 'opencodePanel', press: 'topright-opencode-panel-btn', active: 'topright-opencode-panel-btn', appIcon: 'menu-terminal-opencode', icon: 'panel' },
|
|
93
|
+
{ id: 'open-antigravity-panel', key: 'antigravityPanel', press: 'topright-antigravity-panel-btn', active: 'topright-antigravity-panel-btn', appIcon: 'menu-terminal-antigravity', icon: 'panel' },
|
|
93
94
|
null,
|
|
94
95
|
{ id: 'open-terminal', key: 'terminal', event: ['terminal:open', { profile: 'shell' }], perm: 'terminal', appIcon: 'menu-terminal-shell', icon: 'terminal' },
|
|
95
96
|
{ id: 'open-claude-terminal', key: 'claudeTerminal', action: 'launch-claude', perm: 'terminal', icon: 'terminal' },
|
|
@@ -16,6 +16,16 @@ import {
|
|
|
16
16
|
const ICON_CHECK = '<svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 8.5l3.5 3.5 6.5-7"/></svg>';
|
|
17
17
|
const ICON_X = '<svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>';
|
|
18
18
|
|
|
19
|
+
// A waiting card's spinning border (.cp-spin-ring in ui-claude-panel.js): a box the compositor turns
|
|
20
|
+
// under the ring's mask while the card is .active-perm. The card's first child.
|
|
21
|
+
function spinRing() {
|
|
22
|
+
const ring = document.createElement('span');
|
|
23
|
+
ring.className = 'cp-spin-ring';
|
|
24
|
+
ring.setAttribute('aria-hidden', 'true');
|
|
25
|
+
ring.appendChild(document.createElement('i'));
|
|
26
|
+
return ring;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
// Modes, rule suggestions and the prompt's context are decided in
|
|
20
30
|
// cp-permission-model.js (DOM-free, tested); this file renders them.
|
|
21
31
|
export const PERMISSION_MODES = ALL_PERMISSION_MODES;
|
|
@@ -100,6 +110,7 @@ export function renderPermissionCard(tab, requestId, req, hooks) {
|
|
|
100
110
|
const card = document.createElement('div');
|
|
101
111
|
card.className = 'perm-card active-perm cp-perm-card';
|
|
102
112
|
card.dataset.requestId = requestId;
|
|
113
|
+
card.appendChild(spinRing());
|
|
103
114
|
if (ctx.toolUseId) card.dataset.toolUseId = ctx.toolUseId;
|
|
104
115
|
|
|
105
116
|
const hdr = document.createElement('div');
|
|
@@ -283,6 +294,7 @@ export function renderElicitationCard(tab, requestId, req, hooks) {
|
|
|
283
294
|
const card = document.createElement('div');
|
|
284
295
|
card.className = 'perm-card active-perm cp-perm-card cp-elicit-card';
|
|
285
296
|
card.dataset.requestId = requestId;
|
|
297
|
+
card.appendChild(spinRing());
|
|
286
298
|
if (req.elicitation_id) card.dataset.elicitationId = req.elicitation_id;
|
|
287
299
|
|
|
288
300
|
const hdr = document.createElement('div');
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { normalizeSession } from './cp-session-model.js';
|
|
8
8
|
import { TOOL_POLICIES, BYPASS_MODE } from './cp-permission-model.js';
|
|
9
|
+
import { applyTaskTool } from './cp-tool-results.js';
|
|
9
10
|
|
|
10
11
|
const ACCOUNT_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
11
12
|
const PERMISSION_MODES = new Set(['default', 'acceptEdits', 'plan', 'bypassPermissions', 'dontAsk', 'auto']);
|
|
@@ -222,3 +223,164 @@ export function pagerState(dataset, labelText = '') {
|
|
|
222
223
|
if (!Number.isInteger(start) || !Number.isInteger(total) || start <= 0 || total <= start) return null;
|
|
223
224
|
return { start, total };
|
|
224
225
|
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Split a history page into `{ head, tail }` so the visible tail (~40 rows) can
|
|
229
|
+
* paint and scroll immediately while earlier rows prepend in chunked tasks.
|
|
230
|
+
* Never splits an `assistant` `tool_use` from its matching `tool_result`.
|
|
231
|
+
*/
|
|
232
|
+
export function splitHistoryTail(messages, tailSize = 40) {
|
|
233
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
234
|
+
const cap = Math.max(1, Number(tailSize) || 40);
|
|
235
|
+
if (list.length <= Math.ceil(cap * 1.5)) return { head: [], tail: list };
|
|
236
|
+
let cut = list.length - cap;
|
|
237
|
+
const indexOfToolUse = new Map();
|
|
238
|
+
for (let i = 0; i < list.length; i++) {
|
|
239
|
+
const m = list[i];
|
|
240
|
+
if (m?.role === 'assistant' && Array.isArray(m.tools)) {
|
|
241
|
+
for (const t of m.tools) if (t?.id) indexOfToolUse.set(String(t.id), i);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (let i = cut; i < list.length; i++) {
|
|
245
|
+
const m = list[i];
|
|
246
|
+
if (m?.role === 'tool_result' && m.toolUseId) {
|
|
247
|
+
const ownerIdx = indexOfToolUse.get(String(m.toolUseId));
|
|
248
|
+
if (ownerIdx !== undefined && ownerIdx < cut) cut = ownerIdx;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
while (cut > 0 && list[cut]?.role === 'tool_result') cut--;
|
|
252
|
+
if (cut <= 0) return { head: [], tail: list };
|
|
253
|
+
return { head: list.slice(0, cut), tail: list.slice(cut) };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Chronologically replay TaskCreate / TaskUpdate / TaskList results from a
|
|
258
|
+
* history page so tail-first DOM rendering still preserves exact todo order.
|
|
259
|
+
*/
|
|
260
|
+
export function replayTodosFromMessages(messages, initialTodos = []) {
|
|
261
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
262
|
+
const calls = new Map();
|
|
263
|
+
for (const m of list) {
|
|
264
|
+
if (m?.role === 'assistant' && Array.isArray(m.tools)) {
|
|
265
|
+
for (const t of m.tools) {
|
|
266
|
+
if (t?.id && (t.name === 'TaskCreate' || t.name === 'TaskUpdate' || t.name === 'TaskList')) {
|
|
267
|
+
calls.set(String(t.id), t);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (!calls.size) return null;
|
|
273
|
+
let todos = Array.isArray(initialTodos) ? initialTodos : [];
|
|
274
|
+
let changed = false;
|
|
275
|
+
for (const m of list) {
|
|
276
|
+
if (m?.role !== 'tool_result' || m.isError || !m.toolUseId) continue;
|
|
277
|
+
const call = calls.get(String(m.toolUseId));
|
|
278
|
+
if (!call) continue;
|
|
279
|
+
const next = applyTaskTool(todos, call.name, call.input || {}, m.structured);
|
|
280
|
+
if (next) {
|
|
281
|
+
todos = next;
|
|
282
|
+
changed = true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return changed ? todos : null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── One transcript container, several history loads ──
|
|
289
|
+
// A container can be asked for its history more than once while a load is still
|
|
290
|
+
// running (a restored tab shown while its reattach says "resynced", an in-tab
|
|
291
|
+
// session switch). Loads of one container run one after the other, and only the
|
|
292
|
+
// newest asked for renders; rows a rebuild put in are marked so the next one
|
|
293
|
+
// takes them out instead of appending a second copy after them. Rows that came
|
|
294
|
+
// live stay, after the history.
|
|
295
|
+
|
|
296
|
+
/** The property a history row carries: the load (ticket) that rendered it. */
|
|
297
|
+
export const HISTORY_ROW = '_cpHistoryRow';
|
|
298
|
+
|
|
299
|
+
export function markHistoryRows(nodes, ticket) {
|
|
300
|
+
for (const n of nodes || []) if (n && typeof n === 'object') n[HISTORY_ROW] = ticket;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Rows an earlier rebuild (finished, aborted, or a snapshot put back) left in the container. */
|
|
304
|
+
export function earlierHistoryRows(children, ticket) {
|
|
305
|
+
return [...(children || [])].filter((n) => n && n[HISTORY_ROW] != null && n[HISTORY_ROW] !== ticket);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Rows that came live: no history mark and not the empty-state placeholder. */
|
|
309
|
+
export function liveRows(children) {
|
|
310
|
+
return [...(children || [])].filter((n) => n && n[HISTORY_ROW] == null && !n.classList?.contains?.('cp-empty'));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Starts a load of `$msgs`: it waits for the one running there and is skipped when
|
|
315
|
+
* a newer load was asked for meanwhile. `run(isStale)` does the work; `isStale()`
|
|
316
|
+
* is true as soon as a newer load was asked for (the running one stops at its next
|
|
317
|
+
* yield). Returns the promise of this load.
|
|
318
|
+
*/
|
|
319
|
+
export function queueHistoryLoad(container, run) {
|
|
320
|
+
const ticket = (container._historyTicket || 0) + 1;
|
|
321
|
+
container._historyTicket = ticket;
|
|
322
|
+
const isStale = () => container._historyTicket !== ticket;
|
|
323
|
+
const prev = container._historyChain || Promise.resolve();
|
|
324
|
+
const job = prev.catch(() => {}).then(() => (isStale() ? undefined : run(isStale, ticket)));
|
|
325
|
+
container._historyChain = job;
|
|
326
|
+
return job;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Messages that put rows in a transcript. One of these for a restored tab whose
|
|
330
|
+
// history is still deferred loads that history first; while any history load of
|
|
331
|
+
// the tab runs, they wait in the tab's buffer, so the history always comes first.
|
|
332
|
+
const TRANSCRIPT_MESSAGES = new Set(['event', 'turn_started', 'stderr', 'done', 'aborted', 'error']);
|
|
333
|
+
|
|
334
|
+
/** 'load' (start the deferred history, then hold the message), 'hold', or 'pass'. */
|
|
335
|
+
export function historyGate(tab, msg, bypass = false) {
|
|
336
|
+
if (!tab || bypass) return 'pass';
|
|
337
|
+
const deferred = !!tab._historyDeferred && tab._historyDeferred === tab.sessionId && !tab.automationRunId;
|
|
338
|
+
if (deferred && TRANSCRIPT_MESSAGES.has(msg?.type)) return 'load';
|
|
339
|
+
if (tab._historyHold > 0) return 'hold';
|
|
340
|
+
return 'pass';
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* A live tool result whose card a running replay has not rendered yet (it sits in
|
|
345
|
+
* a head chunk still to come) is kept here and applied when the card is built.
|
|
346
|
+
*/
|
|
347
|
+
export function holdPendingResult(tab, ev) {
|
|
348
|
+
if (!tab || !ev?.tool_use_id || !(tab._historyRendering > 0) || tab._replayRow) return false;
|
|
349
|
+
if (!(tab._pendingResults instanceof Map)) tab._pendingResults = new Map();
|
|
350
|
+
tab._pendingResults.set(String(ev.tool_use_id), ev);
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function takePendingResult(tab, toolUseId) {
|
|
355
|
+
const map = tab?._pendingResults;
|
|
356
|
+
if (!(map instanceof Map) || !toolUseId) return null;
|
|
357
|
+
const ev = map.get(String(toolUseId)) || null;
|
|
358
|
+
if (ev) map.delete(String(toolUseId));
|
|
359
|
+
return ev;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Whether a task tool's result may change the todo list now (a replayed one is in the precomputed list). */
|
|
363
|
+
export function taskResultAppliesTodos(tab, ev) {
|
|
364
|
+
if (!tab || tab._agentScope || ev?.is_error) return false;
|
|
365
|
+
return !(tab._skipReplayTodos && tab._replayRow);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Renders `rows` with `render(row)` (which appends to `container`), marks the
|
|
370
|
+
* nodes it added as load `ticket`'s and moves them before `before` (a live row,
|
|
371
|
+
* or the first row of the tail already shown), so rows already on screen are
|
|
372
|
+
* never moved or rebuilt: the browser's scroll anchoring keeps a reader who
|
|
373
|
+
* scrolled up where they were while older rows go in above. `skip` holds nodes
|
|
374
|
+
* that are never taken as added (the live rows). Returns the added nodes.
|
|
375
|
+
*/
|
|
376
|
+
export function renderHistoryRowsInto(container, rows, render, { before = null, ticket = null, skip = null } = {}) {
|
|
377
|
+
const marker = container.lastElementChild;
|
|
378
|
+
for (const row of rows || []) render(row);
|
|
379
|
+
const added = [];
|
|
380
|
+
for (let n = marker ? marker.nextElementSibling : container.firstElementChild; n; n = n.nextElementSibling) {
|
|
381
|
+
if (!skip || !skip.has(n)) added.push(n);
|
|
382
|
+
}
|
|
383
|
+
markHistoryRows(added, ticket);
|
|
384
|
+
if (before && before.parentNode === container) for (const n of added) container.insertBefore(n, before);
|
|
385
|
+
return added;
|
|
386
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// puts that on the card. Live tool results and replayed history take the same
|
|
4
4
|
// path, so a reopened session looks like the live one did.
|
|
5
5
|
|
|
6
|
+
import { taskResultAppliesTodos } from './cp-restore.js';
|
|
6
7
|
import { cpCtx } from './cp-ctx.js';
|
|
7
8
|
import { escapeHtml } from './cp-markdown.js';
|
|
8
9
|
import {
|
|
@@ -212,11 +213,12 @@ export function applyStructuredResult(tab, card, ev) {
|
|
|
212
213
|
body.appendChild(stats);
|
|
213
214
|
}
|
|
214
215
|
// The task tools keep the list the todo dock shows (root conversation only).
|
|
215
|
-
|
|
216
|
+
// A replayed result is already in the list the replay computed; a live one is applied over it.
|
|
217
|
+
if (taskResultAppliesTodos(tab, ev)) {
|
|
216
218
|
const next = applyTaskTool(tab.todos, name, input, ev.tool_use_result);
|
|
217
219
|
if (next) {
|
|
218
220
|
tab.todos = next;
|
|
219
|
-
try { cpCtx.renderTodoWidget(tab); } catch {}
|
|
221
|
+
if (!tab._replaying) { try { cpCtx.renderTodoWidget(tab); } catch {} }
|
|
220
222
|
}
|
|
221
223
|
}
|
|
222
224
|
return { view, content: view.content };
|
|
@@ -285,7 +287,12 @@ export function buildHistoryPlanCard(block) {
|
|
|
285
287
|
|
|
286
288
|
/** A replayed result for one of the two cards above. True when it was one of them. */
|
|
287
289
|
export function fillHistoryCard($msgs, row) {
|
|
288
|
-
const
|
|
290
|
+
const id = String(row.toolUseId || '');
|
|
291
|
+
let card = null;
|
|
292
|
+
// A replay indexes the cards it builds (no selector scan per result); a card it did not build is looked up.
|
|
293
|
+
const indexed = $msgs?._replayCards instanceof Map ? $msgs._replayCards.get(id) : null;
|
|
294
|
+
if (indexed) card = indexed.classList?.contains('cp-history-card') ? indexed : null;
|
|
295
|
+
else card = $msgs.querySelector(`.cp-history-card[data-tool-id="${CSS.escape(id)}"]`);
|
|
289
296
|
if (!card) return false;
|
|
290
297
|
if (card.classList.contains('cp-ask-history')) {
|
|
291
298
|
const rows = askAnswerRows(card._toolInput, row.structured, row.isError ? '' : (row.text || ''));
|
|
@@ -48,3 +48,20 @@ export function desktopWindow(win = globalThis.window) {
|
|
|
48
48
|
if (!facts || facts.style !== 'overlay' || !['darwin', 'win32', 'linux'].includes(facts.platform)) return null;
|
|
49
49
|
return api;
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
/** True on Windows (desktop bridge platform `'win32'`, or Windows browser/window navigator). */
|
|
53
|
+
export function isWindowsPlatform(win = globalThis.window) {
|
|
54
|
+
const platform = desktopBridge(win)?.platform;
|
|
55
|
+
if (typeof platform === 'string' && platform) return platform === 'win32';
|
|
56
|
+
try {
|
|
57
|
+
const nav = win?.navigator ?? globalThis.navigator;
|
|
58
|
+
if (!nav || typeof nav !== 'object') return false;
|
|
59
|
+
const uaPlatform = nav.userAgentData?.platform;
|
|
60
|
+
if (typeof uaPlatform === 'string' && uaPlatform) return /^win/i.test(uaPlatform);
|
|
61
|
+
if (typeof nav.platform === 'string' && nav.platform) return /^win/i.test(nav.platform);
|
|
62
|
+
if (typeof nav.userAgent === 'string' && nav.userAgent) return /\bwindows\b/i.test(nav.userAgent);
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
@@ -197,3 +197,15 @@ export function drawnUnderBar(style, barLevel) {
|
|
|
197
197
|
const level = Number.isFinite(z) ? (positioned ? z : Math.max(z, 0)) : 0;
|
|
198
198
|
return level < barLevel;
|
|
199
199
|
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* True for a body-level overlay that never receives pointer events on itself or any child (such as `.ui-tooltip` or
|
|
203
|
+
* `#synabun-desktop-drag-strip`), so its hover/move mutations can never cover the bar's drag region.
|
|
204
|
+
*/
|
|
205
|
+
export function isPassiveOverlay(node) {
|
|
206
|
+
if (!node || typeof node !== 'object') return false;
|
|
207
|
+
if (node.id === 'synabun-desktop-drag-strip') return true;
|
|
208
|
+
if (typeof node.classList?.contains === 'function' && node.classList.contains('ui-tooltip')) return true;
|
|
209
|
+
const cls = typeof node.className === 'string' ? node.className : '';
|
|
210
|
+
return /(?:^|\s)ui-tooltip(?:\s|$)/.test(cls);
|
|
211
|
+
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
import { desktopWindow } from './desktop-bridge.js';
|
|
29
29
|
import { colorSender, textTone } from './desktop-titlebar-page.js';
|
|
30
|
-
import { barCovered, changedClasses, drawnUnderBar, fallbackTitlebarArea, overlayColorsFor, stackLevel, styleChange, titleBarActive } from './desktop-titlebar-view.js';
|
|
30
|
+
import { barCovered, changedClasses, drawnUnderBar, fallbackTitlebarArea, isPassiveOverlay, overlayColorsFor, stackLevel, styleChange, titleBarActive } from './desktop-titlebar-view.js';
|
|
31
31
|
import { t } from './i18n.js';
|
|
32
32
|
|
|
33
33
|
const MENU_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h16"/></svg>';
|
|
@@ -52,6 +52,7 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
52
52
|
const facts = api.titleBar;
|
|
53
53
|
const html = doc.documentElement;
|
|
54
54
|
html.classList.add('desktop-titlebar', `desktop-titlebar--${facts.platform}`);
|
|
55
|
+
if (facts.platform === 'win32') html.classList.add('platform-win32');
|
|
55
56
|
const wco = view.navigator?.windowControlsOverlay || null;
|
|
56
57
|
let state = { maximized: false, fullScreen: false, focused: true };
|
|
57
58
|
const send = colorSender(api, doc);
|
|
@@ -168,14 +169,22 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
168
169
|
return null;
|
|
169
170
|
};
|
|
170
171
|
const hasElement = (nodes) => { for (const node of nodes || []) if (node?.nodeType === 1) return true; return false; };
|
|
172
|
+
const onlyPassiveElements = (nodes) => {
|
|
173
|
+
for (const node of nodes || []) if (node?.nodeType === 1 && !isPassiveOverlay(node)) return false;
|
|
174
|
+
return true;
|
|
175
|
+
};
|
|
171
176
|
// Each change → [IGNORE | SOON | NOW, whether the bar has to be measured].
|
|
172
177
|
const QUIET = [IGNORE, false];
|
|
173
178
|
const childListChange = (record) => {
|
|
174
179
|
if (!hasElement(record.addedNodes) && !hasElement(record.removedNodes)) return QUIET; // text
|
|
175
180
|
const target = record.target;
|
|
176
|
-
if (target === body) {
|
|
181
|
+
if (target === body) {
|
|
182
|
+
if (onlyPassiveElements(record.addedNodes) && onlyPassiveElements(record.removedNodes)) return QUIET;
|
|
183
|
+
judgeAgain();
|
|
184
|
+
return [NOW, true]; // an overlay's root; a sibling selector may match anew
|
|
185
|
+
}
|
|
177
186
|
const part = partOf(target);
|
|
178
|
-
if (!part || part === bar) return QUIET; // off the page,
|
|
187
|
+
if (!part || part === bar || isPassiveOverlay(part)) return QUIET; // off the page, passive tooltip, or inside the bar
|
|
179
188
|
toJudge.add(part);
|
|
180
189
|
if (target === part) return [NOW, true]; // an overlay's root got or lost a child (:empty, :has() can show it)
|
|
181
190
|
return [SOON, !underBar.has(part)];
|
|
@@ -189,7 +198,7 @@ export function initDesktopTitleBar({ doc = document, view = window } = {}) {
|
|
|
189
198
|
return [NOW, true];
|
|
190
199
|
}
|
|
191
200
|
const part = partOf(target);
|
|
192
|
-
if (!part) return QUIET;
|
|
201
|
+
if (!part || isPassiveOverlay(part)) return QUIET;
|
|
193
202
|
if (part === bar) return target === bar ? [NOW, true] : QUIET;
|
|
194
203
|
toJudge.add(part);
|
|
195
204
|
// Shown, hidden or restacked: on the next frame; only moved, sized or repainted: at the throttled pace. Inside a
|
|
@@ -87,6 +87,7 @@ export const DESKTOP_COPY = Object.freeze({
|
|
|
87
87
|
'claude-panel': 'a Claude Code panel reply',
|
|
88
88
|
'codex-panel': 'a Codex panel reply',
|
|
89
89
|
'opencode-panel': 'an OpenCode panel reply',
|
|
90
|
+
'antigravity-panel': 'an Antigravity panel reply',
|
|
90
91
|
'claude-cli': 'a Claude Code session in use',
|
|
91
92
|
loop: 'a loop',
|
|
92
93
|
schedule: 'a schedule',
|
|
@@ -96,15 +96,15 @@ export function createExplorerApi({ fetchImpl = (...args) => fetch(...args), Eve
|
|
|
96
96
|
return old.ok ? fromLegacySearch(old) : old;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async function git(root) {
|
|
99
|
+
async function git(root, signal, refresh = false) {
|
|
100
100
|
await capabilities();
|
|
101
|
-
if (caps.explorer) return request(`/api/explorer/git?root=${encodeURIComponent(root)}
|
|
102
|
-
const old = await request(`/api/git/status?path=${encodeURIComponent(root)}
|
|
101
|
+
if (caps.explorer) return request(`/api/explorer/git?root=${encodeURIComponent(root)}${refresh ? '&refresh=1' : ''}`, { signal });
|
|
102
|
+
const old = await request(`/api/git/status?path=${encodeURIComponent(root)}${refresh ? '&refresh=1' : ''}`, { signal });
|
|
103
103
|
if (!old.ok) return old;
|
|
104
104
|
if (!old.isGit) return { ok: true, isGit: false };
|
|
105
105
|
const changes = {};
|
|
106
106
|
for (const change of old.changes || []) if (change?.path) changes[change.path] = change.status;
|
|
107
|
-
return { ok: true, isGit: true, branch: old.branch || '', changes, ignored: [], legacy: true };
|
|
107
|
+
return { ok: true, isGit: true, branch: old.branch || '', changes, ignored: [], legacy: true, ...(old.partial ? { partial: true, scanState: old.scanState } : {}) };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
async function create(parent, name, type) {
|
|
@@ -110,13 +110,13 @@ export function createEditorLink({ openFile, save, tabs, repoint, subscribe, onC
|
|
|
110
110
|
|
|
111
111
|
// ── The agents ──
|
|
112
112
|
|
|
113
|
-
export const SEND_TARGETS = Object.freeze(['claude', 'codex', 'opencode', 'assistant']);
|
|
114
|
-
const TARGET_NAMES = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode' };
|
|
113
|
+
export const SEND_TARGETS = Object.freeze(['claude', 'codex', 'opencode', 'antigravity', 'assistant']);
|
|
114
|
+
const TARGET_NAMES = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity' };
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* @param {object} o
|
|
118
118
|
* @param {object} o.deps what each panel offers: `sendToFocusedSession(kind, data)`, `claude(path)`, `codex(path)`,
|
|
119
|
-
* `opencode(path)`, `assistant(path)`, and `lastActive()` (the panel used last)
|
|
119
|
+
* `opencode(path)`, `antigravity(path)`, `assistant(path)`, and `lastActive()` (the panel used last)
|
|
120
120
|
* @param {(key: string, params?: object) => string} o.t
|
|
121
121
|
*/
|
|
122
122
|
export function createSender({ deps, t }) {
|
|
@@ -56,7 +56,7 @@ export function sameListing(a, b) {
|
|
|
56
56
|
if (!a || !b || a.length !== b.length) return false;
|
|
57
57
|
for (let i = 0; i < a.length; i++) {
|
|
58
58
|
const x = a[i]; const y = b[i];
|
|
59
|
-
if (x.name !== y.name || x.type !== y.type || x.size !== y.size || x.mtime !== y.mtime || !!x.link !== !!y.link || !!x.outside !== !!y.outside || !!x.broken !== !!y.broken) return false;
|
|
59
|
+
if (x.name !== y.name || x.type !== y.type || x.size !== y.size || x.mtime !== y.mtime || !!x.link !== !!y.link || !!x.outside !== !!y.outside || !!x.broken !== !!y.broken || !!x.ignored !== !!y.ignored || (x.ignored === null) !== (y.ignored === null)) return false;
|
|
60
60
|
}
|
|
61
61
|
return true;
|
|
62
62
|
}
|
|
@@ -48,7 +48,9 @@ const EXPAND_LEVEL_MAX = 200;
|
|
|
48
48
|
* @param {object} host.send what each side panel offers for a path (fx-links.js createSender)
|
|
49
49
|
* @param {(name: string) => object} host.fileIcon @param {string} host.folderSvg
|
|
50
50
|
* @param {{ all: { hex: string, key: string }[], of: (path: string) => string | undefined, set: Function, move: Function }} host.colors
|
|
51
|
-
* @param {(git: { isGit: boolean, branch: string, count: number
|
|
51
|
+
* @param {(git: { isGit: boolean, branch: string, count: number, scanState?: 'ready' | 'scanning' | 'timed-out' | 'failed',
|
|
52
|
+
* shown?: boolean, loud?: boolean }) => void} host.onGit `shown`: a result of this project is already on screen;
|
|
53
|
+
* `loud`: the scan is the project's first or one the owner asked for (Refresh), not a background one.
|
|
52
54
|
* @param {() => void} host.addProject
|
|
53
55
|
* @param {typeof fetch} [host.fetchImpl]
|
|
54
56
|
*/
|
|
@@ -77,7 +79,10 @@ export function createExplorer(host) {
|
|
|
77
79
|
let watcher = null;
|
|
78
80
|
let saidLimited = false; // this project was told once that not everything on disk is followed
|
|
79
81
|
let loaded = Promise.resolve();
|
|
80
|
-
let gitTimer = 0; let
|
|
82
|
+
let gitTimer = 0; let gitRequest = null; let gitRefresh = false; let gitAgain = false; let lastGitAt = 0;
|
|
83
|
+
// gitShown: a result of this project is on screen. gitLoud: the owner asked (Refresh), so a slow scan may say so.
|
|
84
|
+
// gitSettled: the last state a scan ended in, put back when a scan that said 'scanning' is called off.
|
|
85
|
+
let gitShown = false; let gitLoud = false; let gitSettled = 'ready';
|
|
81
86
|
let diskTimer = 0; let lastDiskAt = 0; const diskDue = new Set();
|
|
82
87
|
let filterTimer = 0; let searchAbort = null;
|
|
83
88
|
let persistTimer = 0; let lastFocusRefresh = 0;
|
|
@@ -114,18 +119,20 @@ export function createExplorer(host) {
|
|
|
114
119
|
const ed = editor.state();
|
|
115
120
|
const status = item.missing ? 'deleted' : S.git.statusOf(below, isDir);
|
|
116
121
|
const tone = item.missing ? 'deleted' : S.git.toneOf(below, isDir);
|
|
117
|
-
const ignored = S.git.isIgnored(below);
|
|
122
|
+
const ignored = !!item.ignored || S.git.isIgnored(below);
|
|
123
|
+
const ignoreUnknown = item.ignored === null && !ignored;
|
|
118
124
|
const open = !isDir && ed.open.has(path);
|
|
119
125
|
const dirty = !isDir && ed.dirty.has(path);
|
|
120
126
|
const cut = !!S.clipboard && S.clipboard.mode === 'cut' && S.clipboard.paths.some((held) => isWithin(held, path));
|
|
121
127
|
const words = [];
|
|
122
128
|
if (status) words.push(t(`fileExplorer.git.${status}`)); else if (tone) words.push(t('fileExplorer.git.contains'));
|
|
123
129
|
if (ignored) words.push(t('fileExplorer.git.ignored'));
|
|
130
|
+
else if (ignoreUnknown) words.push(t('fileExplorer.git.ignoreUnknown'));
|
|
124
131
|
if (dirty) words.push(t('fileExplorer.row.unsaved')); else if (open) words.push(t('fileExplorer.row.open'));
|
|
125
132
|
if (item.outside) words.push(t('fileExplorer.row.linkOutside')); else if (item.broken) words.push(t('fileExplorer.row.linkBroken')); else if (item.link) words.push(t('fileExplorer.row.link'));
|
|
126
133
|
if (cut) words.push(t('fileExplorer.row.cut'));
|
|
127
134
|
return {
|
|
128
|
-
tone, letter: status ? GIT_MARKS[status].letter : '', ignored, open, dirty, cut, words,
|
|
135
|
+
tone, letter: status ? GIT_MARKS[status].letter : '', ignored, ignoreUnknown, open, dirty, cut, words,
|
|
129
136
|
active: !isDir && ed.active === path,
|
|
130
137
|
missing: !!item.missing,
|
|
131
138
|
faint: !!item.outside || !!item.broken,
|
|
@@ -296,24 +303,46 @@ export function createExplorer(host) {
|
|
|
296
303
|
|
|
297
304
|
// ── Git ──
|
|
298
305
|
|
|
306
|
+
function cancelGit() {
|
|
307
|
+
clearTimeout(gitTimer);
|
|
308
|
+
gitRequest?.controller.abort();
|
|
309
|
+
gitRequest = null; gitAgain = false; gitRefresh = false; gitLoud = false;
|
|
310
|
+
}
|
|
299
311
|
async function loadGit() {
|
|
300
|
-
if (!S.root) return;
|
|
301
|
-
if (
|
|
302
|
-
|
|
312
|
+
if (!S.root || !S.visible) return;
|
|
313
|
+
if (gitRequest?.root === S.root) { gitAgain = true; return; }
|
|
314
|
+
gitRequest?.controller.abort();
|
|
315
|
+
const request = { root: S.root, controller: new AbortController() };
|
|
316
|
+
gitRequest = request;
|
|
303
317
|
const mine = epoch;
|
|
304
|
-
const
|
|
305
|
-
|
|
318
|
+
const refresh = gitRefresh; gitRefresh = false;
|
|
319
|
+
// A background scan (the watcher, focus, the popover) never puts words in the header while a result is shown:
|
|
320
|
+
// only the first scan of a project, or one the owner asked for, is loud.
|
|
321
|
+
const loud = gitLoud || !gitShown; gitLoud = false;
|
|
322
|
+
let said = '';
|
|
323
|
+
const say = (scanState) => { said = scanState; host.onGit({ ...gitState(scanState), loud }); };
|
|
324
|
+
const slow = setTimeout(() => { if (gitRequest === request && mine === epoch) say('scanning'); }, 1500);
|
|
325
|
+
const got = await api.git(request.root, request.controller.signal, refresh);
|
|
326
|
+
clearTimeout(slow);
|
|
327
|
+
if (gitRequest !== request) return;
|
|
328
|
+
gitRequest = null;
|
|
306
329
|
if (mine === epoch && got.ok) {
|
|
307
|
-
S.git = gitIndex(got);
|
|
308
|
-
|
|
330
|
+
if (!got.partial || !S.git.isGit) S.git = gitIndex(got);
|
|
331
|
+
gitShown = true;
|
|
332
|
+
say(gitSettled = got.scanState || 'ready');
|
|
309
333
|
if (S.changedOnly) { buildView(); tree.render(); } else tree.refreshRows();
|
|
310
334
|
updateCount();
|
|
311
|
-
}
|
|
335
|
+
} else if (mine === epoch && got.code !== 'aborted') say(gitSettled = 'failed');
|
|
336
|
+
else if (mine === epoch && said === 'scanning') say(gitSettled);
|
|
312
337
|
if (gitAgain) { gitAgain = false; queueGit(); }
|
|
313
338
|
}
|
|
339
|
+
function gitState(scanState) {
|
|
340
|
+
return { isGit: S.git.isGit, branch: S.git.branch, count: S.git.count, scanState, shown: gitShown };
|
|
341
|
+
}
|
|
314
342
|
// Soon after the last sign of a change, and never more often than once in a while: a file that is written
|
|
315
343
|
// without pause (a log, a database) does not make git run without pause.
|
|
316
344
|
function queueGit() {
|
|
345
|
+
if (!S.root || !S.visible) return;
|
|
317
346
|
clearTimeout(gitTimer);
|
|
318
347
|
gitTimer = setTimeout(() => { lastGitAt = Date.now(); loadGit(); }, Math.max(GIT_DELAY_MS, lastGitAt + GIT_MIN_INTERVAL_MS - Date.now()));
|
|
319
348
|
}
|
|
@@ -654,7 +683,7 @@ export function createExplorer(host) {
|
|
|
654
683
|
case 'sortBy': return [back(), ...SORTS.map((mode) => ({ id: `sort:${mode}`, label: t(`fileExplorer.menu.sort.${mode}`), checked: S.sort === mode }))];
|
|
655
684
|
case 'expandAll': expandLevel(); break;
|
|
656
685
|
case 'collapseAll': collapseAll(); break;
|
|
657
|
-
case 'refresh': refreshAll(); break;
|
|
686
|
+
case 'refresh': gitRefresh = true; gitLoud = true; refreshAll(); break;
|
|
658
687
|
default:
|
|
659
688
|
}
|
|
660
689
|
return undefined;
|
|
@@ -739,7 +768,7 @@ export function createExplorer(host) {
|
|
|
739
768
|
});
|
|
740
769
|
on('fe-collapse-all', collapseAll);
|
|
741
770
|
// Refresh also asks what the server has: one that was restarted since the page loaded is found out here.
|
|
742
|
-
on('fe-refresh', async () => { await host.readServer?.(); await api.capabilities(); syncToolbar(); await refreshAll(); if (!watcher) startWatch(); });
|
|
771
|
+
on('fe-refresh', async () => { gitRefresh = true; gitLoud = true; await host.readServer?.(); await api.capabilities(); syncToolbar(); await refreshAll(); if (!watcher) startWatch(); });
|
|
743
772
|
on('fe-more', (event) => { const box = event.currentTarget.getBoundingClientRect(); openMenu({ y: box.top + box.height / 2, keyboard: event.detail === 0 }); });
|
|
744
773
|
on('fe-filter-clear', () => { clearFilter(); filterInput.focus(); });
|
|
745
774
|
filterInput.addEventListener('input', () => setFilter(filterInput.value));
|
|
@@ -773,11 +802,13 @@ export function createExplorer(host) {
|
|
|
773
802
|
function setProject(project) {
|
|
774
803
|
persist();
|
|
775
804
|
epoch++;
|
|
805
|
+
cancelGit();
|
|
776
806
|
stopWatch();
|
|
777
807
|
saidLimited = false;
|
|
778
808
|
closeFlyout({ refocus: false });
|
|
779
809
|
searchAbort?.abort();
|
|
780
810
|
clearTimeout(gitTimer);
|
|
811
|
+
gitShown = false; gitSettled = 'ready';
|
|
781
812
|
S.dirs = new Map(); S.git = gitIndex(null); S.view = null; S.viewOpen = new Set(); S.viewClosed = new Set();
|
|
782
813
|
S.search = null; S.filter = ''; S.clipboard = null;
|
|
783
814
|
reads.clear();
|
|
@@ -824,7 +855,7 @@ export function createExplorer(host) {
|
|
|
824
855
|
/** The panel is shown or hidden: the disk is followed only while it is shown. */
|
|
825
856
|
function setVisible(visible) {
|
|
826
857
|
S.visible = !!visible;
|
|
827
|
-
if (S.visible) { if (S.root) { startWatch(); if (S.dirs.size) refreshAll(); } } else { persist(); stopWatch(); closeFlyout({ refocus: false }); }
|
|
858
|
+
if (S.visible) { if (S.root) { startWatch(); if (S.dirs.size) refreshAll(); } } else { persist(); cancelGit(); host.onGit(gitState(gitSettled)); stopWatch(); closeFlyout({ refocus: false }); }
|
|
828
859
|
}
|
|
829
860
|
|
|
830
861
|
/** Show a file of the project in the tree: its folders open, its row is selected and marked. */
|
|
@@ -850,7 +881,8 @@ export function createExplorer(host) {
|
|
|
850
881
|
setProject,
|
|
851
882
|
setVisible,
|
|
852
883
|
reveal,
|
|
853
|
-
|
|
884
|
+
/** Read the disk and git again. `quiet`: nobody asked for it in so many words (the git popover opened). */
|
|
885
|
+
refresh: ({ quiet = false } = {}) => { gitRefresh = true; if (!quiet) gitLoud = true; return refreshAll(); },
|
|
854
886
|
collapseAll,
|
|
855
887
|
/** The icons changed: every row is drawn again. */
|
|
856
888
|
redraw() { if (S.root) { const now = tree.selection(); const top = tree.scrollTop(); tree.setRoot(S.root); tree.render(); tree.select(now.selected, { focus: now.focus, quiet: true, scroll: false }); tree.setScrollTop(top); } },
|
|
@@ -161,6 +161,7 @@ export function createTree({ el, mac, icons, hooks }) {
|
|
|
161
161
|
node.classList.toggle('fe-node--selected', selected.has(path));
|
|
162
162
|
node.classList.toggle('fe-node--focus', focus === path);
|
|
163
163
|
node.classList.toggle('fe-node--ignored', !!info.ignored);
|
|
164
|
+
node.dataset.ignoreState = info.ignoreUnknown ? 'unknown' : info.ignored ? 'ignored' : 'known';
|
|
164
165
|
node.classList.toggle('fe-node--cut', !!info.cut);
|
|
165
166
|
node.classList.toggle('fe-node--open', !!info.open);
|
|
166
167
|
node.classList.toggle('fe-node--active', !!info.active);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// SynaBun — Greeting: the card every surface mounts
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
//
|
|
5
|
-
// One renderer for the greeting: the Assistant and the Claude Code, Codex
|
|
6
|
-
// OpenCode panels mount it inside the empty state they already have, Settings
|
|
5
|
+
// One renderer for the greeting: the Assistant and the Claude Code, Codex,
|
|
6
|
+
// OpenCode and Antigravity panels mount it inside the empty state they already have, Settings
|
|
7
7
|
// shows the same card as its preview. greeting-view.js decides the words; this
|
|
8
8
|
// file asks the server (`POST /api/greeting/open`) and draws.
|
|
9
9
|
//
|
|
@@ -41,6 +41,7 @@ export const GREETING_COPY = Object.freeze({
|
|
|
41
41
|
'agent.claude-code': 'Claude Code',
|
|
42
42
|
'agent.codex': 'Codex',
|
|
43
43
|
'agent.opencode': 'OpenCode',
|
|
44
|
+
'agent.antigravity': 'Antigravity',
|
|
44
45
|
open: 'Open memory',
|
|
45
46
|
'source.writer': 'Written ahead by {model}',
|
|
46
47
|
'source.fallback': 'Put together from the records, no model',
|