zidane-ui 9.1.13 → 9.1.16
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/index.d.ts +2 -2
- package/dist/adapters/index.js +2 -2
- package/dist/adapters/live.d.ts +29 -3
- package/dist/adapters/live.js +9 -1
- package/dist/adapters/server-composer-host.js +17 -2
- package/dist/adapters/server-extensions-host.d.ts +0 -1
- package/dist/adapters/server-extensions-host.js +4 -15
- package/dist/adapters/server-git-host.js +7 -16
- package/dist/adapters/server-host.js +181 -43
- package/dist/adapters/server-machines-host.d.ts +2 -0
- package/dist/adapters/server-machines-host.js +22 -23
- package/dist/adapters/server-onboarding-host.d.ts +1 -1
- package/dist/adapters/server-onboarding-host.js +3 -4
- package/dist/adapters/server-session-host.js +22 -3
- package/dist/adapters/server-settings-accounts.js +2 -2
- package/dist/adapters/server-settings-host.js +2 -2
- package/dist/adapters/server-settings-performance.js +7 -9
- package/dist/adapters/server-settings-sections.js +13 -14
- package/dist/adapters/server-sidebar-host.js +2 -2
- package/dist/adapters/sse-host.js +68 -3
- package/dist/adapters/sse-protocol.d.ts +12 -2
- package/dist/adapters/sse-protocol.js +4 -2
- package/dist/app/AppHostProvider.js +4 -3
- package/dist/app/ModalProvider.d.ts +13 -3
- package/dist/app/ModalProvider.js +103 -24
- package/dist/app/NoticeStack.d.ts +33 -0
- package/dist/app/NoticeStack.js +90 -0
- package/dist/app/index.d.ts +3 -2
- package/dist/app/index.js +3 -2
- package/dist/chat/SessionFilterMenu.d.ts +1 -1
- package/dist/chat/SessionFilterMenu.js +2 -1
- package/dist/chat/composer/Composer.js +10 -8
- package/dist/chat/composer/ModelChainList.js +17 -8
- package/dist/chat/composer/ModelChainTooltip.js +5 -2
- package/dist/chat/composer/use-draft-target.d.ts +20 -1
- package/dist/chat/composer/use-draft-target.js +121 -5
- package/dist/chat/composer/use-new-session-defaults.d.ts +5 -1
- package/dist/chat/composer/use-new-session-defaults.js +17 -4
- package/dist/chat/headless/chip-labels.d.ts +1 -0
- package/dist/chat/headless/chip-labels.js +1 -0
- package/dist/chat/headless/composer/labels-editor.d.ts +0 -2
- package/dist/chat/headless/composer/labels-editor.js +0 -2
- package/dist/chat/headless/draft-target.d.ts +21 -1
- package/dist/chat/headless/fixture-host.js +17 -2
- package/dist/chat/headless/host.d.ts +3 -3
- package/dist/chat/headless/index.d.ts +9 -9
- package/dist/chat/headless/index.js +2 -2
- package/dist/chat/headless/labels.d.ts +13 -6
- package/dist/chat/headless/labels.js +10 -9
- package/dist/chat/headless/session-list.d.ts +20 -8
- package/dist/chat/headless/session-list.js +1 -0
- package/dist/chat/headless/sidebar/directory.d.ts +56 -14
- package/dist/chat/headless/sidebar/directory.js +149 -70
- package/dist/chat/headless/sidebar/index.d.ts +3 -3
- package/dist/chat/headless/sidebar/index.js +2 -2
- package/dist/chat/headless/sidebar/labels.d.ts +15 -8
- package/dist/chat/headless/sidebar/types.d.ts +3 -1
- package/dist/chat/headless/sidebar/view.d.ts +13 -5
- package/dist/chat/headless/sidebar/view.js +31 -11
- package/dist/chat/headless/streams/actions.d.ts +5 -3
- package/dist/chat/headless/streams/actions.js +3 -2
- package/dist/chat/headless/streams/index.d.ts +3 -3
- package/dist/chat/headless/streams/merge.d.ts +6 -10
- package/dist/chat/headless/streams/merge.js +7 -21
- package/dist/chat/headless/streams/session-streams.d.ts +29 -4
- package/dist/chat/headless/streams/session-streams.js +86 -0
- package/dist/chat/headless/streams/store.d.ts +162 -16
- package/dist/chat/headless/streams/store.js +334 -38
- package/dist/chat/headless/streams/types.d.ts +10 -7
- package/dist/chat/headless/transcript-labels.d.ts +1 -0
- package/dist/chat/interactions/PlanCard.js +10 -3
- package/dist/chat/interactions/QuestionForm.js +7 -3
- package/dist/chat/interactions/card-ui.d.ts +5 -3
- package/dist/chat/interactions/card-ui.js +9 -7
- package/dist/chat/rich-composer/RichComposer.js +1 -7
- package/dist/chat/rich-composer/composer-extensions.d.ts +0 -2
- package/dist/chat/rich-composer/composer-extensions.js +2 -2
- package/dist/chat/rich-composer/use-composer-editor.d.ts +0 -2
- package/dist/chat/rich-composer/use-composer-editor.js +1 -2
- package/dist/chat/sidebar/SessionSidebar.js +5 -5
- package/dist/chat/sidebar/SidebarFilterMenu.js +3 -1
- package/dist/chat/sidebar/SidebarFrame.d.ts +4 -0
- package/dist/chat/sidebar/SidebarFrame.js +27 -9
- package/dist/chat/sidebar/SidebarGroupHeader.js +1 -1
- package/dist/chat/sidebar/SidebarHostProvider.d.ts +8 -5
- package/dist/chat/sidebar/SidebarHostProvider.js +13 -9
- package/dist/chat/sidebar/SidebarToggleButton.js +7 -3
- package/dist/chat/sidebar/index.d.ts +2 -2
- package/dist/chat/sidebar/session-menu.js +4 -4
- package/dist/chat/sidebar/useSaveViewAsSpace.js +2 -2
- package/dist/chat/testing/contract.js +2 -2
- package/dist/chat/transcript/AuthFailureCard.js +11 -8
- package/dist/chat/transcript/BadgeMenu.js +13 -2
- package/dist/chat/transcript/MermaidDiagram.js +7 -3
- package/dist/chat/transcript/fold.d.ts +4 -0
- package/dist/chat/transcript/fold.js +53 -22
- package/dist/chat/transcript/script-input.d.ts +1 -1
- package/dist/chat/transcript/shell-block.js +7 -3
- package/dist/chat/transcript/useStickToBottom.js +12 -0
- package/dist/components/BrowserSignIn.js +23 -21
- package/dist/components/Form.d.ts +6 -4
- package/dist/components/Form.js +16 -8
- package/dist/components/LoadError.d.ts +4 -2
- package/dist/components/LoadError.js +13 -9
- package/dist/components/Notice.d.ts +29 -0
- package/dist/components/Notice.js +63 -0
- package/dist/components/PanelEmpty.d.ts +1 -1
- package/dist/components/PanelEmpty.js +4 -2
- package/dist/components/Restricted.d.ts +1 -1
- package/dist/components/palette/PaletteList.js +8 -1
- package/dist/components/palette/PalettePanel.js +14 -6
- package/dist/core.css +24 -5
- package/dist/dist/{index-Bq87OcGy.d.ts → index-BZM8tu4L.d.ts} +28 -3
- package/dist/dist/index.d.ts +2 -2
- package/dist/dist/{timeout-B5pO5I5a.d.ts → timeout-Cyx3VGPz.d.ts} +1 -1
- package/dist/extensions/ExtensionSettingsPage.js +1 -4
- package/dist/extensions/ExtensionSettingsSection.js +4 -3
- package/dist/extensions/ExtensionSettingsSidebar.js +2 -6
- package/dist/extensions/SettingsWireForm.d.ts +1 -7
- package/dist/extensions/SettingsWireForm.js +7 -5
- package/dist/extensions/headless/fixture-host.d.ts +1 -5
- package/dist/extensions/headless/fixture-host.js +1 -4
- package/dist/extensions/headless/host.d.ts +1 -11
- package/dist/extensions/headless/index.d.ts +3 -3
- package/dist/extensions/headless/index.js +2 -2
- package/dist/extensions/headless/select.d.ts +1 -11
- package/dist/extensions/headless/select.js +1 -18
- package/dist/files/EditorColumn.js +12 -1
- package/dist/files/TreeColumn.js +13 -20
- package/dist/files/useOpenFiles.js +2 -0
- package/dist/git/BranchMenu.js +5 -3
- package/dist/git/ImportRepoDialog.js +18 -9
- package/dist/git/MoveSessionDialog.js +15 -7
- package/dist/git/RemoteFolderPicker.js +34 -16
- package/dist/git/SyncChip.js +17 -17
- package/dist/git/WherePicker.d.ts +16 -1
- package/dist/git/WherePicker.js +80 -36
- package/dist/git/headless/environment.d.ts +33 -3
- package/dist/git/headless/environment.js +33 -5
- package/dist/git/headless/index.d.ts +2 -2
- package/dist/git/headless/index.js +2 -2
- package/dist/git/headless/labels.d.ts +15 -2
- package/dist/git/headless/labels.js +17 -4
- package/dist/git/useEnvironmentChoices.d.ts +3 -1
- package/dist/git/useEnvironmentChoices.js +6 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/labels.d.ts +5 -0
- package/dist/labels.js +5 -0
- package/dist/machines/AddMachineDialog.d.ts +34 -0
- package/dist/machines/AddMachineDialog.js +464 -0
- package/dist/machines/Banners.d.ts +33 -4
- package/dist/machines/Banners.js +64 -3
- package/dist/machines/DaemonMenu.js +27 -4
- package/dist/machines/HostCapabilities.d.ts +1 -3
- package/dist/machines/HostCapabilities.js +24 -45
- package/dist/machines/HostSyncPanel.js +123 -129
- package/dist/machines/HostsSection.d.ts +4 -1
- package/dist/machines/HostsSection.js +30 -7
- package/dist/machines/MachinesExplainer.d.ts +17 -0
- package/dist/machines/MachinesExplainer.js +71 -0
- package/dist/machines/MachinesProvider.d.ts +14 -2
- package/dist/machines/MachinesProvider.js +35 -1
- package/dist/machines/ServerAddedCard.d.ts +31 -0
- package/dist/machines/ServerAddedCard.js +72 -0
- package/dist/machines/ServerCard.js +91 -215
- package/dist/machines/ServerMachinesPanel.d.ts +2 -2
- package/dist/machines/ServerMachinesPanel.js +184 -323
- package/dist/machines/ServerUpdatePanel.js +51 -40
- package/dist/machines/ShareThisComputerForm.d.ts +19 -0
- package/dist/machines/ShareThisComputerForm.js +133 -0
- package/dist/machines/TokensPanel.d.ts +5 -3
- package/dist/machines/TokensPanel.js +98 -78
- package/dist/machines/headless/fixture-host.d.ts +19 -3
- package/dist/machines/headless/fixture-host.js +42 -5
- package/dist/machines/headless/fixtures.js +4 -6
- package/dist/machines/headless/host.d.ts +40 -19
- package/dist/machines/headless/index.d.ts +3 -3
- package/dist/machines/headless/index.js +2 -2
- package/dist/machines/headless/labels.d.ts +63 -20
- package/dist/machines/headless/labels.js +88 -25
- package/dist/machines/headless/messages.js +8 -0
- package/dist/machines/headless/stores.d.ts +9 -2
- package/dist/machines/headless/stores.js +28 -1
- package/dist/machines/index.d.ts +7 -3
- package/dist/machines/index.js +8 -4
- package/dist/machines/testing/contract.d.ts +15 -3
- package/dist/machines/testing/contract.js +68 -14
- package/dist/motion/grow.d.ts +8 -1
- package/dist/motion/grow.js +10 -4
- package/dist/onboarding/demos/index.js +1 -1
- package/dist/onboarding/headless/labels.d.ts +13 -0
- package/dist/onboarding/headless/labels.js +8 -0
- package/dist/onboarding/index.js +3 -3
- package/dist/onboarding/sources/SourceBrowser.js +11 -7
- package/dist/onboarding/steps/ConnectorsStep.js +1 -1
- package/dist/onboarding/steps/ImportStep.js +4 -1
- package/dist/onboarding/steps/ReadyStep.js +9 -2
- package/dist/onboarding/steps/SkillsStep.js +1 -1
- package/dist/onboarding/steps/connect/KeyForm.js +6 -2
- package/dist/onboarding/steps/connect/ProviderPanel.js +6 -2
- package/dist/onboarding/steps/connect/ProviderReady.js +25 -20
- package/dist/onboarding/steps/tasks.js +17 -14
- package/dist/server/sse-handler.js +141 -19
- package/dist/session/ArtifactsPanel.js +39 -17
- package/dist/session/PlanTracker.js +12 -7
- package/dist/session/ProjectHome.js +13 -11
- package/dist/session/RemindersPanel.js +34 -16
- package/dist/session/RunningWorkDialog.js +6 -2
- package/dist/session/SecretsPanel.js +29 -10
- package/dist/session/SessionDetailsDialog.d.ts +1 -1
- package/dist/session/SessionDetailsDialog.js +18 -10
- package/dist/session/SideChat.js +10 -7
- package/dist/session/TurnDetailsDialog.js +19 -5
- package/dist/session/UsagePanel.js +10 -4
- package/dist/session/headless/host.d.ts +2 -0
- package/dist/session/headless/labels.d.ts +19 -1
- package/dist/session/headless/labels.js +15 -1
- package/dist/session/headless/queue.js +2 -1
- package/dist/session/headless/reminder-labels.d.ts +4 -0
- package/dist/session/headless/reminder-labels.js +4 -0
- package/dist/settings/SettingsRestricted.d.ts +1 -1
- package/dist/settings/SettingsRestricted.js +4 -4
- package/dist/settings/SettingsShell.d.ts +7 -0
- package/dist/settings/SettingsShell.js +6 -1
- package/dist/settings/headless/fixture-performance.d.ts +1 -1
- package/dist/settings/headless/host.d.ts +2 -2
- package/dist/settings/headless/labels.d.ts +3 -3
- package/dist/settings/headless/labels.js +3 -3
- package/dist/settings/headless/performance.d.ts +3 -4
- package/dist/settings/headless/search.d.ts +1 -1
- package/dist/settings/index.d.ts +2 -1
- package/dist/settings/index.js +2 -1
- package/dist/settings/sections/editor/EditorSection.js +7 -4
- package/dist/settings/sections/editor/labels.d.ts +1 -0
- package/dist/settings/sections/editor/labels.js +1 -0
- package/dist/settings/sections/environments/EnvironmentsSection.js +19 -12
- package/dist/settings/sections/environments/labels.d.ts +1 -0
- package/dist/settings/sections/environments/labels.js +2 -1
- package/dist/settings/sections/extensions/ExtensionConfigRows.d.ts +1 -3
- package/dist/settings/sections/extensions/ExtensionConfigRows.js +20 -19
- package/dist/settings/sections/extensions/ExtensionOptionsSection.js +2 -1
- package/dist/settings/sections/extensions/options-labels.d.ts +2 -1
- package/dist/settings/sections/extensions/options-labels.js +2 -1
- package/dist/settings/sections/index.d.ts +2 -1
- package/dist/settings/sections/index.js +2 -1
- package/dist/settings/sections/logs/LogsSection.d.ts +2 -2
- package/dist/settings/sections/logs/LogsSection.js +2 -2
- package/dist/settings/sections/mcp/McpSection.d.ts +1 -1
- package/dist/settings/sections/mcp/McpSection.js +12 -6
- package/dist/settings/sections/mcp/McpServerRow.js +20 -13
- package/dist/settings/sections/mcp/labels.d.ts +8 -2
- package/dist/settings/sections/mcp/labels.js +8 -2
- package/dist/settings/sections/models/ModelRows.js +1 -1
- package/dist/settings/sections/models/NewChatPlaceRow.d.ts +26 -0
- package/dist/settings/sections/models/NewChatPlaceRow.js +78 -0
- package/dist/settings/sections/models/SpacesDefaultsSection.js +15 -3
- package/dist/settings/sections/models/labels.d.ts +7 -0
- package/dist/settings/sections/models/labels.js +7 -0
- package/dist/settings/sections/models/model-test.js +7 -4
- package/dist/settings/sections/performance/PerformanceSection.js +16 -6
- package/dist/settings/sections/performance/labels.d.ts +1 -1
- package/dist/settings/sections/performance/labels.js +1 -1
- package/dist/settings/sections/providers/ProvidersSection.d.ts +1 -1
- package/dist/settings/sections/providers/ProvidersSection.js +14 -7
- package/dist/settings/sections/providers/ServerProvidersSection.d.ts +1 -1
- package/dist/settings/sections/providers/ServerProvidersSection.js +11 -5
- package/dist/settings/sections/providers/labels.d.ts +2 -2
- package/dist/settings/sections/providers/labels.js +2 -2
- package/dist/settings/sections/skills/SkillList.js +1 -1
- package/dist/settings/sections/skills/SkillsSection.js +1 -1
- package/dist/settings/testing/performance.contract.js +8 -12
- package/dist/styles.css +32 -3
- package/dist/terminal/HostTerminalSurface.js +15 -5
- package/dist/terminal/TerminalPanel.js +30 -17
- package/dist/terminal/TerminalSurface.js +11 -4
- package/dist/terminal/headless/labels.d.ts +5 -0
- package/dist/terminal/headless/labels.js +5 -0
- package/dist/terminal/headless/store.d.ts +1 -1
- package/dist/terminal/headless/store.js +6 -3
- package/dist/workspace/LayoutBudget.d.ts +5 -1
- package/dist/workspace/LayoutBudget.js +14 -1
- package/dist/workspace/PanelContentView.d.ts +1 -1
- package/dist/workspace/PanelContentView.js +17 -6
- package/dist/workspace/WorkspaceProvider.js +3 -2
- package/dist/workspace/headless/labels.d.ts +1 -0
- package/dist/workspace/headless/labels.js +1 -0
- package/package.json +3 -3
- package/src/styles/motion.css +8 -0
- package/src/styles/primitives.css +7 -0
|
@@ -133,7 +133,7 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
133
133
|
authSignedInRetrying: "Signed in · retrying",
|
|
134
134
|
authFixed: "fixed",
|
|
135
135
|
authTryAgain: "Try again",
|
|
136
|
-
|
|
136
|
+
authSignInFailed: "Sign-in didn't work.",
|
|
137
137
|
sessionEventLabel: (title, verb, needsAttention) => `${title} ${verb}${needsAttention ? ". Click to answer" : ""}`,
|
|
138
138
|
clickToAnswer: "Answer",
|
|
139
139
|
openSession: "open →",
|
|
@@ -194,7 +194,7 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
194
194
|
/** Copy button of a diagram frame. */
|
|
195
195
|
copyDiagramSource: "Copy diagram source",
|
|
196
196
|
/** Under a fence that could not be drawn; `at` is `line 3, column 7` or empty. */
|
|
197
|
-
|
|
197
|
+
diagramNotDrawn: (at) => at ? `Diagram not drawn (${at}).` : "Diagram not drawn.",
|
|
198
198
|
/** Where a diagram error is. */
|
|
199
199
|
diagramErrorAt: (line, column) => column ? `line ${line}, column ${column}` : `line ${line}`,
|
|
200
200
|
stopCommand: "Stop command",
|
|
@@ -224,7 +224,7 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
224
224
|
/** The output kept only its end. */
|
|
225
225
|
blockRunTruncated: "Earlier output was cut.",
|
|
226
226
|
/** The panel when the block could not run. */
|
|
227
|
-
|
|
227
|
+
blockRunError: "Couldn't run this block.",
|
|
228
228
|
/** After a failed run: it may have wanted input, which a terminal gives. */
|
|
229
229
|
blockRunNeedsTerminal: "Needs input? Run it in a terminal.",
|
|
230
230
|
working: "Working",
|
|
@@ -252,16 +252,15 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
252
252
|
loadingEarlierHistory: "Loading earlier history…",
|
|
253
253
|
composerLabel: "Message",
|
|
254
254
|
composerPlaceholder: "Message the agent…",
|
|
255
|
-
composerPlaceholderSteer: "Enter adds it to the run. Alt+Enter sends it when the run ends.",
|
|
256
|
-
composerPlaceholderQueue: "Enter sends it when the run ends. Alt+Enter adds it to the run.",
|
|
257
255
|
sendMessage: "Send message",
|
|
256
|
+
composerSendFailed: "Couldn't send your message.",
|
|
258
257
|
sendHint: "Send (Enter)",
|
|
259
258
|
stopRun: "Stop",
|
|
260
259
|
stopHint: "Stop the run (Esc)",
|
|
261
260
|
steerMessage: "Steer the run",
|
|
262
|
-
steerHint: "Add to the
|
|
261
|
+
steerHint: "Add to the running turn (Enter)",
|
|
263
262
|
queueMessage: "Queue message",
|
|
264
|
-
queueHint: "Send when the run ends (Enter)
|
|
263
|
+
queueHint: "Send when the run ends (Enter)",
|
|
265
264
|
attachFile: "Attach file",
|
|
266
265
|
removeAttachment: (name) => `Remove ${name}`,
|
|
267
266
|
attachments: "Attachments",
|
|
@@ -299,7 +298,7 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
299
298
|
modelsJumpToProvider: "Jump to provider",
|
|
300
299
|
modelsJumpHint: (provider, digit) => digit ? `${provider} (⌘${digit})` : provider,
|
|
301
300
|
modelsLoading: "Loading…",
|
|
302
|
-
|
|
301
|
+
modelsLoadError: "Couldn't load the models.",
|
|
303
302
|
modelsNoProviders: "No provider is signed in. Sign in under Settings → Accounts.",
|
|
304
303
|
modelsNoMatch: (query) => `No model matches "${query}".`,
|
|
305
304
|
modelsTypeAnId: "Type any model id to use it. Put a provider first to pick one, for example \"xai grok-9\".",
|
|
@@ -356,6 +355,7 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
356
355
|
inboxRecent: "Recent",
|
|
357
356
|
inboxPreviousSession: "previous session",
|
|
358
357
|
unsupportedInteraction: (tool) => `The agent is waiting on ${tool}, which this view cannot answer.`,
|
|
358
|
+
answerSendFailed: "Couldn't send your answer.",
|
|
359
359
|
planRejectNote: "Rejecting stops the run. Add a note so the agent knows why.",
|
|
360
360
|
planReviseNote: "Say what should change. The agent sends back a revised plan.",
|
|
361
361
|
planRejectPlaceholder: "Why you reject this plan (optional)",
|
|
@@ -419,7 +419,8 @@ const DEFAULT_CHAT_LABELS = {
|
|
|
419
419
|
},
|
|
420
420
|
filterSorts: {
|
|
421
421
|
recency: "Recency",
|
|
422
|
-
createdTime: "
|
|
422
|
+
createdTime: "Creation time",
|
|
423
|
+
lastUserMessage: "Last user message",
|
|
423
424
|
alphabetical: "Alphabetically"
|
|
424
425
|
},
|
|
425
426
|
filterAllProjects: "All",
|
|
@@ -13,11 +13,16 @@ interface ListableSession {
|
|
|
13
13
|
createdAt: number;
|
|
14
14
|
/** Epoch milliseconds of the last activity. Used by the `recency` sort and date grouping. */
|
|
15
15
|
updatedAt?: number;
|
|
16
|
+
/** Epoch milliseconds of the user's last typed message. Used by the `lastUserMessage` sort. If absent, `createdAt` stands in. */
|
|
17
|
+
lastUserMessageAt?: number;
|
|
16
18
|
}
|
|
17
19
|
/** How the list groups sessions: `none` (flat), `date`, `state`, or `project`. */
|
|
18
20
|
type SessionGroupBy = 'none' | 'date' | 'state' | 'project';
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* How sessions are ordered inside a group: `recency` (last activity, newest first), `createdTime` (newest first),
|
|
23
|
+
* `lastUserMessage` (the user's last typed message, newest first, else creation), or `alphabetical` by title.
|
|
24
|
+
*/
|
|
25
|
+
type SessionSortBy = 'recency' | 'createdTime' | 'lastUserMessage' | 'alphabetical';
|
|
21
26
|
/** One row of the flat list {@link buildSessionRows} returns: a group `header`, a `session`, or a `showMore` row that ends a capped group. */
|
|
22
27
|
type SessionListRow<S> = {
|
|
23
28
|
kind: 'header';
|
|
@@ -202,7 +207,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
202
207
|
authSignedInRetrying: string;
|
|
203
208
|
authFixed: string;
|
|
204
209
|
authTryAgain: string;
|
|
205
|
-
|
|
210
|
+
authSignInFailed: string;
|
|
206
211
|
sessionEventLabel: (title: string, verb: string, needsAttention: boolean) => string;
|
|
207
212
|
clickToAnswer: string;
|
|
208
213
|
openSession: string;
|
|
@@ -245,7 +250,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
245
250
|
zoomOutDiagram: string;
|
|
246
251
|
fitDiagram: string;
|
|
247
252
|
copyDiagramSource: string;
|
|
248
|
-
|
|
253
|
+
diagramNotDrawn: (at: string) => string;
|
|
249
254
|
diagramErrorAt: (line: number, column: number) => string;
|
|
250
255
|
stopCommand: string;
|
|
251
256
|
shellCancelled: string;
|
|
@@ -266,7 +271,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
266
271
|
blockRunStopped: string;
|
|
267
272
|
blockRunTimedOut: string;
|
|
268
273
|
blockRunTruncated: string;
|
|
269
|
-
|
|
274
|
+
blockRunError: string;
|
|
270
275
|
blockRunNeedsTerminal: string;
|
|
271
276
|
working: string;
|
|
272
277
|
foldSteps: (count: number) => string;
|
|
@@ -293,9 +298,8 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
293
298
|
loadingEarlierHistory: string;
|
|
294
299
|
composerLabel: string;
|
|
295
300
|
composerPlaceholder: string;
|
|
296
|
-
composerPlaceholderSteer: string;
|
|
297
|
-
composerPlaceholderQueue: string;
|
|
298
301
|
sendMessage: string;
|
|
302
|
+
composerSendFailed: string;
|
|
299
303
|
sendHint: string;
|
|
300
304
|
stopRun: string;
|
|
301
305
|
stopHint: string;
|
|
@@ -340,7 +344,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
340
344
|
modelsJumpToProvider: string;
|
|
341
345
|
modelsJumpHint: (provider: string, digit: number | null) => string;
|
|
342
346
|
modelsLoading: string;
|
|
343
|
-
|
|
347
|
+
modelsLoadError: string;
|
|
344
348
|
modelsNoProviders: string;
|
|
345
349
|
modelsNoMatch: (query: string) => string;
|
|
346
350
|
modelsTypeAnId: string;
|
|
@@ -397,6 +401,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
397
401
|
inboxRecent: string;
|
|
398
402
|
inboxPreviousSession: string;
|
|
399
403
|
unsupportedInteraction: (tool: string) => string;
|
|
404
|
+
answerSendFailed: string;
|
|
400
405
|
planRejectNote: string;
|
|
401
406
|
planReviseNote: string;
|
|
402
407
|
planRejectPlaceholder: string;
|
|
@@ -461,6 +466,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
461
466
|
filterSorts: {
|
|
462
467
|
recency: string;
|
|
463
468
|
createdTime: string;
|
|
469
|
+
lastUserMessage: string;
|
|
464
470
|
alphabetical: string;
|
|
465
471
|
};
|
|
466
472
|
filterAllProjects: string;
|
|
@@ -548,6 +554,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
548
554
|
interactionAnsweredBy: (who: string) => string;
|
|
549
555
|
badgePathUnavailable: string;
|
|
550
556
|
badgeNoComposer: string;
|
|
557
|
+
badgeActionFailed: string;
|
|
551
558
|
badgeCopied: string;
|
|
552
559
|
badgeOpenIn: (app: string) => string;
|
|
553
560
|
badgeOpenInBrowserPane: string;
|
|
@@ -583,6 +590,9 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
583
590
|
previous: string;
|
|
584
591
|
next: string;
|
|
585
592
|
loading: string;
|
|
593
|
+
noticeDetails: string;
|
|
594
|
+
noticeStack: string;
|
|
595
|
+
noticeDismiss: string;
|
|
586
596
|
filePath: (path: string) => string;
|
|
587
597
|
rename: string;
|
|
588
598
|
current: string;
|
|
@@ -600,6 +610,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
600
610
|
modalBack: string;
|
|
601
611
|
screenBack: (screen: string) => string;
|
|
602
612
|
screenBackKey: string;
|
|
613
|
+
screenNavBack: (screen: string) => string;
|
|
603
614
|
paletteLabel: string;
|
|
604
615
|
paletteFilter: string;
|
|
605
616
|
paletteBack: string;
|
|
@@ -663,6 +674,7 @@ declare function dateBucket(at: number, now: number, labels?: {
|
|
|
663
674
|
openInNewTab: string;
|
|
664
675
|
terminal: string;
|
|
665
676
|
terminalLoadFailed: (message: string) => string;
|
|
677
|
+
terminalStartFailed: string;
|
|
666
678
|
terminalExited: (code: number) => string;
|
|
667
679
|
}, calendar?: CalendarOptions): string;
|
|
668
680
|
/** From this many days {@link relativeTime} shows a date (`Mar 4`), not a duration: pass it to core `ageStep` to know whether the label takes "ago". */
|
|
@@ -125,6 +125,7 @@ function compareSessions(a, b, sort) {
|
|
|
125
125
|
switch (sort) {
|
|
126
126
|
case "alphabetical": return (a.title || "").localeCompare(b.title || "");
|
|
127
127
|
case "createdTime": return b.createdAt - a.createdAt;
|
|
128
|
+
case "lastUserMessage": return (b.lastUserMessageAt ?? b.createdAt) - (a.lastUserMessageAt ?? a.createdAt);
|
|
128
129
|
default: return (b.updatedAt ?? 0) - (a.updatedAt ?? 0);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
@@ -15,6 +15,21 @@ interface ProjectInSpace {
|
|
|
15
15
|
/** Its repository, when its group also lists the repository's sessions on other machines (`isProjectMember`). */
|
|
16
16
|
repoKey?: string;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* One session's row changed on its host. It was created or updated, with the
|
|
20
|
+
* row the host lists now, or it was deleted. Create and update are applied
|
|
21
|
+
* alike. If `row` is absent, no list shows the session and the change is ignored.
|
|
22
|
+
*/
|
|
23
|
+
type SessionRowChange<S extends {
|
|
24
|
+
id: string;
|
|
25
|
+
}> = {
|
|
26
|
+
op: 'create' | 'update';
|
|
27
|
+
sessionId: S['id'];
|
|
28
|
+
row?: S;
|
|
29
|
+
} | {
|
|
30
|
+
op: 'delete';
|
|
31
|
+
sessionId: S['id'];
|
|
32
|
+
};
|
|
18
33
|
/** What deleting a session did with its environment. */
|
|
19
34
|
type SessionRemoval = {
|
|
20
35
|
kept: false;
|
|
@@ -41,8 +56,8 @@ interface SessionDirectorySource<S extends SidebarListSession, C = unknown> {
|
|
|
41
56
|
setSpace?(sessionId: S['id'], spaceId: string): Promise<void>;
|
|
42
57
|
/** Resolves to `{ kept: true }` when the session's environment stayed on disk. */
|
|
43
58
|
remove?(sessionId: S['id']): Promise<void | SessionRemoval>;
|
|
44
|
-
/** Fires
|
|
45
|
-
onChanged?(listener: (
|
|
59
|
+
/** Fires with each session row change, its new row included. */
|
|
60
|
+
onChanged?(listener: (change: SessionRowChange<S>) => void): () => void;
|
|
46
61
|
/**
|
|
47
62
|
* Fires with a session's facets when the extensions computed new ones (`sessions:facets`): every
|
|
48
63
|
* value, keyed `<extensionId>.<key>`. Absent: facets change only with a re-read.
|
|
@@ -65,6 +80,8 @@ interface SessionDirectoryStreams<K extends string, P> {
|
|
|
65
80
|
hasEvents(sessionId: K): boolean;
|
|
66
81
|
/** Cache a loaded transcript page. */
|
|
67
82
|
setEventsPage(sessionId: K, page: P): void;
|
|
83
|
+
/** Bring a session's window back in step after its live signals may have gone missing (a reconnect). It reads the rows appended since its history position, else its tail. */
|
|
84
|
+
resync(sessionId: K): void;
|
|
68
85
|
/** Drop every cached transcript except these. */
|
|
69
86
|
evictExcept(keep: ReadonlySet<K>): void;
|
|
70
87
|
/** Drop every stream (a runtime switch). */
|
|
@@ -72,6 +89,22 @@ interface SessionDirectoryStreams<K extends string, P> {
|
|
|
72
89
|
/** Drop one session's stream (deleted). */
|
|
73
90
|
reset(sessionId: K): void;
|
|
74
91
|
}
|
|
92
|
+
/** What a transport knows of the runtime process behind a connection change (`signals.onRuntime`). */
|
|
93
|
+
interface RuntimeOrigin {
|
|
94
|
+
/** When connected, the runtime process the live signals come from (a daemon's `signalEpoch`). */
|
|
95
|
+
epoch?: string;
|
|
96
|
+
/** When down, whether the runtime is being reconnected. It is resumed if it comes back as the same process. */
|
|
97
|
+
resuming?: boolean;
|
|
98
|
+
/** When down, whether an attempt to reconnect is in flight. A down notice after one, with no comeback between, means the attempt failed. */
|
|
99
|
+
connecting?: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** The runtime's connection as a transport knows it now (`signals.currentRuntime`). */
|
|
102
|
+
interface RuntimeNow {
|
|
103
|
+
/** Whether the transport reaches the runtime now. */
|
|
104
|
+
connected: boolean;
|
|
105
|
+
/** When connected, the runtime process the live signals come from. */
|
|
106
|
+
origin?: RuntimeOrigin;
|
|
107
|
+
}
|
|
75
108
|
/** A remote host as the directory follows it. */
|
|
76
109
|
interface DirectoryServer {
|
|
77
110
|
/** The host id rows carry as `hostId`. */
|
|
@@ -112,8 +145,19 @@ interface SessionDirectoryOptions<S extends SidebarListSession, P, C = unknown>
|
|
|
112
145
|
signals?: {
|
|
113
146
|
/** A run ended. */
|
|
114
147
|
onRunEnd?(listener: (sessionId: S['id'], ok: boolean) => void): () => void;
|
|
115
|
-
/**
|
|
116
|
-
|
|
148
|
+
/**
|
|
149
|
+
* The runtime connected or disconnected (a daemon, a server connection). The transport passes `origin`
|
|
150
|
+
* when it knows it. When connected, it names the runtime process the signals come from (`epoch`). When
|
|
151
|
+
* down, it says whether the runtime is being reconnected and resumed (`resuming`). A reconnect to the
|
|
152
|
+
* same process keeps the windows, and what they missed is replayed. Any other change drops them.
|
|
153
|
+
*/
|
|
154
|
+
onRuntime?(listener: (connected: boolean, origin?: RuntimeOrigin) => void): () => void;
|
|
155
|
+
/**
|
|
156
|
+
* The runtime's connection now, read once at start. A window that mounts after the last `onRuntime`
|
|
157
|
+
* (a second window, a reload) then knows which process its streams come from. A reconnect to that
|
|
158
|
+
* process keeps them. `null` means unknown.
|
|
159
|
+
*/
|
|
160
|
+
currentRuntime?(): Promise<RuntimeNow | null>;
|
|
117
161
|
/** A remote host was removed, with the sessions it held and the words to show. */
|
|
118
162
|
onHostRemoved?(listener: (removed: {
|
|
119
163
|
hostId: string;
|
|
@@ -132,9 +176,7 @@ interface SessionDirectoryOptions<S extends SidebarListSession, P, C = unknown>
|
|
|
132
176
|
request(run: () => void): number;
|
|
133
177
|
cancel(handle: number): void;
|
|
134
178
|
};
|
|
135
|
-
/**
|
|
136
|
-
changedDebounceMs?: number;
|
|
137
|
-
/** A refresh is about to list these rows (an app's per-row bookkeeping, before they render). */
|
|
179
|
+
/** A refresh or a change is about to list these rows (an app's per-row bookkeeping, before they render). */
|
|
138
180
|
onListed?(rows: readonly S[]): void;
|
|
139
181
|
/** A session was deleted: drop what the app keeps for it (panels, layouts). */
|
|
140
182
|
onRemoved?(sessionId: S['id']): void;
|
|
@@ -202,17 +244,17 @@ interface SessionDirectory<S extends SidebarListSession, C = unknown> {
|
|
|
202
244
|
create(input: C & {
|
|
203
245
|
spaceId?: string;
|
|
204
246
|
}): Promise<S>;
|
|
205
|
-
/** Renames a session
|
|
247
|
+
/** Renames a session (its row comes back as a change). */
|
|
206
248
|
rename(sessionId: S['id'], title: string): Promise<void>;
|
|
207
249
|
/** Stars or unstars a session at once; a failed write puts it back. */
|
|
208
250
|
setFavorite(sessionId: S['id'], favorite: boolean): Promise<void>;
|
|
209
|
-
/** Archives or restores a session
|
|
251
|
+
/** Archives or restores a session. */
|
|
210
252
|
setArchived(sessionId: S['id'], archived: boolean): Promise<void>;
|
|
211
|
-
/** Archives sessions
|
|
253
|
+
/** Archives sessions. */
|
|
212
254
|
archiveMany(sessionIds: readonly S['id'][]): Promise<void>;
|
|
213
|
-
/** Delete one session
|
|
255
|
+
/** Delete one session. */
|
|
214
256
|
drop(sessionId: S['id']): Promise<SessionRemoval>;
|
|
215
|
-
/** Delete sessions
|
|
257
|
+
/** Delete sessions. Resolves to how many kept their environment. */
|
|
216
258
|
removeMany(sessionIds: readonly S['id'][]): Promise<{
|
|
217
259
|
kept: number;
|
|
218
260
|
reason?: string;
|
|
@@ -224,7 +266,7 @@ interface SessionDirectory<S extends SidebarListSession, C = unknown> {
|
|
|
224
266
|
setActiveSpace(spaceId: string): void;
|
|
225
267
|
/** Step to the next or previous space, wrapping at either end; false with spaces off or only one space. */
|
|
226
268
|
stepSpace(delta: 1 | -1): boolean;
|
|
227
|
-
/** Moves a session to a space
|
|
269
|
+
/** Moves a session to a space. */
|
|
228
270
|
setSessionSpace(sessionId: S['id'], spaceId: string): Promise<void>;
|
|
229
271
|
/** Hides a space from the switcher or shows it again; hiding the shown space shows the default one. */
|
|
230
272
|
setSpaceHidden(spaceId: string, hidden: boolean): void;
|
|
@@ -293,4 +335,4 @@ declare function filterDirectorySessions<S extends SidebarListSession>(sessions:
|
|
|
293
335
|
now?: number;
|
|
294
336
|
}): readonly S[];
|
|
295
337
|
//#endregion
|
|
296
|
-
export { DirectoryServer, DirectorySettings, ProjectInSpace, SessionDirectory, SessionDirectoryOptions, SessionDirectorySource, SessionDirectoryState, SessionDirectoryStreams, SessionRef, SessionRemoval, availableProjectsOf, createSessionDirectory, filterDirectorySessions, projectKeyOf, sessionRefsOf };
|
|
338
|
+
export { DirectoryServer, DirectorySettings, ProjectInSpace, RuntimeNow, RuntimeOrigin, SessionDirectory, SessionDirectoryOptions, SessionDirectorySource, SessionDirectoryState, SessionDirectoryStreams, SessionRef, SessionRemoval, SessionRowChange, availableProjectsOf, createSessionDirectory, filterDirectorySessions, projectKeyOf, sessionRefsOf };
|
|
@@ -4,23 +4,29 @@ import { isProjectMember } from "./rows.js";
|
|
|
4
4
|
import { DEFAULT_SIDEBAR_VIEW } from "./view.js";
|
|
5
5
|
import { createNotificationStore } from "./notifications.js";
|
|
6
6
|
import { NO_SIDEBAR_CONTRIBUTIONS, filterQueryOf, filtersForSpace, mergeSidebarSpaces, resolveGrouping, resolveSort, spaceMembership, switcherSpaces } from "./query.js";
|
|
7
|
-
import { DEFAULT_SPACE_ID, SessionQueryError, activeSpaceOf, normalizeSpaces, projectNameOf, resolveSessionSpace, sessionDirLabel, viewQuery } from "zidane/chat/pure";
|
|
7
|
+
import { DEFAULT_SPACE_ID, SessionQueryError, activeSpaceOf, createRecentChanges, normalizeSpaces, projectNameOf, resolveSessionSpace, sessionDirLabel, viewQuery } from "zidane/chat/pure";
|
|
8
8
|
//#region src/chat/headless/sidebar/directory.ts
|
|
9
9
|
/**
|
|
10
10
|
* The session directory: every host's sessions as the sidebar lists them,
|
|
11
11
|
* kept current outside React.
|
|
12
12
|
*
|
|
13
13
|
* - Listing: one read per host (this machine plus each remote host) under
|
|
14
|
-
* the shown archive filter
|
|
15
|
-
*
|
|
14
|
+
* the shown archive filter, at the start, on a reconnect, a host or filter
|
|
15
|
+
* change. Reads are versioned: only the newest refresh lands, and a host's
|
|
16
|
+
* own older answer never overwrites a newer one.
|
|
17
|
+
* - Live rows: each change signal carries the session's row as its host
|
|
18
|
+
* lists it (`SessionRowChange`). The directory applies it at once in
|
|
19
|
+
* place, with no read. Create and update upsert, and delete removes. A
|
|
20
|
+
* change that arrives while a read of its host is in flight is applied on
|
|
21
|
+
* top of that read's answer.
|
|
16
22
|
* - Run state: a refresh reconciles the rows' status with the stream store
|
|
17
23
|
* (a running row marks the session running, an idle one confirmed by the
|
|
18
24
|
* host clears a run this client missed the end of) unless a stream signal
|
|
19
25
|
* moved the run state while the refresh was in flight.
|
|
20
|
-
* - Live: a
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
26
|
+
* - Live: a run end leaves a notification on a session not open. A runtime
|
|
27
|
+
* switch drops every stream, unless the runtime comes back as the same
|
|
28
|
+
* process. After a reconnect (runtime or the open session's server), the
|
|
29
|
+
* windows on screen climb the reconnect ladder (`streams.resync`).
|
|
24
30
|
* - Prewarm: the transcripts around the open session load in idle time.
|
|
25
31
|
* - Spaces: the shown space (kept by the views store), the slide direction of
|
|
26
32
|
* the last switch, following the open session to its space, and reopening
|
|
@@ -56,7 +62,6 @@ const defaultIdle = {
|
|
|
56
62
|
function createSessionDirectory(options) {
|
|
57
63
|
const { sessions, streams, views } = options;
|
|
58
64
|
const idle = options.idle ?? defaultIdle;
|
|
59
|
-
const debounceMs = options.changedDebounceMs ?? 300;
|
|
60
65
|
const pinnedSession = options.pinnedSession ?? null;
|
|
61
66
|
const notifications = createNotificationStore();
|
|
62
67
|
let contributionsStore = options.contributions ?? constant(NO_SIDEBAR_CONTRIBUTIONS);
|
|
@@ -117,6 +122,22 @@ function createSessionDirectory(options) {
|
|
|
117
122
|
let refreshVersion = 0;
|
|
118
123
|
const hostLists = /* @__PURE__ */ new Map();
|
|
119
124
|
const hostReads = /* @__PURE__ */ new Map();
|
|
125
|
+
/** Per host, the changes received while its list is read. A read's answer takes the ones after the read started (`createRecentChanges`). */
|
|
126
|
+
const recentChanges = /* @__PURE__ */ new Map();
|
|
127
|
+
const recentFor = (host) => {
|
|
128
|
+
let recent = recentChanges.get(host);
|
|
129
|
+
if (!recent) recentChanges.set(host, recent = createRecentChanges());
|
|
130
|
+
return recent;
|
|
131
|
+
};
|
|
132
|
+
/** The row as the shown archive filter lists it, `null` when it does not. */
|
|
133
|
+
const listed = (row) => row && (archive === "active" ? !row.archived : archive === "archived" ? Boolean(row.archived) : true) ? row : null;
|
|
134
|
+
/** `rows` with `id`'s row replaced, added (`row`) or removed (`null`), newest first as a host lists them. */
|
|
135
|
+
const withRow = (rows, id, row) => {
|
|
136
|
+
const at = rows.findIndex((s) => s.id === id);
|
|
137
|
+
if (at === -1 && !row) return rows;
|
|
138
|
+
const next = at === -1 ? [...rows, row] : row ? rows.map((s, i) => i === at ? row : s) : rows.filter((_, i) => i !== at);
|
|
139
|
+
return row ? next.map((s, i) => [s, i]).sort((a, b) => b[0].updatedAt - a[0].updatedAt || a[1] - b[1]).map(([s]) => s) : next;
|
|
140
|
+
};
|
|
120
141
|
const refreshHosts = async (only) => {
|
|
121
142
|
const version = ++refreshVersion;
|
|
122
143
|
const unchanged = streams.captureRunState();
|
|
@@ -126,11 +147,17 @@ function createSessionDirectory(options) {
|
|
|
126
147
|
await Promise.all(hosts.filter((host) => !only || only.has(host) || !hostLists.has(host)).map(async (host) => {
|
|
127
148
|
const read = (hostReads.get(host) ?? 0) + 1;
|
|
128
149
|
hostReads.set(host, read);
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
const recent = recentFor(host);
|
|
151
|
+
const reading = recent.begin();
|
|
152
|
+
try {
|
|
153
|
+
const rows = host ? await sessions.query({
|
|
154
|
+
archive: filter,
|
|
155
|
+
hostId: host
|
|
156
|
+
}).catch(() => []) : await sessions.query({ archive: filter });
|
|
157
|
+
if (hostReads.get(host) === read) hostLists.set(host, recent.end(reading, rows, (list, row, id) => withRow(list, id, listed(row))));
|
|
158
|
+
} finally {
|
|
159
|
+
recent.drop(reading);
|
|
160
|
+
}
|
|
134
161
|
}));
|
|
135
162
|
if (version !== refreshVersion) return;
|
|
136
163
|
for (const host of hostLists.keys()) if (!hosts.includes(host)) hostLists.delete(host);
|
|
@@ -157,13 +184,52 @@ function createSessionDirectory(options) {
|
|
|
157
184
|
}
|
|
158
185
|
};
|
|
159
186
|
const refresh = () => refreshHosts();
|
|
160
|
-
|
|
187
|
+
/** Applies a change signal. The row lands in its host's list at once. */
|
|
188
|
+
const applyChange = (change) => {
|
|
189
|
+
const row = change.op === "delete" ? null : change.row ?? void 0;
|
|
190
|
+
if (row === void 0) return;
|
|
191
|
+
const id = change.sessionId;
|
|
192
|
+
const host = row?.hostId ?? options.serverOf(id) ?? [...hostLists].find(([, rows]) => rows.some((s) => s.id === id))?.[0] ?? "";
|
|
193
|
+
recentFor(host).note(id, row);
|
|
194
|
+
const rows = hostLists.get(host);
|
|
195
|
+
if (!rows) return;
|
|
196
|
+
const shown = listed(row);
|
|
197
|
+
const next = withRow(rows, id, shown);
|
|
198
|
+
if (next === rows) return;
|
|
199
|
+
hostLists.set(host, next);
|
|
200
|
+
if (shown) {
|
|
201
|
+
options.onListed?.([shown]);
|
|
202
|
+
if (shown.status === "running") streams.markRunning(shown.id);
|
|
203
|
+
}
|
|
204
|
+
const hosts = ["", ...options.servers.get().map((h) => h.id)];
|
|
205
|
+
patch({ allSessions: hosts.flatMap((h) => hostLists.get(h) ?? []) });
|
|
206
|
+
};
|
|
207
|
+
const comebacks = /* @__PURE__ */ new Map();
|
|
208
|
+
const caughtUp = /* @__PURE__ */ new Map();
|
|
209
|
+
const comebacksOf = (sessionId) => comebacks.get(options.serverOf(sessionId) ?? "") ?? 0;
|
|
210
|
+
const comeBack = (host) => comebacks.set(host, (comebacks.get(host) ?? 0) + 1);
|
|
211
|
+
const catchUp = (sessionId) => {
|
|
212
|
+
const seen = comebacksOf(sessionId);
|
|
213
|
+
if (seen === 0 || caughtUp.get(sessionId) === seen) return;
|
|
214
|
+
caughtUp.set(sessionId, seen);
|
|
215
|
+
if (streams.hasEvents(sessionId)) streams.resync(sessionId);
|
|
216
|
+
};
|
|
217
|
+
/** When the open session's connection comes back, its window climbs the reconnect ladder's last steps (`streams.resync`). */
|
|
218
|
+
const resyncActiveTranscript = () => {
|
|
161
219
|
const active = state.get().activeSessionId;
|
|
162
|
-
if (!active
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
220
|
+
if (!active) return;
|
|
221
|
+
caughtUp.set(active, comebacksOf(active));
|
|
222
|
+
streams.resync(active);
|
|
223
|
+
};
|
|
224
|
+
/** When this machine's runtime comes back, every window on screen (the open session, a side chat) climbs the ladder. */
|
|
225
|
+
const resyncVisibleTranscripts = () => {
|
|
226
|
+
resyncActiveTranscript();
|
|
227
|
+
const active = state.get().activeSessionId;
|
|
228
|
+
for (const id of options.pinned?.() ?? []) {
|
|
229
|
+
if (id === active || options.serverOf(id) !== null || !streams.hasEvents(id)) continue;
|
|
230
|
+
caughtUp.set(id, comebacksOf(id));
|
|
231
|
+
streams.resync(id);
|
|
232
|
+
}
|
|
167
233
|
};
|
|
168
234
|
const setActive = (next) => {
|
|
169
235
|
const current = state.get().activeSessionId;
|
|
@@ -349,11 +415,19 @@ function createSessionDirectory(options) {
|
|
|
349
415
|
id,
|
|
350
416
|
status
|
|
351
417
|
};
|
|
352
|
-
if (id !== null && last.id === id && last.status !== void 0 && last.status !== "connected" && status === "connected")
|
|
418
|
+
if (id !== null && last.id === id && last.status !== void 0 && last.status !== "connected" && status === "connected") resyncActiveTranscript();
|
|
419
|
+
if (active) catchUp(active);
|
|
353
420
|
};
|
|
421
|
+
let statuses = new Map(options.servers.get().map((host) => [host.id, host.status]));
|
|
354
422
|
followActiveServer();
|
|
355
423
|
offs.push(state.subscribe(followActiveServer));
|
|
356
424
|
offs.push(options.servers.subscribe(() => {
|
|
425
|
+
const next = new Map(options.servers.get().map((host) => [host.id, host.status]));
|
|
426
|
+
for (const [host, status] of next) {
|
|
427
|
+
const was = statuses.get(host);
|
|
428
|
+
if (was !== void 0 && was !== "connected" && status === "connected") comeBack(host);
|
|
429
|
+
}
|
|
430
|
+
statuses = next;
|
|
357
431
|
followActiveServer();
|
|
358
432
|
const key = options.servers.get().map((host) => `${host.id}:${host.status}`).join("|");
|
|
359
433
|
if (key === serversKey) return;
|
|
@@ -361,24 +435,65 @@ function createSessionDirectory(options) {
|
|
|
361
435
|
refresh();
|
|
362
436
|
}));
|
|
363
437
|
let runtime = null;
|
|
438
|
+
/** The process the windows' streams come from, when the transport names it. */
|
|
439
|
+
let runtimeEpoch;
|
|
440
|
+
/** Set when the runtime went down while being resumed. The windows wait for the comeback to know whether to go. */
|
|
441
|
+
let dropPending = false;
|
|
442
|
+
/** The last down notice said an attempt to reconnect was in flight. */
|
|
443
|
+
let attempting = false;
|
|
444
|
+
/** The runtime's connection went down since it was last up, so its next `connected` is a comeback. */
|
|
445
|
+
let runtimeDropped = false;
|
|
364
446
|
const onRuntime = options.signals?.onRuntime;
|
|
365
447
|
if (onRuntime) {
|
|
366
|
-
offs.push(onRuntime((connected) => {
|
|
448
|
+
offs.push(onRuntime((connected, origin) => {
|
|
367
449
|
const previous = runtime;
|
|
368
450
|
runtime = connected;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
451
|
+
const drop = () => {
|
|
452
|
+
dropPending = false;
|
|
453
|
+
streams.dropAll();
|
|
454
|
+
refresh();
|
|
455
|
+
};
|
|
456
|
+
const wasAttempting = attempting;
|
|
457
|
+
attempting = !connected && origin?.connecting === true;
|
|
458
|
+
if (previous === null) {
|
|
459
|
+
if (connected) runtimeEpoch = origin?.epoch;
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (!connected) {
|
|
463
|
+
if (previous) {
|
|
464
|
+
if (origin?.resuming && runtimeEpoch !== void 0) dropPending = true;
|
|
465
|
+
else drop();
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (dropPending && (!origin?.resuming || wasAttempting && !attempting)) drop();
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (previous) return;
|
|
472
|
+
const same = dropPending && origin?.epoch !== void 0 && origin.epoch === runtimeEpoch;
|
|
473
|
+
runtimeEpoch = origin?.epoch;
|
|
474
|
+
if (same) dropPending = false;
|
|
475
|
+
else drop();
|
|
372
476
|
}));
|
|
373
477
|
offs.push(onRuntime((connected) => {
|
|
374
|
-
if (!connected)
|
|
478
|
+
if (!connected) {
|
|
479
|
+
runtimeDropped = true;
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (runtimeDropped) comeBack("");
|
|
483
|
+
runtimeDropped = false;
|
|
375
484
|
refresh();
|
|
376
|
-
|
|
485
|
+
resyncVisibleTranscripts();
|
|
377
486
|
}));
|
|
487
|
+
let seeding = true;
|
|
488
|
+
offs.push(() => seeding = false);
|
|
489
|
+
options.signals?.currentRuntime?.().then((now) => {
|
|
490
|
+
if (!seeding || !now || runtime !== null) return;
|
|
491
|
+
runtime = now.connected;
|
|
492
|
+
if (now.connected) runtimeEpoch = now.origin?.epoch;
|
|
493
|
+
}, () => void 0);
|
|
378
494
|
}
|
|
379
495
|
const onRunEnd = options.signals?.onRunEnd;
|
|
380
496
|
if (onRunEnd) offs.push(onRunEnd((sessionId, ok) => {
|
|
381
|
-
refreshHosts(/* @__PURE__ */ new Set([options.serverOf(sessionId) ?? ""]));
|
|
382
497
|
if (sessionId === state.get().activeSessionId) return;
|
|
383
498
|
notifications.set(sessionId, ok ? "completed" : "error");
|
|
384
499
|
}));
|
|
@@ -391,30 +506,7 @@ function createSessionDirectory(options) {
|
|
|
391
506
|
}
|
|
392
507
|
refresh();
|
|
393
508
|
}));
|
|
394
|
-
if (sessions.onChanged)
|
|
395
|
-
let timer = null;
|
|
396
|
-
let activeTouched = false;
|
|
397
|
-
let touched = /* @__PURE__ */ new Set();
|
|
398
|
-
const off = sessions.onChanged((sessionId) => {
|
|
399
|
-
activeTouched ||= sessionId === state.get().activeSessionId;
|
|
400
|
-
touched.add(options.serverOf(sessionId) ?? "");
|
|
401
|
-
if (timer !== null) clearTimeout(timer);
|
|
402
|
-
timer = setTimeout(() => {
|
|
403
|
-
timer = null;
|
|
404
|
-
const hosts = touched;
|
|
405
|
-
touched = /* @__PURE__ */ new Set();
|
|
406
|
-
refreshHosts(hosts);
|
|
407
|
-
if (activeTouched) {
|
|
408
|
-
activeTouched = false;
|
|
409
|
-
reconvergeActiveTranscript();
|
|
410
|
-
}
|
|
411
|
-
}, debounceMs);
|
|
412
|
-
});
|
|
413
|
-
offs.push(() => {
|
|
414
|
-
off();
|
|
415
|
-
if (timer !== null) clearTimeout(timer);
|
|
416
|
-
});
|
|
417
|
-
}
|
|
509
|
+
if (sessions.onChanged) offs.push(sessions.onChanged(applyChange));
|
|
418
510
|
refresh();
|
|
419
511
|
return () => {
|
|
420
512
|
for (const off of offs.splice(0).reverse()) off();
|
|
@@ -443,7 +535,6 @@ function createSessionDirectory(options) {
|
|
|
443
535
|
},
|
|
444
536
|
async rename(sessionId, title) {
|
|
445
537
|
await sessions.rename?.(sessionId, title);
|
|
446
|
-
await refresh();
|
|
447
538
|
},
|
|
448
539
|
async setFavorite(sessionId, favorite) {
|
|
449
540
|
const flip = (value) => {
|
|
@@ -460,33 +551,23 @@ function createSessionDirectory(options) {
|
|
|
460
551
|
flip(!favorite);
|
|
461
552
|
throw err;
|
|
462
553
|
}
|
|
463
|
-
await refresh();
|
|
464
554
|
},
|
|
465
555
|
async setArchived(sessionId, archived) {
|
|
466
556
|
await sessions.setArchived?.(sessionId, archived);
|
|
467
|
-
await refresh();
|
|
468
557
|
},
|
|
469
558
|
async archiveMany(sessionIds) {
|
|
470
|
-
|
|
471
|
-
for (const id of sessionIds) await sessions.setArchived?.(id, true);
|
|
472
|
-
} finally {
|
|
473
|
-
await refresh();
|
|
474
|
-
}
|
|
559
|
+
for (const id of sessionIds) await sessions.setArchived?.(id, true);
|
|
475
560
|
},
|
|
476
561
|
drop,
|
|
477
562
|
async removeMany(sessionIds) {
|
|
478
563
|
let kept = 0;
|
|
479
564
|
let reason;
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
reason ??= result.reason;
|
|
486
|
-
}
|
|
565
|
+
for (const id of sessionIds) {
|
|
566
|
+
const result = await drop(id);
|
|
567
|
+
if (result.kept) {
|
|
568
|
+
kept += 1;
|
|
569
|
+
reason ??= result.reason;
|
|
487
570
|
}
|
|
488
|
-
} finally {
|
|
489
|
-
await refresh();
|
|
490
571
|
}
|
|
491
572
|
return reason ? {
|
|
492
573
|
kept,
|
|
@@ -504,7 +585,6 @@ function createSessionDirectory(options) {
|
|
|
504
585
|
async setSessionSpace(sessionId, spaceId) {
|
|
505
586
|
await sessions.setSpace?.(sessionId, spaceId);
|
|
506
587
|
views.forgetSession(sessionId, spaceId);
|
|
507
|
-
await refresh();
|
|
508
588
|
},
|
|
509
589
|
setSpaceHidden(spaceId, hidden) {
|
|
510
590
|
if (spaceId === DEFAULT_SPACE_ID) return;
|
|
@@ -604,7 +684,6 @@ function createSessionDirectory(options) {
|
|
|
604
684
|
await sessions.setSpace?.(member.id, spaceId);
|
|
605
685
|
views.forgetSession(member.id, spaceId);
|
|
606
686
|
}
|
|
607
|
-
await refresh();
|
|
608
687
|
return members.length;
|
|
609
688
|
}
|
|
610
689
|
};
|