zidane-ui 9.1.6 → 9.1.9
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/dist/adapters/run-host.js +17 -20
- package/dist/adapters/server-files-host.js +3 -1
- package/dist/adapters/server-host.js +18 -11
- package/dist/adapters/server-machines-host.js +3 -1
- package/dist/adapters/server-session-host.js +2 -1
- package/dist/adapters/server-settings-accounts.js +2 -1
- package/dist/adapters/server-settings-host.js +3 -3
- package/dist/adapters/sse-host.js +9 -7
- package/dist/chat/SessionGroupHeader.js +2 -2
- package/dist/chat/SessionHostHeader.js +1 -1
- package/dist/chat/StatusDot.js +24 -20
- package/dist/chat/composer/Composer.d.ts +13 -8
- package/dist/chat/composer/Composer.js +14 -10
- package/dist/chat/composer/ComposerEditor.js +1 -1
- package/dist/chat/composer/HostComposer.d.ts +1 -1
- package/dist/chat/composer/HostComposer.js +7 -10
- package/dist/chat/composer/RunStatusChip.js +13 -1
- package/dist/chat/composer/attachment-ingest.d.ts +4 -1
- package/dist/chat/composer/attachment-ingest.js +27 -10
- package/dist/chat/composer/composer-bus.d.ts +9 -1
- package/dist/chat/composer/composer-bus.js +49 -6
- package/dist/chat/composer/index.d.ts +2 -2
- package/dist/chat/composer/index.js +2 -2
- package/dist/chat/composer/use-draft-target.d.ts +4 -0
- package/dist/chat/composer/use-draft-target.js +15 -4
- package/dist/chat/composer/use-send-prompt.d.ts +5 -1
- package/dist/chat/composer/use-send-prompt.js +2 -12
- package/dist/chat/headless/chip-labels.d.ts +2 -0
- package/dist/chat/headless/chip-labels.js +3 -1
- package/dist/chat/headless/composer/labels-editor.d.ts +2 -0
- package/dist/chat/headless/composer/labels-editor.js +2 -0
- package/dist/chat/headless/file-chip.d.ts +12 -13
- package/dist/chat/headless/file-chip.js +40 -2
- package/dist/chat/headless/fixture-host.js +14 -17
- package/dist/chat/headless/host.d.ts +24 -6
- package/dist/chat/headless/index.d.ts +4 -4
- package/dist/chat/headless/index.js +4 -4
- package/dist/chat/headless/labels.d.ts +16 -0
- package/dist/chat/headless/labels.js +15 -2
- package/dist/chat/headless/send-flow.d.ts +21 -4
- package/dist/chat/headless/send-flow.js +25 -6
- package/dist/chat/headless/session-list.d.ts +11 -0
- package/dist/chat/headless/sidebar/index.js +1 -1
- package/dist/chat/headless/sidebar/labels.d.ts +12 -1
- package/dist/chat/headless/transcript-host.d.ts +8 -1
- package/dist/chat/headless/transcript-labels.d.ts +2 -0
- package/dist/chat/index.d.ts +3 -3
- package/dist/chat/index.js +2 -2
- package/dist/chat/interactions/InboxPanel.js +2 -2
- package/dist/chat/interactions/QuestionField.js +2 -2
- package/dist/chat/rich-composer/RichComposer.d.ts +9 -2
- package/dist/chat/rich-composer/RichComposer.js +28 -7
- package/dist/chat/rich-composer/composer-extensions.d.ts +2 -0
- package/dist/chat/rich-composer/composer-extensions.js +2 -2
- package/dist/chat/rich-composer/composer-keys.js +1 -1
- package/dist/chat/rich-composer/use-composer-bus.js +2 -1
- package/dist/chat/rich-composer/use-composer-editor.d.ts +4 -1
- package/dist/chat/rich-composer/use-composer-editor.js +2 -1
- package/dist/chat/sidebar/SessionSidebar.js +1 -2
- package/dist/chat/sidebar/SidebarSearchRow.js +1 -1
- package/dist/chat/sidebar/useSidebarKeyboardNav.d.ts +0 -2
- package/dist/chat/sidebar/useSidebarKeyboardNav.js +7 -13
- package/dist/chat/testing/contract.d.ts +1 -1
- package/dist/chat/testing/contract.js +8 -7
- package/dist/chat/transcript/BadgeMenu.d.ts +12 -3
- package/dist/chat/transcript/BadgeMenu.js +20 -3
- package/dist/chat/transcript/CodemodeScript.js +147 -44
- package/dist/chat/transcript/EditDiffBlock.js +1 -4
- package/dist/chat/transcript/FileChip.js +60 -14
- package/dist/chat/transcript/LiveOutput.js +7 -3
- package/dist/chat/transcript/badges.js +2 -2
- package/dist/chat/transcript/index.d.ts +2 -2
- package/dist/chat/transcript/markdown-context.d.ts +4 -1
- package/dist/chat/transcript/rolling.d.ts +12 -0
- package/dist/chat/transcript/rolling.js +22 -0
- package/dist/chat/transcript/script-input.d.ts +1 -1
- package/dist/chat/transcript/shell-block.js +70 -60
- package/dist/chat/transcript/tool-row.js +21 -5
- package/dist/code/editor/FileTree.d.ts +3 -1
- package/dist/code/editor/FileTree.js +29 -1
- package/dist/components/Restricted.d.ts +25 -0
- package/dist/components/Restricted.js +32 -0
- package/dist/components/StepFrame.d.ts +1 -1
- package/dist/components/ZidaneMark.d.ts +28 -0
- package/dist/components/ZidaneMark.js +114 -0
- package/dist/components/palette/PalettePanel.d.ts +7 -1
- package/dist/components/palette/PalettePanel.js +37 -11
- package/dist/components/zidane-mark.d.ts +52 -0
- package/dist/components/zidane-mark.js +94 -0
- package/dist/core.css +59 -6
- package/dist/dist/{index-DqCio7Nd.d.ts → index-Bq87OcGy.d.ts} +19 -12
- package/dist/dist/index.d.ts +1 -1
- package/dist/extensions/ExtensionSettingsSection.d.ts +18 -0
- package/dist/extensions/ExtensionSettingsSection.js +227 -0
- package/dist/extensions/headless/host.d.ts +1 -1
- package/dist/extensions/headless/labels.d.ts +41 -1
- package/dist/extensions/headless/labels.js +57 -0
- package/dist/extensions/headless/select.js +1 -2
- package/dist/extensions/index.d.ts +2 -1
- package/dist/extensions/index.js +2 -1
- package/dist/files/FilesTab.js +2 -0
- package/dist/files/TreeColumn.d.ts +5 -1
- package/dist/files/TreeColumn.js +6 -0
- package/dist/files/headless/fixture-host.js +5 -3
- package/dist/files/headless/host.d.ts +24 -6
- package/dist/files/headless/index.d.ts +2 -2
- package/dist/files/headless/labels.d.ts +1 -0
- package/dist/files/headless/labels.js +1 -0
- package/dist/files/testing/contract.js +9 -2
- package/dist/files/useWorkspace.d.ts +14 -2
- package/dist/files/useWorkspace.js +87 -9
- package/dist/git/BranchMenu.js +1 -1
- package/dist/git/MoveSessionDialog.js +1 -1
- package/dist/git/RemoteFolderPicker.js +1 -1
- package/dist/git/WherePicker.js +1 -1
- package/dist/git/headless/index.js +1 -1
- package/dist/git/useEnvironmentChoices.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/keybindings/headless/catalog.js +1 -1
- package/dist/labels.d.ts +1 -0
- package/dist/labels.js +1 -0
- package/dist/machines/DaemonMenu.js +1 -1
- package/dist/machines/HostCapabilities.d.ts +23 -1
- package/dist/machines/HostCapabilities.js +149 -65
- package/dist/machines/HostSyncPanel.js +129 -123
- package/dist/machines/HostsSection.js +5 -1
- package/dist/machines/ServerCard.d.ts +4 -2
- package/dist/machines/ServerCard.js +222 -99
- package/dist/machines/ServerMachinesPanel.js +16 -13
- package/dist/machines/ServerUpdatePanel.js +31 -21
- package/dist/machines/TokensPanel.js +75 -53
- package/dist/machines/headless/fixture-host.d.ts +5 -0
- package/dist/machines/headless/fixture-host.js +13 -4
- package/dist/machines/headless/fixtures.js +1 -1
- package/dist/machines/headless/host.d.ts +22 -2
- package/dist/machines/headless/index.d.ts +2 -2
- package/dist/machines/headless/labels.d.ts +22 -1
- package/dist/machines/headless/labels.js +22 -2
- package/dist/machines/testing/contract.d.ts +3 -1
- package/dist/machines/testing/contract.js +9 -0
- package/dist/motion/size.d.ts +22 -1
- package/dist/motion/size.js +82 -4
- package/dist/onboarding/demos/delivery.d.ts +16 -0
- package/dist/onboarding/demos/delivery.js +132 -0
- package/dist/onboarding/demos/index.d.ts +2 -1
- package/dist/onboarding/demos/index.js +2 -1
- package/dist/onboarding/headless/labels.d.ts +8 -0
- package/dist/onboarding/headless/labels.js +7 -0
- package/dist/onboarding/index.d.ts +2 -1
- package/dist/onboarding/index.js +2 -1
- package/dist/onboarding/steps/ConversationStep.js +46 -4
- package/dist/palette/CommandPalette.js +3 -0
- package/dist/palette/PaletteDialog.js +7 -3
- package/dist/palette/usePaletteController.d.ts +10 -0
- package/dist/palette/usePaletteController.js +25 -3
- package/dist/server/sse-handler.js +6 -1
- package/dist/session/ArtifactsPanel.js +1 -1
- package/dist/session/SideChat.js +1 -2
- package/dist/session/UsagePanel.js +1 -1
- package/dist/session/headless/host.d.ts +3 -1
- package/dist/session/headless/index.js +2 -2
- package/dist/settings/SettingsRestricted.d.ts +25 -0
- package/dist/settings/SettingsRestricted.js +44 -0
- package/dist/settings/SettingsRowsPage.js +31 -37
- package/dist/settings/headless/catalog.js +7 -6
- package/dist/settings/headless/labels.d.ts +4 -2
- package/dist/settings/headless/labels.js +4 -2
- package/dist/settings/headless/layout.js +1 -1
- package/dist/settings/headless/providers.d.ts +2 -0
- package/dist/settings/headless/search.d.ts +1 -1
- package/dist/settings/index.d.ts +2 -2
- package/dist/settings/index.js +2 -2
- package/dist/settings/rows.d.ts +51 -8
- package/dist/settings/rows.js +81 -26
- package/dist/settings/sections/enabled-list/EnabledListSection.js +3 -2
- package/dist/settings/sections/environments/EnvironmentsSection.js +4 -3
- package/dist/settings/sections/extensions/ExtensionConfigRows.d.ts +2 -2
- package/dist/settings/sections/extensions/ExtensionConfigRows.js +22 -25
- package/dist/settings/sections/extensions/ExtensionOptionsSection.js +3 -2
- package/dist/settings/sections/extensions/options-labels.d.ts +2 -2
- package/dist/settings/sections/extensions/options-labels.js +2 -2
- package/dist/settings/sections/host-catalog/HostCatalogSection.d.ts +5 -3
- package/dist/settings/sections/host-catalog/HostCatalogSection.js +7 -4
- package/dist/settings/sections/instructions/InstructionsSection.js +24 -17
- package/dist/settings/sections/logs/LogsSection.d.ts +2 -1
- package/dist/settings/sections/logs/LogsSection.js +64 -56
- package/dist/settings/sections/mcp/McpSection.d.ts +1 -1
- package/dist/settings/sections/mcp/McpSection.js +18 -17
- package/dist/settings/sections/models/ModelRows.d.ts +3 -3
- package/dist/settings/sections/models/ModelRows.js +8 -7
- package/dist/settings/sections/performance/PerformanceSection.js +13 -4
- package/dist/settings/sections/performance/labels.d.ts +0 -1
- package/dist/settings/sections/performance/labels.js +0 -1
- package/dist/settings/sections/providers/CredentialSources.js +18 -8
- package/dist/settings/sections/providers/ProvidersSection.d.ts +2 -2
- package/dist/settings/sections/providers/ProvidersSection.js +20 -17
- package/dist/settings/sections/providers/ServerProvidersSection.d.ts +1 -1
- package/dist/settings/sections/providers/ServerProvidersSection.js +10 -14
- package/dist/settings/sections/shared/catalog.js +3 -2
- package/dist/settings/sections/skills/SkillList.js +3 -2
- package/dist/settings/testing/accounts-demo.js +1 -0
- package/dist/settings/testing/accounts.contract.js +2 -0
- package/dist/styles.css +160 -24
- package/dist/terminal/HostTerminalSurface.js +1 -1
- package/dist/terminal/headless/links.d.ts +19 -1
- package/dist/terminal/headless/links.js +26 -2
- package/dist/theme/css-vars.js +1 -0
- package/dist/theme/derive.d.ts +4 -0
- package/dist/theme/derive.js +2 -0
- package/dist/workspace/PanelContentView.js +1 -1
- package/dist/workspace/PanelHome.d.ts +3 -1
- package/dist/workspace/PanelHome.js +4 -3
- package/dist/workspace/SidePanel.d.ts +3 -1
- package/dist/workspace/SidePanel.js +3 -2
- package/dist/workspace/headless/open-target.d.ts +1 -1
- package/dist/workspace/headless/open-target.js +2 -2
- package/package.json +2 -2
- package/src/styles/chat.css +10 -10
- package/src/styles/motion.css +37 -0
- package/src/styles/primitives.css +11 -3
- package/src/styles/sidebar.css +12 -0
- package/src/styles/tokens.css +6 -0
- package/src/styles/transcript.css +69 -5
|
@@ -24,7 +24,7 @@ import { usePromptGhost } from "./use-prompt-ghost.js";
|
|
|
24
24
|
import { useStagedAttachments } from "./use-staged-attachments.js";
|
|
25
25
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
26
26
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
27
|
-
import { createSkillsCompletionProvider, uniqueSkillNamesFromReferences } from "zidane/chat/pure";
|
|
27
|
+
import { createSkillsCompletionProvider, otherDelivery, uniqueSkillNamesFromReferences } from "zidane/chat/pure";
|
|
28
28
|
import { EditorContent, useEditorState } from "@tiptap/react";
|
|
29
29
|
//#region src/chat/rich-composer/RichComposer.tsx
|
|
30
30
|
/**
|
|
@@ -62,12 +62,17 @@ const DRAFT_HAS_TEXT = "\0draft";
|
|
|
62
62
|
*
|
|
63
63
|
* @param props - What the composer edits and sends, and its slots.
|
|
64
64
|
*/
|
|
65
|
-
function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null, hostId = null, running = false, onStop, onSend, leadingActions, trailingActions, badges, searchStatus = null, onSearchQueryChange, onSearchNavigate, searchOverlayOpen = false, remoteSearchApply = null, attachmentsEnabled = true, inlineAutocomplete = false, promptSuggestions = false, scopeKey = null, commandArgs, siteIcons, attachmentLabel, textPreview, footer, className, style }) {
|
|
65
|
+
function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null, hostId = null, running = false, messageDelivery = "queue", onStop, onSend, leadingActions, trailingActions, badges, searchStatus = null, onSearchQueryChange, onSearchNavigate, searchOverlayOpen = false, remoteSearchApply = null, attachmentsEnabled = true, inlineAutocomplete = false, promptSuggestions = false, scopeKey = null, commandArgs, siteIcons, attachmentLabel, textPreview, footer, className, style }) {
|
|
66
66
|
const { completions, autocomplete } = useComposerHost();
|
|
67
67
|
const labels = useComposerLabels();
|
|
68
68
|
const history = usePromptHistory();
|
|
69
69
|
const [historyStatus, setHistoryStatus] = useState(null);
|
|
70
70
|
const editorRef = useRef(null);
|
|
71
|
+
const runningPlaceholder = running ? messageDelivery === "steer" ? labels.placeholderSteer : labels.placeholderQueue : null;
|
|
72
|
+
const runningPlaceholderRef = useRef(runningPlaceholder);
|
|
73
|
+
useLayoutEffect(() => {
|
|
74
|
+
runningPlaceholderRef.current = runningPlaceholder;
|
|
75
|
+
});
|
|
71
76
|
const liveEditor = () => {
|
|
72
77
|
const ed = editorRef.current;
|
|
73
78
|
return ed && !ed.isDestroyed ? ed : null;
|
|
@@ -152,7 +157,8 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
152
157
|
history,
|
|
153
158
|
activeSessionId,
|
|
154
159
|
hostId,
|
|
155
|
-
|
|
160
|
+
runningPlaceholderRef,
|
|
161
|
+
send: (alternate) => send(alternate),
|
|
156
162
|
onSearchNavigate,
|
|
157
163
|
applyMode,
|
|
158
164
|
applyGhost,
|
|
@@ -163,6 +169,19 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
163
169
|
useEffect(() => {
|
|
164
170
|
editorRef.current = editor;
|
|
165
171
|
}, [editor]);
|
|
172
|
+
const focusOnCreate = useRef(false);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!editor) return;
|
|
175
|
+
const onCreate = () => {
|
|
176
|
+
if (!focusOnCreate.current) return;
|
|
177
|
+
focusOnCreate.current = false;
|
|
178
|
+
editor.commands.focus("end");
|
|
179
|
+
};
|
|
180
|
+
editor.on("create", onCreate);
|
|
181
|
+
return () => {
|
|
182
|
+
editor.off("create", onCreate);
|
|
183
|
+
};
|
|
184
|
+
}, [editor]);
|
|
166
185
|
useEffect(() => {
|
|
167
186
|
if (!editor || editor.isDestroyed) return;
|
|
168
187
|
const scope = () => ({
|
|
@@ -199,7 +218,7 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
199
218
|
setHistoryStatus(null);
|
|
200
219
|
if (activeSessionId) history.reset(activeSessionId);
|
|
201
220
|
}, [activeSessionId, history]);
|
|
202
|
-
const send = () => {
|
|
221
|
+
const send = (alternate = false) => {
|
|
203
222
|
if (!editor) return;
|
|
204
223
|
const raw = editorJsonToMarkdown(editor.getJSON(), { blockGap: "\n" }).trim();
|
|
205
224
|
const text = composeSubmitText(modeRef.current, raw);
|
|
@@ -210,7 +229,7 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
210
229
|
replaceAttachments([]);
|
|
211
230
|
mirror.dropStash();
|
|
212
231
|
setHistoryStatus(null);
|
|
213
|
-
dispatch(text, submitted);
|
|
232
|
+
dispatch(text, submitted, running ? { delivery: alternate ? otherDelivery(messageDelivery) : messageDelivery } : void 0);
|
|
214
233
|
};
|
|
215
234
|
const sendText = (raw) => {
|
|
216
235
|
const text = raw.trim();
|
|
@@ -276,7 +295,7 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
276
295
|
useEffect(() => {
|
|
277
296
|
const ed = editorRef.current;
|
|
278
297
|
if (ed && !ed.isDestroyed) ed.view.dispatch(ed.state.tr);
|
|
279
|
-
}, [composerMode]);
|
|
298
|
+
}, [composerMode, runningPlaceholder]);
|
|
280
299
|
useEffect(() => {
|
|
281
300
|
if (modeRef.current === "search") liveEditor()?.commands.clearContent(true);
|
|
282
301
|
live.current.applyMode("normal");
|
|
@@ -315,7 +334,9 @@ function RichComposer({ disabled, folderPath, fileRoot, activeSessionId = null,
|
|
|
315
334
|
liveEditor()?.chain().focus("end").insertContent(text).run();
|
|
316
335
|
},
|
|
317
336
|
focus: () => {
|
|
318
|
-
liveEditor()
|
|
337
|
+
const ed = liveEditor();
|
|
338
|
+
if (ed?.isInitialized) ed.commands.focus("end");
|
|
339
|
+
else focusOnCreate.current = true;
|
|
319
340
|
},
|
|
320
341
|
prompt: sendText,
|
|
321
342
|
draft: ({ text, attachments: next }) => {
|
|
@@ -32,6 +32,8 @@ interface ComposerExtensionsContext {
|
|
|
32
32
|
modeRef: RefObject<ComposerMode>;
|
|
33
33
|
/** Re-evaluate the ghost after a gate input flipped without an edit. */
|
|
34
34
|
ghostGateChangedRef: RefObject<() => void>;
|
|
35
|
+
/** The placeholder while a run works (what Enter and Alt+Enter do), `null` otherwise. */
|
|
36
|
+
runningPlaceholderRef: RefObject<string | null>;
|
|
35
37
|
labels: ComposerLabels;
|
|
36
38
|
}
|
|
37
39
|
/** The editor's extensions. */
|
|
@@ -28,7 +28,7 @@ const MentionChat = Mention.extend({ name: "mentionChat" });
|
|
|
28
28
|
const MAX_RESULTS = 8;
|
|
29
29
|
/** The editor's extensions. */
|
|
30
30
|
function composerExtensions(ctx) {
|
|
31
|
-
const { disabled, folderPath, extensionCompletionProviders, extensionItems, sources, filesProvider, slashSource, chatsProvider, chatScopeRef, mentionPopovers, promptGhost, modeRef, ghostGateChangedRef, labels } = ctx;
|
|
31
|
+
const { disabled, folderPath, extensionCompletionProviders, extensionItems, sources, filesProvider, slashSource, chatsProvider, chatScopeRef, mentionPopovers, promptGhost, modeRef, ghostGateChangedRef, runningPlaceholderRef, labels } = ctx;
|
|
32
32
|
const chatScopeLabel = (scope) => scope === "project" ? labels.chatScopeProject : labels.chatScopeAll;
|
|
33
33
|
return [
|
|
34
34
|
StarterKit.configure({
|
|
@@ -54,7 +54,7 @@ function composerExtensions(ctx) {
|
|
|
54
54
|
if (promptGhost.painted()) return "";
|
|
55
55
|
if (modeRef.current === "shell") return labels.placeholderShell;
|
|
56
56
|
if (modeRef.current === "search") return labels.placeholderSearch;
|
|
57
|
-
return labels.placeholderChat;
|
|
57
|
+
return runningPlaceholderRef.current ?? labels.placeholderChat;
|
|
58
58
|
}
|
|
59
59
|
}),
|
|
60
60
|
Mention.configure({
|
|
@@ -87,7 +87,7 @@ function composerEditorProps(latest, name) {
|
|
|
87
87
|
if (event.key === "Enter" && !mentionPopovers.open) {
|
|
88
88
|
event.preventDefault();
|
|
89
89
|
if (event.shiftKey) editorRef.current?.chain().focus().splitBlock().run();
|
|
90
|
-
else latest.current.send();
|
|
90
|
+
else latest.current.send(event.altKey);
|
|
91
91
|
return true;
|
|
92
92
|
}
|
|
93
93
|
if ((event.key === "ArrowUp" || event.key === "ArrowDown") && !event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey && !mentionPopovers.open) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { claimComposerFocus, onComposerAttachment, onComposerDraft, onComposerFocus, onComposerInsert, onComposerMention, onComposerPrompt, registerComposerScope } from "../composer/composer-bus.js";
|
|
1
|
+
import { claimComposerFocus, onComposerAttachment, onComposerDraft, onComposerFocus, onComposerInsert, onComposerMention, onComposerPrompt, registerComposerScope, takePendingComposerFocus } from "../composer/composer-bus.js";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
3
3
|
//#region src/chat/rich-composer/use-composer-bus.ts
|
|
4
4
|
/**
|
|
@@ -23,6 +23,7 @@ function useComposerBus(key, handlers) {
|
|
|
23
23
|
onComposerPrompt(key, (text) => latest.current.prompt(text)),
|
|
24
24
|
onComposerDraft(key, (draft) => latest.current.draft(draft))
|
|
25
25
|
];
|
|
26
|
+
if (takePendingComposerFocus()) latest.current.focus();
|
|
26
27
|
return () => {
|
|
27
28
|
for (const off of offs) off();
|
|
28
29
|
};
|
|
@@ -23,6 +23,8 @@ interface ComposerEditorOptions {
|
|
|
23
23
|
/** The live editor (the one this hook returns, once pinned). */
|
|
24
24
|
editorRef: RefObject<Editor | null>;
|
|
25
25
|
modeRef: RefObject<ComposerMode>;
|
|
26
|
+
/** The placeholder while a run works, `null` otherwise. */
|
|
27
|
+
runningPlaceholderRef: RefObject<string | null>;
|
|
26
28
|
/** The inline ghost on display. */
|
|
27
29
|
ghostRef: RefObject<InlineGhost | null>;
|
|
28
30
|
/** True while a history recall's own transactions run (no ghost for them). */
|
|
@@ -33,7 +35,8 @@ interface ComposerEditorOptions {
|
|
|
33
35
|
history: ReturnType<typeof usePromptHistory>;
|
|
34
36
|
activeSessionId: string | null;
|
|
35
37
|
hostId: string | null;
|
|
36
|
-
|
|
38
|
+
/** Send the draft. `alternate`: Alt+Enter, the other delivery while a run works. */
|
|
39
|
+
send(alternate?: boolean): void;
|
|
37
40
|
onSearchNavigate: ((direction: 1 | -1) => void) | undefined;
|
|
38
41
|
applyMode(next: ComposerMode): void;
|
|
39
42
|
applyGhost(next: InlineGhost | null): void;
|
|
@@ -18,7 +18,7 @@ import { useEditor } from "@tiptap/react";
|
|
|
18
18
|
* state through the refs it passes.
|
|
19
19
|
*/
|
|
20
20
|
function useComposerEditor(options) {
|
|
21
|
-
const { disabled, folderPath, extensionCompletionProviders, sources, skillsProvider, mentionPopovers, promptGhost, modeRef, ghostGateChangedRef } = options;
|
|
21
|
+
const { disabled, folderPath, extensionCompletionProviders, sources, skillsProvider, mentionPopovers, promptGhost, modeRef, runningPlaceholderRef, ghostGateChangedRef } = options;
|
|
22
22
|
const completions = useComposerHost().completions;
|
|
23
23
|
const labels = useComposerLabels();
|
|
24
24
|
const latest = useRef(options);
|
|
@@ -85,6 +85,7 @@ function useComposerEditor(options) {
|
|
|
85
85
|
promptGhost,
|
|
86
86
|
modeRef,
|
|
87
87
|
ghostGateChangedRef,
|
|
88
|
+
runningPlaceholderRef,
|
|
88
89
|
labels
|
|
89
90
|
}),
|
|
90
91
|
editable: !disabled,
|
|
@@ -121,7 +121,7 @@ function NewSessionButton({ onNewSession, isActive, className = "", style }) {
|
|
|
121
121
|
children: /* @__PURE__ */ jsx("span", { className: "i-ph-plus text-sm text-[var(--zg-text-muted)] group-data-[active=true]:text-[var(--zg-text)]" })
|
|
122
122
|
}),
|
|
123
123
|
/* @__PURE__ */ jsx("span", {
|
|
124
|
-
className: "text-sm text-[var(--zg-text-muted)] group-data-[active=true]:text-[var(--zg-text)]",
|
|
124
|
+
className: "text-sm text-[var(--zg-sidebar-label-fg,var(--zg-text-muted))] group-data-[active=true]:text-[var(--zg-text)]",
|
|
125
125
|
children: labels.newSession
|
|
126
126
|
}),
|
|
127
127
|
/* @__PURE__ */ jsx("span", { className: "flex-1" }),
|
|
@@ -314,7 +314,6 @@ function SessionSidebar(props) {
|
|
|
314
314
|
sessions: displaySessions,
|
|
315
315
|
activeSessionId,
|
|
316
316
|
onActivate,
|
|
317
|
-
onNewSession,
|
|
318
317
|
...keyboardBlocked ? { blocked: keyboardBlocked } : {}
|
|
319
318
|
});
|
|
320
319
|
const onRowSelect = useCallback((sessionId) => {
|
|
@@ -34,7 +34,7 @@ function SidebarSearchRow({ value, onChange, filter, className, style }) {
|
|
|
34
34
|
placeholder: labels.searchSessions,
|
|
35
35
|
"aria-label": labels.searchSessionsLabel,
|
|
36
36
|
spellCheck: false,
|
|
37
|
-
className: "flex-1 min-w-0 bg-transparent border-0 outline-none p-0 text-sm text-[var(--zg-text)] placeholder:text-[var(--zg-text-muted)]"
|
|
37
|
+
className: "flex-1 min-w-0 bg-transparent border-0 outline-none p-0 text-sm text-[var(--zg-text)] placeholder:text-[var(--zg-sidebar-label-fg,var(--zg-text-muted))]"
|
|
38
38
|
}),
|
|
39
39
|
value.length > 0 && /* @__PURE__ */ jsx(IconButton, {
|
|
40
40
|
icon: "i-ph-x",
|
|
@@ -12,8 +12,6 @@ interface SidebarKeyboardNavOptions<K extends string> {
|
|
|
12
12
|
activeSessionId: K | null;
|
|
13
13
|
/** Opens a session (`null`: none, the new-session surface). */
|
|
14
14
|
onActivate: (sessionId: K | null) => void;
|
|
15
|
-
/** Starts a new session (⌘N / Ctrl+N). */
|
|
16
|
-
onNewSession: () => void;
|
|
17
15
|
/** The keys belong elsewhere now (a modal is open). Read at each key. */
|
|
18
16
|
blocked?: () => boolean;
|
|
19
17
|
}
|
|
@@ -3,7 +3,9 @@ import { useCallback, useEffect, useRef } from "react";
|
|
|
3
3
|
//#region src/chat/sidebar/useSidebarKeyboardNav.ts
|
|
4
4
|
/**
|
|
5
5
|
* The sidebar's keyboard: ↑ / ↓ walk the chats in the order the list shows
|
|
6
|
-
* them
|
|
6
|
+
* them. ⌘N / Ctrl+N is the `newSession` key command, not this hook's: the
|
|
7
|
+
* dispatcher fires it from the composer too, and a new session's
|
|
8
|
+
* `activeSessionId` change drops the cursor here. Only sessions: an extension's item row
|
|
7
9
|
* is skipped, since the walk opens what it lands on and an item's target may
|
|
8
10
|
* run an extension command (Tab reaches the item instead).
|
|
9
11
|
*
|
|
@@ -12,7 +14,7 @@ import { useCallback, useEffect, useRef } from "react";
|
|
|
12
14
|
* DOM, no React render in the burst, and scrolls it into view
|
|
13
15
|
* (`block: 'nearest'`). Only once the presses stop for `NAV_COMMIT_MS` does
|
|
14
16
|
* the session open (`onActivate`), so the heavy switch runs once per burst.
|
|
15
|
-
* Clicks
|
|
17
|
+
* Clicks and outside changes commit at once (`commitImmediate`).
|
|
16
18
|
*
|
|
17
19
|
* The keydown listener binds ONCE: every value it reads goes through refs, so
|
|
18
20
|
* a list refresh never re-attaches it.
|
|
@@ -29,7 +31,7 @@ const NAV_COMMIT_MS = 20;
|
|
|
29
31
|
* @returns `commitImmediate`: open a session now, cancelling a pending arrow commit.
|
|
30
32
|
*/
|
|
31
33
|
function useSidebarKeyboardNav(options) {
|
|
32
|
-
const { listRef, sessions, activeSessionId, onActivate,
|
|
34
|
+
const { listRef, sessions, activeSessionId, onActivate, blocked } = options;
|
|
33
35
|
const navRef = useRef({
|
|
34
36
|
tentativeId: null,
|
|
35
37
|
commitTimer: null
|
|
@@ -37,13 +39,11 @@ function useSidebarKeyboardNav(options) {
|
|
|
37
39
|
const latest = useRef({
|
|
38
40
|
sessions,
|
|
39
41
|
onActivate,
|
|
40
|
-
onNewSession,
|
|
41
42
|
blocked
|
|
42
43
|
});
|
|
43
44
|
latest.current = {
|
|
44
45
|
sessions,
|
|
45
46
|
onActivate,
|
|
46
|
-
onNewSession,
|
|
47
47
|
blocked
|
|
48
48
|
};
|
|
49
49
|
const activeSessionIdRef = useRef(activeSessionId);
|
|
@@ -85,7 +85,7 @@ function useSidebarKeyboardNav(options) {
|
|
|
85
85
|
}, NAV_COMMIT_MS);
|
|
86
86
|
}, [applyTentative, cancelCommitTimer]);
|
|
87
87
|
/**
|
|
88
|
-
* Synchronous commit (clicks,
|
|
88
|
+
* Synchronous commit (clicks, the New session button). Updates the ref BEFORE the state setter so a keydown in the same tick
|
|
89
89
|
* reads the new value.
|
|
90
90
|
*/
|
|
91
91
|
const commitImmediate = useCallback((id) => {
|
|
@@ -107,12 +107,6 @@ function useSidebarKeyboardNav(options) {
|
|
|
107
107
|
function onKey(e) {
|
|
108
108
|
if (latest.current.blocked?.()) return;
|
|
109
109
|
if (keyScopeOf(e) !== "app") return;
|
|
110
|
-
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "n") {
|
|
111
|
-
e.preventDefault();
|
|
112
|
-
commitImmediate(null);
|
|
113
|
-
latest.current.onNewSession();
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
110
|
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
|
|
117
111
|
const list = latest.current.sessions;
|
|
118
112
|
if (list.length === 0) return;
|
|
@@ -126,7 +120,7 @@ function useSidebarKeyboardNav(options) {
|
|
|
126
120
|
}
|
|
127
121
|
window.addEventListener("keydown", onKey);
|
|
128
122
|
return () => window.removeEventListener("keydown", onKey);
|
|
129
|
-
}, [commitDebounced
|
|
123
|
+
}, [commitDebounced]);
|
|
130
124
|
return { commitImmediate };
|
|
131
125
|
}
|
|
132
126
|
//#endregion
|
|
@@ -6,7 +6,7 @@ import { BadgeActionEffect, BadgeMenuItem, ExtensionReferenceView, ReferenceProv
|
|
|
6
6
|
import { InteractionRequest } from "zidane/chat";
|
|
7
7
|
//#region src/chat/testing/contract.d.ts
|
|
8
8
|
/** An optional `ChatHost` member, by its path. */
|
|
9
|
-
type ChatHostCapability = 'sessions' | 'sessions.create' | 'sessions.rename' | 'sessions.setArchived' | 'sessions.setFavorite' | 'sessions.remove' | 'sessions.query' | 'sessions.setSpace' | 'sessions.search' | 'sessions.onChanged' | 'sessions.views' | 'sessions.main' | 'send' | 'interrupt' | 'shell' | 'providerReady' | '
|
|
9
|
+
type ChatHostCapability = 'sessions' | 'sessions.create' | 'sessions.rename' | 'sessions.setArchived' | 'sessions.setFavorite' | 'sessions.remove' | 'sessions.query' | 'sessions.setSpace' | 'sessions.search' | 'sessions.onChanged' | 'sessions.views' | 'sessions.main' | 'send' | 'interrupt' | 'shell' | 'providerReady' | 'interactions' | 'interactions.cancel' | 'interactions.all' | 'models' | 'toolOutput' | 'cancelTool' | 'searchSync' | 'references' | 'paths' | 'chips' | 'chips.commit' | 'chips.sessions' | 'chips.context' | 'chips.skills' | 'chips.commands' | 'badges' | 'siteIcons' | 'blockRuns';
|
|
10
10
|
/** One thing the backend does while it runs a prompt, in order. */
|
|
11
11
|
type ChatHostScriptStep =
|
|
12
12
|
/** Stream a markdown reply with this text. */
|
|
@@ -19,8 +19,6 @@ const CAPABILITIES = [
|
|
|
19
19
|
"interrupt",
|
|
20
20
|
"shell",
|
|
21
21
|
"providerReady",
|
|
22
|
-
"steer",
|
|
23
|
-
"followUp",
|
|
24
22
|
"interactions",
|
|
25
23
|
"interactions.cancel",
|
|
26
24
|
"interactions.all",
|
|
@@ -339,18 +337,21 @@ function describeChatHostContract(name, makeHarness, capabilities) {
|
|
|
339
337
|
await waitFor(() => expect(t.get().running).toBe(false));
|
|
340
338
|
if (has("sessions")) await waitFor(() => expect(list().find((s) => s.id === "c-2")?.status).not.toBe("running"));
|
|
341
339
|
});
|
|
342
|
-
it.skipIf(!has("send", "interrupt"
|
|
340
|
+
it.skipIf(!has("send", "interrupt"))("a steer sent while a run works joins that run", async () => {
|
|
343
341
|
const { host, watch } = await setup();
|
|
344
342
|
const t = await watch("c-2");
|
|
345
|
-
expect(await host.steer("c-2", "nudge")).toBe(false);
|
|
346
|
-
expect(await host.followUp("c-2", "later")).toBe(false);
|
|
347
343
|
host.send({
|
|
348
344
|
sessionId: "c-2",
|
|
349
345
|
prompt: "hold"
|
|
350
346
|
}).catch(() => {});
|
|
351
347
|
await waitFor(() => expect(t.get().running).toBe(true));
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
await host.send({
|
|
349
|
+
sessionId: "c-2",
|
|
350
|
+
prompt: "nudge",
|
|
351
|
+
delivery: "steer"
|
|
352
|
+
});
|
|
353
|
+
await waitFor(() => expect(t.get().events.some((e) => e.kind === "user-prompt" && e.text === "nudge")).toBe(true));
|
|
354
|
+
expect(t.get().running).toBe(true);
|
|
354
355
|
host.interrupt("c-2");
|
|
355
356
|
await waitFor(() => expect(t.get().running).toBe(false));
|
|
356
357
|
});
|
|
@@ -22,6 +22,13 @@ interface BadgeMenuSystem {
|
|
|
22
22
|
/** Shows a path in the file manager. */
|
|
23
23
|
revealPath(path: string): Promise<boolean>;
|
|
24
24
|
}
|
|
25
|
+
/** Where web links open: the default browser, and the app's own browser pane. */
|
|
26
|
+
interface BadgeMenuBrowser {
|
|
27
|
+
/** The default browser's name ("Arc"): the row reads "Open in Arc". `null` when unknown: "Open in browser". */
|
|
28
|
+
name: string | null;
|
|
29
|
+
/** Opens a web link in the app's browser pane. Absent: no "Open in browser pane" row. */
|
|
30
|
+
openInPane?: ((url: string) => void) | undefined;
|
|
31
|
+
}
|
|
25
32
|
/** What {@link applyBadgeEffect} applies an effect with. */
|
|
26
33
|
interface BadgeEffectContext {
|
|
27
34
|
/** The badge acted on. */
|
|
@@ -45,19 +52,21 @@ interface BadgeMenuProviderProps {
|
|
|
45
52
|
composer?: BadgeMenuComposer;
|
|
46
53
|
/** Opens and reveals paths. Absent: path actions answer "Path unavailable." */
|
|
47
54
|
system?: BadgeMenuSystem;
|
|
55
|
+
/** The default browser's name and the browser pane. Absent: "Open in browser" only. */
|
|
56
|
+
browser?: BadgeMenuBrowser;
|
|
48
57
|
/** The surfaces whose badges open the menu. */
|
|
49
58
|
children: ReactNode;
|
|
50
59
|
}
|
|
51
60
|
/**
|
|
52
61
|
* Provides the badge menu to the badges below it and renders it (and its notice) when one opens.
|
|
53
62
|
*
|
|
54
|
-
* @param props - The composer
|
|
63
|
+
* @param props - The composer, the path opener and the browser the effects use, and the children.
|
|
55
64
|
*/
|
|
56
|
-
declare function BadgeMenuProvider({ composer, system, children }: BadgeMenuProviderProps): import("react").JSX.Element;
|
|
65
|
+
declare function BadgeMenuProvider({ composer, system, browser, children }: BadgeMenuProviderProps): import("react").JSX.Element;
|
|
57
66
|
/**
|
|
58
67
|
* Opens the badge menu for a badge. Outside the provider (a test, a detached
|
|
59
68
|
* surface) the event is left alone, so the surrounding menu still opens.
|
|
60
69
|
*/
|
|
61
70
|
declare function useBadgeMenu(): OpenBadgeMenu | null;
|
|
62
71
|
//#endregion
|
|
63
|
-
export { BadgeEffectContext, BadgeMenuComposer, BadgeMenuProvider, BadgeMenuProviderProps, BadgeMenuSystem, OpenBadgeMenu, useBadgeMenu };
|
|
72
|
+
export { BadgeEffectContext, BadgeMenuBrowser, BadgeMenuComposer, BadgeMenuProvider, BadgeMenuProviderProps, BadgeMenuSystem, OpenBadgeMenu, useBadgeMenu };
|
|
@@ -23,6 +23,8 @@ import { builtinBadgeActions, builtinBadgeEffect, mergeBadgeMenu } from "zidane/
|
|
|
23
23
|
* the turn's own menu never opens over a badge.
|
|
24
24
|
*/
|
|
25
25
|
const BadgeMenuContext = createContext(null);
|
|
26
|
+
/** The kit's own row next to "Open in browser", shown when the host has a browser pane. */
|
|
27
|
+
const OPEN_IN_PANE = "open-browser-pane";
|
|
26
28
|
const NOTICE_MS = 2400;
|
|
27
29
|
/** `abs` relative to `cwd` when inside it: the form the composer's `@` mentions use. */
|
|
28
30
|
function relativeTo(abs, cwd) {
|
|
@@ -58,9 +60,9 @@ async function applyBadgeEffect(effect, ctx) {
|
|
|
58
60
|
/**
|
|
59
61
|
* Provides the badge menu to the badges below it and renders it (and its notice) when one opens.
|
|
60
62
|
*
|
|
61
|
-
* @param props - The composer
|
|
63
|
+
* @param props - The composer, the path opener and the browser the effects use, and the children.
|
|
62
64
|
*/
|
|
63
|
-
function BadgeMenuProvider({ composer, system, children }) {
|
|
65
|
+
function BadgeMenuProvider({ composer, system, browser, children }) {
|
|
64
66
|
const extensions = useExtensionsHost();
|
|
65
67
|
const badges = useOptionalChatHost()?.badges ?? extensionsBadgeActions(extensions);
|
|
66
68
|
const labels = useChatLabels();
|
|
@@ -98,7 +100,17 @@ function BadgeMenuProvider({ composer, system, children }) {
|
|
|
98
100
|
const items = useMemo(() => {
|
|
99
101
|
if (!open) return [];
|
|
100
102
|
const { target, cwd, at } = open;
|
|
101
|
-
|
|
103
|
+
const builtins = builtinBadgeActions(target).flatMap((item) => item.id !== "open-browser" ? [item] : [browser?.name ? {
|
|
104
|
+
...item,
|
|
105
|
+
label: labels.badgeOpenIn(browser.name)
|
|
106
|
+
} : item, ...browser?.openInPane ? [{
|
|
107
|
+
id: OPEN_IN_PANE,
|
|
108
|
+
label: labels.badgeOpenInBrowserPane,
|
|
109
|
+
group: "open",
|
|
110
|
+
shortcut: "B",
|
|
111
|
+
source: "builtin"
|
|
112
|
+
}] : []]);
|
|
113
|
+
return mergeBadgeMenu([...builtins, ...open.extra]).map((item) => ({
|
|
102
114
|
id: `${item.source}:${item.id}`,
|
|
103
115
|
label: item.label,
|
|
104
116
|
...item.shortcut ? { shortcut: item.shortcut } : {},
|
|
@@ -107,6 +119,10 @@ function BadgeMenuProvider({ composer, system, children }) {
|
|
|
107
119
|
title: labels.badgeNoComposer
|
|
108
120
|
} : {},
|
|
109
121
|
onSelect: () => {
|
|
122
|
+
if (item.source === "builtin" && item.id === OPEN_IN_PANE) {
|
|
123
|
+
if (target.href) browser?.openInPane?.(target.href);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
110
126
|
(async () => {
|
|
111
127
|
const text = await applyBadgeEffect(item.source === "builtin" ? builtinBadgeEffect(item.id, target) ?? {} : await (badges?.run(cwd, item.source, item.id, target) ?? Promise.resolve({})).catch((err) => ({ notice: err instanceof Error ? err.message : String(err) })), {
|
|
112
128
|
target,
|
|
@@ -130,6 +146,7 @@ function BadgeMenuProvider({ composer, system, children }) {
|
|
|
130
146
|
badges,
|
|
131
147
|
composer,
|
|
132
148
|
system,
|
|
149
|
+
browser,
|
|
133
150
|
labels,
|
|
134
151
|
links
|
|
135
152
|
]);
|