zidane-ui 9.1.4 → 9.1.6

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.
Files changed (251) hide show
  1. package/dist/adapters/server-extensions-host.js +1 -1
  2. package/dist/adapters/server-host.js +2 -1
  3. package/dist/adapters/server-machines-host.js +1 -1
  4. package/dist/adapters/server-session-host.js +87 -13
  5. package/dist/adapters/server-settings-host.js +1 -1
  6. package/dist/appearance/AppearanceProvider.js +2 -2
  7. package/dist/appearance/headless/index.js +1 -1
  8. package/dist/approvals/headless/index.js +2 -2
  9. package/dist/chat/HostTranscript.d.ts +5 -1
  10. package/dist/chat/HostTranscript.js +16 -4
  11. package/dist/chat/RunEstimateChip.d.ts +5 -3
  12. package/dist/chat/RunEstimateChip.js +33 -3
  13. package/dist/chat/Transcript.js +1 -1
  14. package/dist/chat/composer/ConversationBadges.js +16 -10
  15. package/dist/chat/composer/composer-bus.d.ts +2 -2
  16. package/dist/chat/composer/composer-bus.js +2 -2
  17. package/dist/chat/composer/use-composer-stores.js +1 -1
  18. package/dist/chat/composer/use-run-meter.js +1 -1
  19. package/dist/chat/headless/composer/conversation-badges.d.ts +8 -3
  20. package/dist/chat/headless/composer/conversation-badges.js +26 -11
  21. package/dist/chat/headless/composer/index.js +2 -2
  22. package/dist/chat/headless/composer/labels-chrome.d.ts +3 -1
  23. package/dist/chat/headless/composer/labels-chrome.js +3 -1
  24. package/dist/chat/headless/index.d.ts +6 -3
  25. package/dist/chat/headless/index.js +16 -13
  26. package/dist/chat/headless/labels.d.ts +54 -1
  27. package/dist/chat/headless/labels.js +62 -2
  28. package/dist/chat/headless/secrets.d.ts +32 -0
  29. package/dist/chat/headless/secrets.js +43 -0
  30. package/dist/chat/headless/session-list.d.ts +45 -0
  31. package/dist/chat/headless/sidebar/estimates.js +13 -1
  32. package/dist/chat/headless/sidebar/history.d.ts +71 -0
  33. package/dist/chat/headless/sidebar/history.js +174 -0
  34. package/dist/chat/headless/sidebar/index.d.ts +3 -2
  35. package/dist/chat/headless/sidebar/index.js +2 -1
  36. package/dist/chat/headless/sidebar/labels.d.ts +50 -0
  37. package/dist/chat/headless/sidebar/labels.js +1 -0
  38. package/dist/chat/headless/sidebar/query.d.ts +2 -2
  39. package/dist/chat/headless/sidebar/query.js +6 -3
  40. package/dist/chat/headless/sidebar/types.d.ts +7 -1
  41. package/dist/chat/headless/sidebar/view.js +2 -0
  42. package/dist/chat/headless/transcript/auth-failure.d.ts +67 -0
  43. package/dist/chat/headless/transcript/auth-failure.js +87 -0
  44. package/dist/chat/headless/transcript/index.d.ts +2 -1
  45. package/dist/chat/headless/transcript/index.js +2 -1
  46. package/dist/chat/headless/transcript/nesting.d.ts +3 -3
  47. package/dist/chat/headless/transcript/nesting.js +12 -4
  48. package/dist/chat/index.d.ts +4 -2
  49. package/dist/chat/index.js +3 -1
  50. package/dist/chat/interactions/InboxPanel.d.ts +6 -2
  51. package/dist/chat/interactions/InboxPanel.js +82 -44
  52. package/dist/chat/interactions/PlanCard.d.ts +2 -2
  53. package/dist/chat/interactions/PlanCard.js +1 -1
  54. package/dist/chat/interactions/QuestionField.d.ts +8 -3
  55. package/dist/chat/interactions/QuestionField.js +89 -8
  56. package/dist/chat/interactions/QuestionForm.d.ts +3 -1
  57. package/dist/chat/interactions/QuestionForm.js +48 -10
  58. package/dist/chat/interactions/SecretInput.d.ts +32 -0
  59. package/dist/chat/interactions/SecretInput.js +72 -0
  60. package/dist/chat/interactions/card-ui.d.ts +5 -3
  61. package/dist/chat/interactions/card-ui.js +5 -3
  62. package/dist/chat/interactions/index.d.ts +2 -1
  63. package/dist/chat/interactions/index.js +2 -1
  64. package/dist/chat/sidebar/SessionSidebar.js +2 -0
  65. package/dist/chat/sidebar/SidebarFilterMenu.d.ts +7 -2
  66. package/dist/chat/sidebar/SidebarFilterMenu.js +26 -2
  67. package/dist/chat/sidebar/SidebarSessionRow.d.ts +3 -1
  68. package/dist/chat/sidebar/SidebarSessionRow.js +8 -3
  69. package/dist/chat/sidebar/sidebar-live.js +8 -4
  70. package/dist/chat/transcript/AuthFailureCard.d.ts +34 -0
  71. package/dist/chat/transcript/AuthFailureCard.js +358 -0
  72. package/dist/chat/transcript/InteractionToolBody.js +12 -2
  73. package/dist/chat/transcript/actions.d.ts +24 -1
  74. package/dist/chat/transcript/index.d.ts +3 -2
  75. package/dist/chat/transcript/index.js +2 -1
  76. package/dist/chat/transcript/notices.d.ts +2 -1
  77. package/dist/chat/transcript/notices.js +13 -1
  78. package/dist/chat/transcript/script-input.d.ts +1 -1
  79. package/dist/chat/transcript/shell-block.js +1 -1
  80. package/dist/components/HowItWorks.d.ts +8 -1
  81. package/dist/components/HowItWorks.js +2 -2
  82. package/dist/components/RowActionsMenu.d.ts +6 -1
  83. package/dist/components/RowActionsMenu.js +4 -0
  84. package/dist/core.css +24 -0
  85. package/dist/dist/{index-WlE93Q6d.d.ts → index-DqCio7Nd.d.ts} +36 -3
  86. package/dist/dist/index-f-XWIoYo.d.ts +1 -0
  87. package/dist/dist/index.d.ts +6 -0
  88. package/dist/dist/sandbox-B4OW6oNx.d.ts +2 -0
  89. package/dist/dist/timeout-B5pO5I5a.d.ts +30 -0
  90. package/dist/extensions/ExtensionSettingsPage.js +1 -1
  91. package/dist/extensions/ExtensionSettingsSidebar.js +1 -1
  92. package/dist/extensions/headless/fixture-host.js +1 -1
  93. package/dist/extensions/headless/index.js +4 -4
  94. package/dist/extensions/hooks.js +1 -1
  95. package/dist/files/DiffsTab.js +1 -1
  96. package/dist/files/EditorColumn.js +1 -1
  97. package/dist/files/MediaDiff.js +1 -1
  98. package/dist/files/SearchPanel.js +121 -53
  99. package/dist/files/headless/name-match.js +29 -14
  100. package/dist/files/useEntryActions.js +1 -1
  101. package/dist/files/useOpenFiles.js +1 -1
  102. package/dist/git/WherePicker.js +2 -2
  103. package/dist/git/headless/environment.d.ts +6 -1
  104. package/dist/git/headless/environment.js +5 -2
  105. package/dist/git/headless/labels.d.ts +2 -0
  106. package/dist/git/headless/labels.js +2 -0
  107. package/dist/git/index.d.ts +2 -1
  108. package/dist/git/index.js +2 -1
  109. package/dist/git/useStartFromPanel.d.ts +22 -0
  110. package/dist/git/useStartFromPanel.js +91 -0
  111. package/dist/keybindings/KeyCommands.js +1 -1
  112. package/dist/keybindings/KeybindingsProvider.js +1 -1
  113. package/dist/keybindings/headless/catalog.d.ts +1 -1
  114. package/dist/keybindings/headless/catalog.js +14 -2
  115. package/dist/keybindings/headless/index.js +2 -2
  116. package/dist/keybindings/headless/key-scope.d.ts +4 -2
  117. package/dist/keybindings/headless/key-scope.js +17 -2
  118. package/dist/keybindings/headless/overrides.js +1 -1
  119. package/dist/machines/HostSyncPanel.js +1 -1
  120. package/dist/machines/SyncOutcomeBadge.js +1 -1
  121. package/dist/machines/headless/index.js +1 -1
  122. package/dist/onboarding/demos/features.d.ts +9 -1
  123. package/dist/onboarding/demos/features.js +107 -2
  124. package/dist/onboarding/demos/how-it-works.d.ts +5 -1
  125. package/dist/onboarding/demos/how-it-works.js +120 -2
  126. package/dist/onboarding/demos/index.js +1 -1
  127. package/dist/onboarding/headless/labels.d.ts +17 -0
  128. package/dist/onboarding/headless/labels.js +17 -0
  129. package/dist/onboarding/index.js +4 -4
  130. package/dist/onboarding/marks.d.ts +1 -2
  131. package/dist/onboarding/marks.js +4 -12
  132. package/dist/onboarding/steps/ConnectorsStep.js +1 -1
  133. package/dist/onboarding/steps/ReadyStep.js +1 -1
  134. package/dist/onboarding/steps/SkillsStep.js +1 -1
  135. package/dist/onboarding/steps/connect/ProviderPanel.js +1 -1
  136. package/dist/onboarding/tutorials.d.ts +3 -1
  137. package/dist/onboarding/tutorials.js +2 -1
  138. package/dist/palette/app-sources.js +1 -1
  139. package/dist/palette/headless/app-sources.js +10 -0
  140. package/dist/palette/headless/labels.js +2 -0
  141. package/dist/session/ArtifactsPanel.js +1 -1
  142. package/dist/session/PanelAttention.d.ts +14 -6
  143. package/dist/session/PanelAttention.js +35 -24
  144. package/dist/session/PlanTracker.d.ts +54 -0
  145. package/dist/session/PlanTracker.js +413 -0
  146. package/dist/session/ProjectHome.d.ts +15 -2
  147. package/dist/session/ProjectHome.js +158 -85
  148. package/dist/session/RemindersPanel.d.ts +25 -0
  149. package/dist/session/RemindersPanel.js +526 -0
  150. package/dist/session/SecretsPanel.d.ts +19 -0
  151. package/dist/session/SecretsPanel.js +241 -0
  152. package/dist/session/SessionProvider.d.ts +26 -5
  153. package/dist/session/SessionProvider.js +51 -6
  154. package/dist/session/StepStatusIcon.d.ts +24 -0
  155. package/dist/session/StepStatusIcon.js +50 -0
  156. package/dist/session/TodoList.d.ts +4 -3
  157. package/dist/session/TodoList.js +6 -5
  158. package/dist/session/headless/fixture-host.d.ts +32 -22
  159. package/dist/session/headless/fixture-host.js +86 -51
  160. package/dist/session/headless/fixture-plans.d.ts +57 -0
  161. package/dist/session/headless/fixture-plans.js +141 -0
  162. package/dist/session/headless/fixture-schedules.d.ts +49 -0
  163. package/dist/session/headless/fixture-schedules.js +133 -0
  164. package/dist/session/headless/home-layout.d.ts +10 -2
  165. package/dist/session/headless/home-layout.js +7 -2
  166. package/dist/session/headless/host.d.ts +85 -13
  167. package/dist/session/headless/index.d.ts +5 -3
  168. package/dist/session/headless/index.js +5 -3
  169. package/dist/session/headless/labels.d.ts +95 -12
  170. package/dist/session/headless/labels.js +67 -18
  171. package/dist/session/headless/plans.d.ts +53 -0
  172. package/dist/session/headless/plans.js +78 -0
  173. package/dist/session/headless/reminder-labels.d.ts +63 -0
  174. package/dist/session/headless/reminder-labels.js +84 -0
  175. package/dist/session/headless/schedules.d.ts +93 -0
  176. package/dist/session/headless/schedules.js +190 -0
  177. package/dist/session/headless/secrets.d.ts +28 -0
  178. package/dist/session/headless/secrets.js +48 -0
  179. package/dist/session/index.d.ts +7 -5
  180. package/dist/session/index.js +6 -4
  181. package/dist/session/testing/contract.d.ts +18 -5
  182. package/dist/session/testing/contract.js +351 -43
  183. package/dist/session/testing/index.d.ts +4 -2
  184. package/dist/session/testing/index.js +4 -2
  185. package/dist/settings/headless/catalog.js +17 -1
  186. package/dist/settings/headless/index.d.ts +2 -2
  187. package/dist/settings/headless/index.js +3 -3
  188. package/dist/settings/headless/labels.d.ts +0 -2
  189. package/dist/settings/headless/labels.js +0 -2
  190. package/dist/settings/headless/layout.js +7 -1
  191. package/dist/settings/headless/providers.d.ts +17 -1
  192. package/dist/settings/headless/providers.js +19 -1
  193. package/dist/settings/headless/search.d.ts +1 -1
  194. package/dist/settings/rows.d.ts +1 -1
  195. package/dist/settings/rows.js +2 -2
  196. package/dist/settings/sections/appearance/AppearanceSection.js +2 -2
  197. package/dist/settings/sections/claude-import/ClaudeImportDialog.js +1 -1
  198. package/dist/settings/sections/editor/EditorSection.js +1 -1
  199. package/dist/settings/sections/environments/EnvironmentsSection.js +1 -1
  200. package/dist/settings/sections/extensions/ChangeOriginDialog.js +1 -1
  201. package/dist/settings/sections/extensions/CreateExtensionDialog.js +1 -1
  202. package/dist/settings/sections/extensions/ExtensionsSection.js +1 -1
  203. package/dist/settings/sections/host-catalog/HostCatalogSection.js +1 -1
  204. package/dist/settings/sections/instructions/InstructionsSection.js +1 -1
  205. package/dist/settings/sections/keybindings/labels.d.ts +2 -0
  206. package/dist/settings/sections/keybindings/labels.js +2 -0
  207. package/dist/settings/sections/keybindings/rows.js +8 -0
  208. package/dist/settings/sections/mcp/McpSection.js +1 -1
  209. package/dist/settings/sections/mcp/McpServerRow.js +1 -1
  210. package/dist/settings/sections/models/ModelRows.js +2 -2
  211. package/dist/settings/sections/providers/ProvidersSection.js +209 -201
  212. package/dist/settings/sections/providers/ServerProvidersSection.js +1 -1
  213. package/dist/settings/sections/shared/catalog.js +1 -1
  214. package/dist/settings/sections/skills/SkillList.js +1 -1
  215. package/dist/settings/sections/skills/SkillsSection.js +2 -2
  216. package/dist/styles.css +59 -1
  217. package/dist/terminal/HostTerminalSurface.js +2 -2
  218. package/dist/workspace/PanelContentView.d.ts +22 -4
  219. package/dist/workspace/PanelContentView.js +112 -16
  220. package/dist/workspace/PanelGlyph.d.ts +21 -0
  221. package/dist/workspace/PanelGlyph.js +33 -0
  222. package/dist/workspace/PanelHome.js +5 -3
  223. package/dist/workspace/PanelStrip.js +11 -4
  224. package/dist/workspace/PanelToolbar.d.ts +32 -0
  225. package/dist/workspace/PanelToolbar.js +194 -0
  226. package/dist/workspace/WorkspaceProvider.d.ts +3 -1
  227. package/dist/workspace/WorkspaceProvider.js +6 -2
  228. package/dist/workspace/extension-tabs.js +5 -2
  229. package/dist/workspace/headless/host.d.ts +4 -0
  230. package/dist/workspace/headless/index.d.ts +3 -1
  231. package/dist/workspace/headless/index.js +4 -2
  232. package/dist/workspace/headless/labels.d.ts +10 -0
  233. package/dist/workspace/headless/labels.js +11 -1
  234. package/dist/workspace/headless/pages.d.ts +15 -0
  235. package/dist/workspace/headless/pages.js +49 -0
  236. package/dist/workspace/headless/tab-mark.d.ts +14 -0
  237. package/dist/workspace/headless/tab-mark.js +27 -0
  238. package/dist/workspace/headless/view.d.ts +7 -1
  239. package/dist/workspace/index.d.ts +3 -2
  240. package/dist/workspace/index.js +3 -2
  241. package/dist/workspace/testing/index.js +6 -0
  242. package/dist/workspace/workspace-key-commands.js +3 -3
  243. package/package.json +2 -2
  244. package/src/styles/motion.css +13 -0
  245. package/src/styles/onboarding.css +0 -1
  246. package/src/styles/primitives.css +10 -0
  247. package/src/styles/workspace.css +17 -0
  248. package/dist/session/TodosPanel.d.ts +0 -19
  249. package/dist/session/TodosPanel.js +0 -251
  250. package/dist/session/headless/todos.d.ts +0 -22
  251. package/dist/session/headless/todos.js +0 -38
@@ -5,6 +5,8 @@ import { RowActionsMenu } from "../components/RowActionsMenu.js";
5
5
  import { CountBadge, TabPill } from "../components/TabPill.js";
6
6
  import { useWorkspaceLabels } from "./useWorkspaceLabels.js";
7
7
  import { useWorkspaceActions, useWorkspaceContent } from "./WorkspaceProvider.js";
8
+ import { panelTabMark } from "./headless/tab-mark.js";
9
+ import { PanelGlyph } from "./PanelGlyph.js";
8
10
  import { caretX, insertBefore } from "./headless/strip-drop.js";
9
11
  import { startDrag } from "./dnd/engine.js";
10
12
  import { useDropTarget } from "./dnd/hooks.js";
@@ -120,6 +122,10 @@ function PanelStrip({ tabs, activeId, groupId, scopeKey, closedTabs, label, cont
120
122
  const addItems = useMemo(() => closedTabs.map((t) => ({
121
123
  id: t.id,
122
124
  label: t.label,
125
+ icon: /* @__PURE__ */ jsx(PanelGlyph, {
126
+ tab: t,
127
+ fallback: "i-ph-app-window"
128
+ }),
123
129
  onSelect: () => onPick(t.id)
124
130
  })), [closedTabs, onPick]);
125
131
  return /* @__PURE__ */ jsxs("div", {
@@ -157,7 +163,8 @@ function PanelStrip({ tabs, activeId, groupId, scopeKey, closedTabs, label, cont
157
163
  text: panelBadgeText(entry.content.badge),
158
164
  tone: "neutral"
159
165
  } : null;
160
- const icon = tab.icon?.gui;
166
+ const mark = panelTabMark(tab);
167
+ const icon = mark?.kind === "icon" ? mark.gui : void 0;
161
168
  return /* @__PURE__ */ jsx(motion.div, {
162
169
  layout: "position",
163
170
  "data-tab-id": tab.id,
@@ -194,9 +201,9 @@ function PanelStrip({ tabs, activeId, groupId, scopeKey, closedTabs, label, cont
194
201
  role: "status",
195
202
  "aria-label": labels.panelTabInUse,
196
203
  className: "zg-arc shrink-0"
197
- }) : icon && /* @__PURE__ */ jsx("span", {
198
- className: `${icon} text-zg-body shrink-0`,
199
- "aria-hidden": true
204
+ }) : /* @__PURE__ */ jsx(PanelGlyph, {
205
+ tab,
206
+ className: "text-zg-body shrink-0"
200
207
  }),
201
208
  trailing: badge && /* @__PURE__ */ jsx(CountBadge, {
202
209
  text: badge.text,
@@ -0,0 +1,32 @@
1
+ import { CSSProperties } from "react";
2
+ import { PanelControl, PanelSearch, PanelView } from "zidane/chat/pure";
3
+ //#region src/workspace/PanelToolbar.d.ts
4
+ /** How long typing rests before the search reloads the panel. */
5
+ declare const PANEL_SEARCH_DEBOUNCE_MS = 300;
6
+ /** Props for {@link PanelToolbar}. */
7
+ interface PanelToolbarProps {
8
+ /** The panel's controls. */
9
+ controls: readonly PanelControl[];
10
+ /** The panel's search field. Absent: none. */
11
+ search?: PanelSearch;
12
+ /** What the user picked, by control id: shown over the content's own values. */
13
+ view?: PanelView;
14
+ /** The text in the search field. */
15
+ searchText?: string;
16
+ /** A control changed: one option, or the options of a `multi` control. */
17
+ onControl: (id: string, value: string | readonly string[]) => void;
18
+ /** The search text changed (after typing rests). Absent: no search field. */
19
+ onSearch?: (text: string) => void;
20
+ /** Extra classes on the root. */
21
+ className?: string;
22
+ /** Inline styles on the root. */
23
+ style?: CSSProperties;
24
+ }
25
+ /**
26
+ * Renders a panel's controls, search field and filter menus.
27
+ *
28
+ * @param props - The {@link PanelToolbarProps}.
29
+ */
30
+ declare function PanelToolbar({ controls, search, view, searchText, onControl, onSearch, className, style }: PanelToolbarProps): import("react").JSX.Element | null;
31
+ //#endregion
32
+ export { PANEL_SEARCH_DEBOUNCE_MS, PanelToolbar, PanelToolbarProps };
@@ -0,0 +1,194 @@
1
+ import { Menu, MenuRow, MenuSeparator } from "../components/Menu.js";
2
+ import { Popover } from "../components/floating/Popover.js";
3
+ import { useWorkspaceLabels } from "./useWorkspaceLabels.js";
4
+ import { PanelControls } from "./PanelItems.js";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { useEffect, useRef, useState } from "react";
7
+ //#region src/workspace/PanelToolbar.tsx
8
+ /** How long typing rests before the search reloads the panel. */
9
+ const PANEL_SEARCH_DEBOUNCE_MS = 300;
10
+ /** What a menu control shows: its picked options, and the option that means no filter (`select`). */
11
+ function pickedOf(control, view) {
12
+ const own = view?.[control.id];
13
+ if (control.kind === "multi") {
14
+ const picked = Array.isArray(own) ? own : typeof own === "string" ? [own] : control.values ?? [];
15
+ return {
16
+ picked,
17
+ active: picked.length > 0
18
+ };
19
+ }
20
+ const value = typeof own === "string" && control.options.some((o) => o.id === own) ? own : control.value;
21
+ return {
22
+ picked: [value],
23
+ active: value !== (control.neutral ?? control.options[0]?.id)
24
+ };
25
+ }
26
+ /**
27
+ * Renders a panel's controls, search field and filter menus.
28
+ *
29
+ * @param props - The {@link PanelToolbarProps}.
30
+ */
31
+ function PanelToolbar({ controls, search, view, searchText = "", onControl, onSearch, className = "", style }) {
32
+ const labels = useWorkspaceLabels();
33
+ const segmented = controls.filter((c) => (c.kind ?? "segmented") === "segmented");
34
+ const menus = controls.filter((c) => c.kind === "select" || c.kind === "multi");
35
+ const searching = search !== void 0 && onSearch !== void 0;
36
+ const filtering = menus.some((c) => pickedOf(c, view).active);
37
+ if (segmented.length === 0 && menus.length === 0 && !searching) return null;
38
+ const clear = () => {
39
+ for (const c of menus) {
40
+ if (!pickedOf(c, view).active) continue;
41
+ onControl(c.id, c.kind === "multi" ? [] : c.neutral ?? c.options[0].id);
42
+ }
43
+ if (searchText) onSearch?.("");
44
+ };
45
+ const controlsOf = segmented.map((c) => withPicked(c, view));
46
+ if (!searching && menus.length === 0) return /* @__PURE__ */ jsx("div", {
47
+ className,
48
+ style,
49
+ children: /* @__PURE__ */ jsx(PanelControls, {
50
+ controls: controlsOf,
51
+ onChange: onControl
52
+ })
53
+ });
54
+ return /* @__PURE__ */ jsxs("div", {
55
+ className: `flex flex-wrap items-center gap-1.5 ${className}`,
56
+ style,
57
+ children: [
58
+ segmented.length > 0 && /* @__PURE__ */ jsx("div", {
59
+ className: "shrink-0",
60
+ children: /* @__PURE__ */ jsx(PanelControls, {
61
+ controls: controlsOf,
62
+ onChange: onControl
63
+ })
64
+ }),
65
+ searching && /* @__PURE__ */ jsx(SearchField, {
66
+ text: searchText,
67
+ placeholder: search.placeholder,
68
+ onSearch
69
+ }),
70
+ menus.map((c) => /* @__PURE__ */ jsx(FilterMenu, {
71
+ control: c,
72
+ view,
73
+ onChange: onControl
74
+ }, c.id)),
75
+ (filtering || searchText !== "") && /* @__PURE__ */ jsx("button", {
76
+ type: "button",
77
+ onClick: clear,
78
+ className: "h-6 px-1.5 bg-transparent border-0 rounded-zg-sm text-zg-meta text-[var(--zg-text-muted)] cursor-pointer whitespace-nowrap hover:text-[var(--zg-text)]",
79
+ children: labels.panelFiltersClear
80
+ })
81
+ ]
82
+ });
83
+ }
84
+ /** A segmented control showing the user's pick before the content for it lands. */
85
+ function withPicked(control, view) {
86
+ const own = view?.[control.id];
87
+ return typeof own === "string" && control.options.some((o) => o.id === own) ? {
88
+ ...control,
89
+ value: own
90
+ } : control;
91
+ }
92
+ /** The search field: it reloads once typing rests; Esc clears it. */
93
+ function SearchField({ text, placeholder, onSearch }) {
94
+ const labels = useWorkspaceLabels();
95
+ const [draft, setDraft] = useState(text);
96
+ const sent = useRef(text);
97
+ useEffect(() => {
98
+ if (text !== sent.current) {
99
+ sent.current = text;
100
+ setDraft(text);
101
+ }
102
+ }, [text]);
103
+ useEffect(() => {
104
+ if (draft === sent.current) return void 0;
105
+ const timer = setTimeout(() => {
106
+ sent.current = draft;
107
+ onSearch(draft);
108
+ }, 300);
109
+ return () => clearTimeout(timer);
110
+ }, [draft, onSearch]);
111
+ const name = placeholder ?? labels.panelSearch;
112
+ return /* @__PURE__ */ jsxs("label", {
113
+ className: "zg-well h-7 flex-1 min-w-[8rem] flex items-center gap-1.5 px-2 text-zg-ui",
114
+ children: [/* @__PURE__ */ jsx("span", {
115
+ "aria-hidden": true,
116
+ className: "i-ph-magnifying-glass shrink-0 text-[var(--zg-text-muted)]"
117
+ }), /* @__PURE__ */ jsx("input", {
118
+ type: "search",
119
+ value: draft,
120
+ onChange: (e) => setDraft(e.target.value),
121
+ onKeyDown: (e) => {
122
+ if (e.key !== "Escape" || draft === "") return;
123
+ e.stopPropagation();
124
+ sent.current = "";
125
+ setDraft("");
126
+ onSearch("");
127
+ },
128
+ placeholder: name,
129
+ "aria-label": name,
130
+ className: "flex-1 min-w-0 bg-transparent border-0 outline-none text-[var(--zg-text)] placeholder:text-[var(--zg-text-subtle)]"
131
+ })]
132
+ });
133
+ }
134
+ /** A `select` or `multi` control: a button saying what is picked, opening its options. */
135
+ function FilterMenu({ control, view, onChange }) {
136
+ const labels = useWorkspaceLabels();
137
+ const multi = control.kind === "multi";
138
+ const { picked, active } = pickedOf(control, view);
139
+ const names = active ? control.options.filter((o) => picked.includes(o.id)).map((o) => o.label) : [];
140
+ const summary = labels.panelFilterSummary(control.label, names);
141
+ return /* @__PURE__ */ jsx(Popover, {
142
+ ariaLabel: control.label,
143
+ role: "menu",
144
+ side: "bottom",
145
+ align: "start",
146
+ width: 240,
147
+ trigger: (props) => /* @__PURE__ */ jsxs("button", {
148
+ type: "button",
149
+ ...props,
150
+ title: summary,
151
+ "data-active": active ? "" : void 0,
152
+ className: `zg-press-subtle inline-flex items-center gap-1 h-6 max-w-[14rem] px-2 rounded-full text-zg-meta cursor-pointer ${active ? "zg-raised border-0 text-[var(--zg-text)]" : "bg-transparent border-0 text-[var(--zg-text-muted)] hover:text-[var(--zg-text)] hover:before:bg-[var(--zg-row-hover)]"}`,
153
+ children: [/* @__PURE__ */ jsx("span", {
154
+ className: "truncate",
155
+ children: summary
156
+ }), /* @__PURE__ */ jsx("span", {
157
+ "aria-hidden": true,
158
+ className: "i-ph-caret-down shrink-0 text-zg-badge"
159
+ })]
160
+ }),
161
+ children: (close) => /* @__PURE__ */ jsxs(Menu, {
162
+ "aria-label": control.label,
163
+ className: "max-h-80 overflow-y-auto",
164
+ children: [control.options.map((o) => {
165
+ const checked = picked.includes(o.id);
166
+ return /* @__PURE__ */ jsx(MenuRow, {
167
+ size: "sm",
168
+ role: multi ? "menuitemcheckbox" : "menuitemradio",
169
+ "aria-checked": checked,
170
+ checked,
171
+ ...o.icon?.gui ? { icon: /* @__PURE__ */ jsx("span", {
172
+ "aria-hidden": true,
173
+ className: o.icon.gui
174
+ }) } : {},
175
+ title: o.label,
176
+ onClick: () => {
177
+ if (multi) {
178
+ onChange(control.id, checked ? picked.filter((id) => id !== o.id) : [...picked, o.id]);
179
+ return;
180
+ }
181
+ if (!checked) onChange(control.id, o.id);
182
+ close();
183
+ }
184
+ }, o.id);
185
+ }), multi && picked.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuSeparator, {}), /* @__PURE__ */ jsx(MenuRow, {
186
+ size: "sm",
187
+ title: labels.panelFilterClear,
188
+ onClick: () => onChange(control.id, [])
189
+ })] })]
190
+ })
191
+ });
192
+ }
193
+ //#endregion
194
+ export { PANEL_SEARCH_DEBOUNCE_MS, PanelToolbar };
@@ -134,6 +134,8 @@ declare function useWorkspaceActions(): WorkspaceActions;
134
134
  declare function useOptionalWorkspaceActions(): WorkspaceActions | null;
135
135
  /** The catalog, strips and open state. Re-renders on navigation, not on content loads. */
136
136
  declare function useWorkspaceView(): WorkspaceView;
137
+ /** {@link useWorkspaceView}, or `null` outside a `WorkspaceProvider`. */
138
+ declare function useOptionalWorkspaceView(): WorkspaceView | null;
137
139
  /** The one-shot requests to tab bodies. */
138
140
  declare function useWorkspaceRequests(): WorkspaceRequests;
139
141
  /** Extension content and badges. Re-renders on every content load and badge change. */
@@ -152,4 +154,4 @@ declare function useWorkspace(): WorkspaceApi;
152
154
  */
153
155
  declare function useExtensionPanels(panels: WorkspacePanels | undefined, cwd: string | null): readonly WorkspacePanelSummary[];
154
156
  //#endregion
155
- export { WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, WorkspaceView, useExtensionPanels, useOptionalWorkspaceActions, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView };
157
+ export { WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, WorkspaceView, useExtensionPanels, useOptionalWorkspaceActions, useOptionalWorkspaceView, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView };
@@ -1,7 +1,7 @@
1
1
  import { LabelsProvider } from "../labels.js";
2
- import { EMPTY_CONTENT_ENTRY, bindingKey, createPanelContents } from "./headless/contents.js";
3
2
  import { activeTabOf } from "./headless/panel-tree.js";
4
3
  import { EMPTY_WORKSPACE_LAYOUT, createMemoryLayouts } from "./headless/layouts.js";
4
+ import { EMPTY_CONTENT_ENTRY, bindingKey, createPanelContents } from "./headless/contents.js";
5
5
  import { groupViews, sameAttention, viewOf } from "./headless/view.js";
6
6
  import { createWorkspaceOps } from "./headless/ops.js";
7
7
  import { useWorkspaceLabels } from "./useWorkspaceLabels.js";
@@ -339,6 +339,10 @@ function useOptionalWorkspaceActions() {
339
339
  function useWorkspaceView() {
340
340
  return required(useContext(ViewContext), "useWorkspaceView");
341
341
  }
342
+ /** {@link useWorkspaceView}, or `null` outside a `WorkspaceProvider`. */
343
+ function useOptionalWorkspaceView() {
344
+ return useContext(ViewContext);
345
+ }
342
346
  /** The one-shot requests to tab bodies. */
343
347
  function useWorkspaceRequests() {
344
348
  return required(useContext(RequestsContext), "useWorkspaceRequests");
@@ -409,4 +413,4 @@ function useExtensionPanels(panels, cwd) {
409
413
  return known ?? lastCatalog;
410
414
  }
411
415
  //#endregion
412
- export { WorkspaceHostProvider, WorkspaceProvider, useExtensionPanels, useOptionalWorkspaceActions, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView };
416
+ export { WorkspaceHostProvider, WorkspaceProvider, useExtensionPanels, useOptionalWorkspaceActions, useOptionalWorkspaceView, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView };
@@ -1,7 +1,7 @@
1
+ import { extensionsErrorOf } from "../extensions/headless/host.js";
1
2
  import { useExtensionsLabels } from "../extensions/useExtensionsLabels.js";
2
3
  import { ExtensionComponent } from "../extensions/ExtensionComponent.js";
3
4
  import { useExtensionEffectRouter, useExtensionItems, useExtensionsHost } from "../extensions/ExtensionsProvider.js";
4
- import { extensionsErrorOf } from "../extensions/headless/host.js";
5
5
  import { ExtensionWebview } from "../extensions/ExtensionWebview.js";
6
6
  import { PanelContentView } from "./PanelContentView.js";
7
7
  import { useWorkspaceView } from "./WorkspaceProvider.js";
@@ -38,13 +38,16 @@ function useWithExtensionTabs(tabs) {
38
38
  return useMemo(() => {
39
39
  const listed = new Set(tabs.map((t) => t.id));
40
40
  const added = [];
41
- for (const { item } of panels) {
41
+ for (const { item, extension } of panels) {
42
42
  if (!item.surfaces.includes("side") || listed.has(item.id)) continue;
43
43
  listed.add(item.id);
44
44
  added.push({
45
45
  id: item.id,
46
46
  label: item.label,
47
47
  ...item.icon ? { icon: item.icon } : {},
48
+ owner: extension,
49
+ ...item.ownerName ? { ownerName: item.ownerName } : {},
50
+ ...item.ownerIcon ? { ownerIcon: item.ownerIcon } : {},
48
51
  builtin: true,
49
52
  extension: "panel"
50
53
  });
@@ -42,6 +42,10 @@ interface WorkspacePanelSummary {
42
42
  label: string;
43
43
  /** The extension that registered it. */
44
44
  owner: string;
45
+ /** That extension's display name. */
46
+ ownerName?: string;
47
+ /** That extension's own icon: the tab draws its mark when the panel has no `icon`. */
48
+ ownerIcon?: PanelIcon;
45
49
  /** The tab icon. */
46
50
  icon?: PanelIcon;
47
51
  /** Where its side tab shows: with a session, on a folder alone, or both. */
@@ -6,9 +6,11 @@ import { PanelContentEntry, PanelGroupView, PanelSplitView, PanelViewNode, TabAt
6
6
  import { DEFAULT_WORKSPACE_LABELS, WorkspaceLabels } from "./labels.js";
7
7
  import { WorkspaceOps, WorkspaceOpsOptions, WorkspaceOpsView, createWorkspaceOps } from "./ops.js";
8
8
  import { CHAT_MIN, CHAT_MIN_SHARE, LayoutResolved, LayoutWants, OVERLAY_CHAT_PEEK, chatFloor, maxColumnWidth, panelDragResult, resolveLayout, splitPanelWidth } from "./resolve.js";
9
+ import { PanelTabMarkSource, panelTabMark } from "./tab-mark.js";
9
10
  import { EMPTY_CONTENT_ENTRY, PanelContentCache, PanelContents, bindingInFolder, bindingKey, createPanelContents } from "./contents.js";
11
+ import { PANEL_PAGED_ITEMS, appendPanelPage } from "./pages.js";
10
12
  import { EMPTY_LAYOUT, LAYOUT_STORAGE_KEY, LayoutMemory, LayoutMemoryOptions, LayoutPatch, LayoutScope, SessionLayout, createLayoutMemory, layoutProjectKey, layoutScopeKey, parseLayout } from "./layout-memory.js";
11
13
  import { OpenTarget, OpenTargetApi, OpenTargetDeps, OpenTargetEvent, createOpenTarget } from "./open-target.js";
12
14
  import { setPendingReveal, takePendingReveal } from "./reveal-at.js";
13
15
  import { PanelFocusEntry, PanelFocusStore, createPanelFocusStore } from "./panel-focus.js";
14
- export { CHAT_MIN, CHAT_MIN_SHARE, DEFAULT_WORKSPACE_LABELS, type DropZone, EMPTY_CONTENT_ENTRY, EMPTY_LAYOUT, EMPTY_WORKSPACE_LAYOUT, LAYOUT_STORAGE_KEY, LayoutMemory, LayoutMemoryOptions, LayoutPatch, LayoutResolved, LayoutScope, LayoutStorage, LayoutStoreOptions, LayoutWants, MemoryLayouts, OVERLAY_CHAT_PEEK, OpenTarget, OpenTargetApi, OpenTargetDeps, OpenTargetEvent, type PanelContent, PanelContentCache, PanelContentEntry, PanelContents, PanelFocusEntry, PanelFocusStore, type PanelGroup, PanelGroupView, type PanelLoadContextInput, type PanelNode, type PanelSplit, PanelSplitView, PanelTree, PanelViewNode, SessionLayout, type SplitDir, type SplitSide, StorageLayoutsOptions, TabAttention, TabDrop, WORKSPACE_STORAGE_KEY, WorkspaceHost, WorkspaceHostError, WorkspaceLabels, WorkspaceLayout, WorkspaceLayouts, WorkspaceOps, WorkspaceOpsOptions, WorkspaceOpsView, WorkspacePanelActionResult, WorkspacePanelChange, WorkspacePanelSummary, WorkspacePanels, WorkspaceScope, WorkspaceTab, activeTabOf, bindingInFolder, bindingKey, chatFloor, closeTab, createLayoutMemory, createMemoryLayouts, createOpenTarget, createPanelContents, createPanelFocusStore, createStorageLayouts, createWorkspaceOps, dropTab, findGroup, focusTab, focusedGroup, groupIdsOf, groupViews, groupsOf, layoutProjectKey, layoutScopeKey, maxColumnWidth, moveTabBy, panelDragResult, panelTreeOf, parseLayout, parsePanelTree, parseTree, parseWorkspaceLayout, pickTab, pinTab, resizeSplit, resolveLayout, sameAttention, sameWorkspaceLayout, setPendingReveal, splitPanelWidth, splitTab, splitView, takePendingReveal, treeTabs, viewOf };
16
+ export { CHAT_MIN, CHAT_MIN_SHARE, DEFAULT_WORKSPACE_LABELS, type DropZone, EMPTY_CONTENT_ENTRY, EMPTY_LAYOUT, EMPTY_WORKSPACE_LAYOUT, LAYOUT_STORAGE_KEY, LayoutMemory, LayoutMemoryOptions, LayoutPatch, LayoutResolved, LayoutScope, LayoutStorage, LayoutStoreOptions, LayoutWants, MemoryLayouts, OVERLAY_CHAT_PEEK, OpenTarget, OpenTargetApi, OpenTargetDeps, OpenTargetEvent, PANEL_PAGED_ITEMS, type PanelContent, PanelContentCache, PanelContentEntry, PanelContents, PanelFocusEntry, PanelFocusStore, type PanelGroup, PanelGroupView, type PanelLoadContextInput, type PanelNode, type PanelSplit, PanelSplitView, PanelTabMarkSource, PanelTree, PanelViewNode, SessionLayout, type SplitDir, type SplitSide, StorageLayoutsOptions, TabAttention, TabDrop, WORKSPACE_STORAGE_KEY, WorkspaceHost, WorkspaceHostError, WorkspaceLabels, WorkspaceLayout, WorkspaceLayouts, WorkspaceOps, WorkspaceOpsOptions, WorkspaceOpsView, WorkspacePanelActionResult, WorkspacePanelChange, WorkspacePanelSummary, WorkspacePanels, WorkspaceScope, WorkspaceTab, activeTabOf, appendPanelPage, bindingInFolder, bindingKey, chatFloor, closeTab, createLayoutMemory, createMemoryLayouts, createOpenTarget, createPanelContents, createPanelFocusStore, createStorageLayouts, createWorkspaceOps, dropTab, findGroup, focusTab, focusedGroup, groupIdsOf, groupViews, groupsOf, layoutProjectKey, layoutScopeKey, maxColumnWidth, moveTabBy, panelDragResult, panelTabMark, panelTreeOf, parseLayout, parsePanelTree, parseTree, parseWorkspaceLayout, pickTab, pinTab, resizeSplit, resolveLayout, sameAttention, sameWorkspaceLayout, setPendingReveal, splitPanelWidth, splitTab, splitView, takePendingReveal, treeTabs, viewOf };
@@ -1,13 +1,15 @@
1
- import { EMPTY_CONTENT_ENTRY, bindingInFolder, bindingKey, createPanelContents } from "./contents.js";
2
1
  import { findGroup, groupsOf, parseTree, treeTabs } from "./tree.js";
3
2
  import { activeTabOf, closeTab, dropTab, focusTab, focusedGroup, moveTabBy, panelTreeOf, parsePanelTree, pickTab, pinTab, resizeSplit, splitTab } from "./panel-tree.js";
4
3
  import { EMPTY_WORKSPACE_LAYOUT, WORKSPACE_STORAGE_KEY, createMemoryLayouts, createStorageLayouts, parseWorkspaceLayout, sameWorkspaceLayout } from "./layouts.js";
4
+ import { EMPTY_CONTENT_ENTRY, bindingInFolder, bindingKey, createPanelContents } from "./contents.js";
5
5
  import { groupIdsOf, groupViews, sameAttention, splitView, viewOf } from "./view.js";
6
6
  import { createWorkspaceOps } from "./ops.js";
7
7
  import { DEFAULT_WORKSPACE_LABELS } from "./labels.js";
8
+ import { panelTabMark } from "./tab-mark.js";
9
+ import { PANEL_PAGED_ITEMS, appendPanelPage } from "./pages.js";
8
10
  import { CHAT_MIN, CHAT_MIN_SHARE, OVERLAY_CHAT_PEEK, chatFloor, maxColumnWidth, panelDragResult, resolveLayout, splitPanelWidth } from "./resolve.js";
9
11
  import { EMPTY_LAYOUT, LAYOUT_STORAGE_KEY, createLayoutMemory, layoutProjectKey, layoutScopeKey, parseLayout } from "./layout-memory.js";
10
12
  import { setPendingReveal, takePendingReveal } from "./reveal-at.js";
11
13
  import { createOpenTarget } from "./open-target.js";
12
14
  import { createPanelFocusStore } from "./panel-focus.js";
13
- export { CHAT_MIN, CHAT_MIN_SHARE, DEFAULT_WORKSPACE_LABELS, EMPTY_CONTENT_ENTRY, EMPTY_LAYOUT, EMPTY_WORKSPACE_LAYOUT, LAYOUT_STORAGE_KEY, OVERLAY_CHAT_PEEK, WORKSPACE_STORAGE_KEY, activeTabOf, bindingInFolder, bindingKey, chatFloor, closeTab, createLayoutMemory, createMemoryLayouts, createOpenTarget, createPanelContents, createPanelFocusStore, createStorageLayouts, createWorkspaceOps, dropTab, findGroup, focusTab, focusedGroup, groupIdsOf, groupViews, groupsOf, layoutProjectKey, layoutScopeKey, maxColumnWidth, moveTabBy, panelDragResult, panelTreeOf, parseLayout, parsePanelTree, parseTree, parseWorkspaceLayout, pickTab, pinTab, resizeSplit, resolveLayout, sameAttention, sameWorkspaceLayout, setPendingReveal, splitPanelWidth, splitTab, splitView, takePendingReveal, treeTabs, viewOf };
15
+ export { CHAT_MIN, CHAT_MIN_SHARE, DEFAULT_WORKSPACE_LABELS, EMPTY_CONTENT_ENTRY, EMPTY_LAYOUT, EMPTY_WORKSPACE_LAYOUT, LAYOUT_STORAGE_KEY, OVERLAY_CHAT_PEEK, PANEL_PAGED_ITEMS, WORKSPACE_STORAGE_KEY, activeTabOf, appendPanelPage, bindingInFolder, bindingKey, chatFloor, closeTab, createLayoutMemory, createMemoryLayouts, createOpenTarget, createPanelContents, createPanelFocusStore, createStorageLayouts, createWorkspaceOps, dropTab, findGroup, focusTab, focusedGroup, groupIdsOf, groupViews, groupsOf, layoutProjectKey, layoutScopeKey, maxColumnWidth, moveTabBy, panelDragResult, panelTabMark, panelTreeOf, parseLayout, parsePanelTree, parseTree, parseWorkspaceLayout, pickTab, pinTab, resizeSplit, resolveLayout, sameAttention, sameWorkspaceLayout, setPendingReveal, splitPanelWidth, splitTab, splitView, takePendingReveal, treeTabs, viewOf };
@@ -31,6 +31,16 @@ declare const DEFAULT_WORKSPACE_LABELS: {
31
31
  panelRowExpand: string;
32
32
  panelRowCollapse: string;
33
33
  panelRowShowMore: string;
34
+ panelSearch: string;
35
+ panelSearchClear: string;
36
+ panelFiltersClear: string;
37
+ /** In a filter's menu: drop its picks. */
38
+ panelFilterClear: string;
39
+ /** A filter's button: its label, and what is picked (none: its label alone). */
40
+ panelFilterSummary: (label: string, picked: readonly string[]) => string;
41
+ panelLoadMore: string;
42
+ panelLoadingMore: string;
43
+ panelRowActions: (row: string) => string;
34
44
  };
35
45
  /** The shape of the workspace label dictionary. Some values are functions that build a string. */
36
46
  type WorkspaceLabels = typeof DEFAULT_WORKSPACE_LABELS;
@@ -37,7 +37,17 @@ const DEFAULT_WORKSPACE_LABELS = {
37
37
  },
38
38
  panelRowExpand: "Expand",
39
39
  panelRowCollapse: "Collapse",
40
- panelRowShowMore: "Show more"
40
+ panelRowShowMore: "Show more",
41
+ panelSearch: "Search",
42
+ panelSearchClear: "Clear search",
43
+ panelFiltersClear: "Clear filters",
44
+ /** In a filter's menu: drop its picks. */
45
+ panelFilterClear: "Clear",
46
+ /** A filter's button: its label, and what is picked (none: its label alone). */
47
+ panelFilterSummary: (label, picked) => picked.length === 0 ? label : picked.length === 1 ? `${label}: ${picked[0]}` : `${label}: ${picked.length}`,
48
+ panelLoadMore: "Show more",
49
+ panelLoadingMore: "Loading…",
50
+ panelRowActions: (row) => `Actions for ${row}`
41
51
  };
42
52
  //#endregion
43
53
  export { DEFAULT_WORKSPACE_LABELS };
@@ -0,0 +1,15 @@
1
+ import { PanelContent } from "zidane/chat/pure";
2
+ //#region src/workspace/headless/pages.d.ts
3
+ /** Rows kept across every page of one panel, at most: past it, Show more goes away. */
4
+ declare const PANEL_PAGED_ITEMS = 1000;
5
+ /**
6
+ * `base` with `page` after it: its rows join the sections of the same id (a row already shown, by id, is not
7
+ * repeated), its other sections follow, and its `more` replaces `base`'s. The header (title, controls, search)
8
+ * stays `base`'s. Past {@link PANEL_PAGED_ITEMS} rows, the rest is dropped and there is no more.
9
+ *
10
+ * @param base - The content shown.
11
+ * @param page - The next page.
12
+ */
13
+ declare function appendPanelPage(base: PanelContent, page: PanelContent): PanelContent;
14
+ //#endregion
15
+ export { PANEL_PAGED_ITEMS, appendPanelPage };
@@ -0,0 +1,49 @@
1
+ //#region src/workspace/headless/pages.ts
2
+ /** Rows kept across every page of one panel, at most: past it, Show more goes away. */
3
+ const PANEL_PAGED_ITEMS = 1e3;
4
+ const keyOf = (item) => item.kind === "row" && item.id ? item.id : null;
5
+ /**
6
+ * `base` with `page` after it: its rows join the sections of the same id (a row already shown, by id, is not
7
+ * repeated), its other sections follow, and its `more` replaces `base`'s. The header (title, controls, search)
8
+ * stays `base`'s. Past {@link PANEL_PAGED_ITEMS} rows, the rest is dropped and there is no more.
9
+ *
10
+ * @param base - The content shown.
11
+ * @param page - The next page.
12
+ */
13
+ function appendPanelPage(base, page) {
14
+ const seen = new Set(base.sections.flatMap((s) => s.items.map(keyOf).filter((k) => k !== null)));
15
+ let budget = PANEL_PAGED_ITEMS - base.sections.reduce((n, s) => n + s.items.length, 0);
16
+ const fresh = (items) => {
17
+ const out = [];
18
+ for (const item of items) {
19
+ const key = keyOf(item);
20
+ if (key !== null && seen.has(key)) continue;
21
+ if (budget <= 0) break;
22
+ if (key !== null) seen.add(key);
23
+ out.push(item);
24
+ budget--;
25
+ }
26
+ return out;
27
+ };
28
+ const byId = new Map(page.sections.map((s) => [s.id, s]));
29
+ const sections = base.sections.map((s) => {
30
+ const more = byId.get(s.id);
31
+ return more ? {
32
+ ...s,
33
+ items: [...s.items, ...fresh(more.items)]
34
+ } : s;
35
+ });
36
+ const known = new Set(base.sections.map((s) => s.id));
37
+ for (const s of page.sections) if (!known.has(s.id)) sections.push({
38
+ ...s,
39
+ items: fresh(s.items)
40
+ });
41
+ const { more: _more, ...rest } = base;
42
+ return {
43
+ ...rest,
44
+ sections,
45
+ ...page.more && budget > 0 ? { more: page.more } : {}
46
+ };
47
+ }
48
+ //#endregion
49
+ export { PANEL_PAGED_ITEMS, appendPanelPage };
@@ -0,0 +1,14 @@
1
+ import { WorkspaceTab } from "./view.js";
2
+ import { ExtensionMark } from "zidane/chat/pure";
3
+ //#region src/workspace/headless/tab-mark.d.ts
4
+ /** What {@link panelTabMark} reads of a tab. */
5
+ type PanelTabMarkSource = Pick<WorkspaceTab, 'icon' | 'owner' | 'ownerName' | 'ownerIcon'>;
6
+ /**
7
+ * The mark a panel's tab draws: `icon.gui` as an icon, else its extension's mark. `undefined`: a tab with
8
+ * neither (an app's tab without an icon).
9
+ *
10
+ * @param tab - The tab.
11
+ */
12
+ declare function panelTabMark(tab: PanelTabMarkSource): ExtensionMark | undefined;
13
+ //#endregion
14
+ export { PanelTabMarkSource, panelTabMark };
@@ -0,0 +1,27 @@
1
+ import { extensionMark } from "zidane/chat/pure";
2
+ //#region src/workspace/headless/tab-mark.ts
3
+ /**
4
+ * What a panel's tab draws: its own icon, else the mark of the extension that added it, the one the rest of
5
+ * the app draws for that extension (`extensionMark`: its icon, else its logo, else its first letter).
6
+ */
7
+ /**
8
+ * The mark a panel's tab draws: `icon.gui` as an icon, else its extension's mark. `undefined`: a tab with
9
+ * neither (an app's tab without an icon).
10
+ *
11
+ * @param tab - The tab.
12
+ */
13
+ function panelTabMark(tab) {
14
+ const own = tab.icon?.gui?.trim();
15
+ if (own) return {
16
+ kind: "icon",
17
+ gui: own
18
+ };
19
+ if (!tab.owner) return void 0;
20
+ return extensionMark({
21
+ id: tab.owner,
22
+ ...tab.ownerName ? { name: tab.ownerName } : {},
23
+ ...tab.ownerIcon ? { icon: tab.ownerIcon } : {}
24
+ });
25
+ }
26
+ //#endregion
27
+ export { panelTabMark };
@@ -12,8 +12,14 @@ interface WorkspaceTab {
12
12
  id: string;
13
13
  /** The pill's label. */
14
14
  label: string;
15
- /** The pill's icon (`gui`: an icon class). */
15
+ /** The pill's icon (`gui`: an icon class). Absent on an extension's panel: its extension's mark ({@link panelTabMark}). */
16
16
  icon?: PanelIcon;
17
+ /** The extension that added it (its id): the tab draws the extension's mark when it has no `icon.gui`. */
18
+ owner?: string;
19
+ /** That extension's display name. */
20
+ ownerName?: string;
21
+ /** That extension's own icon. */
22
+ ownerIcon?: PanelIcon;
17
23
  /** The app renders it; `false`: an extension panel. */
18
24
  builtin: boolean;
19
25
  /**
@@ -1,7 +1,8 @@
1
- import { WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, WorkspaceView, useExtensionPanels, useOptionalWorkspaceActions, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView } from "./WorkspaceProvider.js";
1
+ import { WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, WorkspaceView, useExtensionPanels, useOptionalWorkspaceActions, useOptionalWorkspaceView, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView } from "./WorkspaceProvider.js";
2
2
  import { PanelContentView, PanelContentViewProps, PanelOpenEvent, PanelSessionRowProps, PanelSessionRowProvider, PanelSessionRowProviderProps, detailsPreview } from "./PanelContentView.js";
3
3
  import { BudgetColumn, LayoutBudget, LayoutBudgetProvider, LayoutBudgetProviderProps, useLayoutBudget, useLayoutBudgetOptional } from "./LayoutBudget.js";
4
4
  import { PanelCorner, PanelCornerProps, panelCornerWidth } from "./PanelCorner.js";
5
+ import { PanelGlyph, PanelGlyphProps } from "./PanelGlyph.js";
5
6
  import { StripControls } from "./PanelStrip.js";
6
7
  import { WorkspaceTabRegistry } from "./TabParking.js";
7
8
  import { SidePanel, SidePanelProps } from "./SidePanel.js";
@@ -11,4 +12,4 @@ import { useScopeLayout } from "./useScopeLayout.js";
11
12
  import { TAB_REGION_PROPS, TabScope, TabTarget, focusedTabScope, hoveredTabScope, keepFocusIn, panelTabScope, registerTabScope, steppedIndex, tabTarget, trackTabRegions, useTabScope } from "./tab-scopes.js";
12
13
  import { usePanelGroupHandlers } from "./panel-group-keys.js";
13
14
  import { WorkspaceKeyCommandsOptions, useWorkspaceKeyCommands } from "./workspace-key-commands.js";
14
- export { BudgetColumn, LayoutBudget, LayoutBudgetProvider, LayoutBudgetProviderProps, PanelContentView, PanelContentViewProps, PanelCorner, PanelCornerProps, PanelOpenEvent, PanelSessionRowProps, PanelSessionRowProvider, PanelSessionRowProviderProps, PanelVisibilityContext, SidePanel, SidePanelProps, type StripControls, TAB_REGION_PROPS, TabScope, TabTarget, WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceKeyCommandsOptions, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, type WorkspaceTabRegistry, WorkspaceView, detailsPreview, focusedTabScope, hoveredTabScope, keepFocusIn, panelCornerWidth, panelTabScope, registerTabScope, steppedIndex, tabTarget, touchRecent, trackTabRegions, useExtensionPanels, useLayoutBudget, useLayoutBudgetOptional, useOptionalWorkspaceActions, usePanelGroupHandlers, usePanelVisible, useScopeLayout, useTabScope, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceKeyCommands, useWorkspaceLabels, useWorkspaceRequests, useWorkspaceView };
15
+ export { BudgetColumn, LayoutBudget, LayoutBudgetProvider, LayoutBudgetProviderProps, PanelContentView, PanelContentViewProps, PanelCorner, PanelCornerProps, PanelGlyph, PanelGlyphProps, PanelOpenEvent, PanelSessionRowProps, PanelSessionRowProvider, PanelSessionRowProviderProps, PanelVisibilityContext, SidePanel, SidePanelProps, type StripControls, TAB_REGION_PROPS, TabScope, TabTarget, WorkspaceActions, WorkspaceApi, WorkspaceContent, WorkspaceContentTarget, WorkspaceHostProvider, WorkspaceHostProviderProps, WorkspaceKeyCommandsOptions, WorkspaceProvider, WorkspaceProviderProps, WorkspaceRequests, type WorkspaceTabRegistry, WorkspaceView, detailsPreview, focusedTabScope, hoveredTabScope, keepFocusIn, panelCornerWidth, panelTabScope, registerTabScope, steppedIndex, tabTarget, touchRecent, trackTabRegions, useExtensionPanels, useLayoutBudget, useLayoutBudgetOptional, useOptionalWorkspaceActions, useOptionalWorkspaceView, usePanelGroupHandlers, usePanelVisible, useScopeLayout, useTabScope, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceKeyCommands, useWorkspaceLabels, useWorkspaceRequests, useWorkspaceView };
@@ -1,12 +1,13 @@
1
1
  import { useWorkspaceLabels } from "./useWorkspaceLabels.js";
2
2
  import { PanelContentView, PanelSessionRowProvider, detailsPreview } from "./PanelContentView.js";
3
- import { WorkspaceHostProvider, WorkspaceProvider, useExtensionPanels, useOptionalWorkspaceActions, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView } from "./WorkspaceProvider.js";
3
+ import { WorkspaceHostProvider, WorkspaceProvider, useExtensionPanels, useOptionalWorkspaceActions, useOptionalWorkspaceView, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceRequests, useWorkspaceView } from "./WorkspaceProvider.js";
4
4
  import { TAB_REGION_PROPS, focusedTabScope, hoveredTabScope, keepFocusIn, panelTabScope, registerTabScope, steppedIndex, tabTarget, trackTabRegions, useTabScope } from "./tab-scopes.js";
5
5
  import { PanelVisibilityContext, touchRecent, usePanelVisible } from "./panel-visibility.js";
6
+ import { PanelGlyph } from "./PanelGlyph.js";
6
7
  import { LayoutBudgetProvider, useLayoutBudget, useLayoutBudgetOptional } from "./LayoutBudget.js";
7
8
  import { PanelCorner, panelCornerWidth } from "./PanelCorner.js";
8
9
  import { SidePanel } from "./SidePanel.js";
9
10
  import { useScopeLayout } from "./useScopeLayout.js";
10
11
  import { usePanelGroupHandlers } from "./panel-group-keys.js";
11
12
  import { useWorkspaceKeyCommands } from "./workspace-key-commands.js";
12
- export { LayoutBudgetProvider, PanelContentView, PanelCorner, PanelSessionRowProvider, PanelVisibilityContext, SidePanel, TAB_REGION_PROPS, WorkspaceHostProvider, WorkspaceProvider, detailsPreview, focusedTabScope, hoveredTabScope, keepFocusIn, panelCornerWidth, panelTabScope, registerTabScope, steppedIndex, tabTarget, touchRecent, trackTabRegions, useExtensionPanels, useLayoutBudget, useLayoutBudgetOptional, useOptionalWorkspaceActions, usePanelGroupHandlers, usePanelVisible, useScopeLayout, useTabScope, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceKeyCommands, useWorkspaceLabels, useWorkspaceRequests, useWorkspaceView };
13
+ export { LayoutBudgetProvider, PanelContentView, PanelCorner, PanelGlyph, PanelSessionRowProvider, PanelVisibilityContext, SidePanel, TAB_REGION_PROPS, WorkspaceHostProvider, WorkspaceProvider, detailsPreview, focusedTabScope, hoveredTabScope, keepFocusIn, panelCornerWidth, panelTabScope, registerTabScope, steppedIndex, tabTarget, touchRecent, trackTabRegions, useExtensionPanels, useLayoutBudget, useLayoutBudgetOptional, useOptionalWorkspaceActions, useOptionalWorkspaceView, usePanelGroupHandlers, usePanelVisible, useScopeLayout, useTabScope, useWorkspace, useWorkspaceActions, useWorkspaceContent, useWorkspaceHost, useWorkspaceKeyCommands, useWorkspaceLabels, useWorkspaceRequests, useWorkspaceView };
@@ -108,6 +108,12 @@ const DEMO_WORKSPACE_TABS = [
108
108
  label: "Terminal",
109
109
  icon: { gui: "i-ph-terminal-window" },
110
110
  builtin: true
111
+ },
112
+ {
113
+ id: "inbox",
114
+ label: "Inbox",
115
+ icon: { gui: "i-ph-tray" },
116
+ builtin: true
111
117
  }
112
118
  ];
113
119
  /** A demo layout: open, Files and Todos in one group, Diffs split off to the right. */
@@ -1,7 +1,7 @@
1
1
  import { useWorkspaceActions, useWorkspaceView } from "./WorkspaceProvider.js";
2
2
  import { focusedTabScope, keepFocusIn, panelTabScope, tabTarget, trackTabRegions } from "./tab-scopes.js";
3
3
  import { useCallback, useEffect, useMemo } from "react";
4
- import { TODOS_PANEL_ID } from "zidane/chat/pure";
4
+ import { INBOX_PANEL_ID } from "zidane/chat/pure";
5
5
  //#region src/workspace/workspace-key-commands.ts
6
6
  /**
7
7
  * The side panel's key commands: show or hide the panel (`openArtifacts`),
@@ -69,8 +69,8 @@ function useWorkspaceKeyCommands(options) {
69
69
  return useMemo(() => ({
70
70
  ...toggle ? { openArtifacts: () => actions.toggle() } : {},
71
71
  ...session ? { openTodos: () => {
72
- if (activeId === TODOS_PANEL_ID) actions.close();
73
- else actions.pick(TODOS_PANEL_ID);
72
+ if (activeId === INBOX_PANEL_ID) actions.close();
73
+ else actions.pick(INBOX_PANEL_ID);
74
74
  } } : {},
75
75
  ...scope ? {
76
76
  panelNext: () => actions.cycle(1),