@nextclaw/ui 0.14.4 → 0.15.0

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 (194) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/assets/api-DLtsiBKm.js +13 -0
  3. package/dist/assets/appearance-settings-page-CtkeYXux.js +1 -0
  4. package/dist/assets/book-open-BQwWBDKb.js +1 -0
  5. package/dist/assets/channels-list-page-Cv8o9Hxg.js +8 -0
  6. package/dist/assets/chat-page-BtA7H1Ea.js +1 -0
  7. package/dist/assets/{config-split-page-CRhlZ_pH.js → config-split-page-EidRBpP2.js} +1 -1
  8. package/dist/assets/confirm-dialog-iIJFFp2T.js +5 -0
  9. package/dist/assets/desktop-update-config-C6n_TB1r.js +1 -0
  10. package/dist/assets/{dist-CikkhOlq.js → dist-CBxWOazz.js} +1 -1
  11. package/dist/assets/{dist-PVA_xL1D.js → dist-D2eZC3OR.js} +1 -1
  12. package/dist/assets/doc-browser-B76XOXmW.js +1 -0
  13. package/dist/assets/doc-browser-DBnj9UMB.js +1 -0
  14. package/dist/assets/{doc-browser-context-MTw4EvKd.js → doc-browser-context-Sv-044Cl.js} +1 -1
  15. package/dist/assets/{ellipsis-y6vDtq4M.js → ellipsis-2uzhypw5.js} +1 -1
  16. package/dist/assets/es2015-DhQo_oS6.js +41 -0
  17. package/dist/assets/{external-link-DVJIQf_G.js → external-link-CM5dnUI6.js} +1 -1
  18. package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
  19. package/dist/assets/i18n-provider-di1waTGX.js +1 -0
  20. package/dist/assets/index-CBRP-7-a.css +1 -0
  21. package/dist/assets/index-Z1fYpbNd.js +104 -0
  22. package/dist/assets/loader-circle-BjmN5DeJ.js +1 -0
  23. package/dist/assets/mcp-marketplace-page-C_MVQUbT.js +9 -0
  24. package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +1 -0
  25. package/dist/assets/model-config-page-Cdr9P6TQ.js +1 -0
  26. package/dist/assets/plus-3ztRZD_l.js +1 -0
  27. package/dist/assets/provider-scoped-model-input-g1WkGiJ8.js +1 -0
  28. package/dist/assets/providers-config-page-CWlO7J4A.js +1 -0
  29. package/dist/assets/{react-VNqT5Zp7.js → react-DjpzoZXB.js} +1 -1
  30. package/dist/assets/refresh-cw-DaTv3zxE.js +1 -0
  31. package/dist/assets/remote-CKWZfVVB.js +1 -0
  32. package/dist/assets/rotate-cw-CHcaRUDz.js +1 -0
  33. package/dist/assets/runtime-config-page-CfTqh1nv.js +1 -0
  34. package/dist/assets/{save-BOJ477H-.js → save-BZof1uie.js} +1 -1
  35. package/dist/assets/{search-BEti5beA.js → search-MxAarS_g.js} +1 -1
  36. package/dist/assets/search-config-page-Dm8daYny.js +1 -0
  37. package/dist/assets/{secrets-config-page--IzrIOid.js → secrets-config-page-D8AbcR05.js} +2 -2
  38. package/dist/assets/security-config-Ds0LsJmo.js +1 -0
  39. package/dist/assets/select-D7GcNP0z.js +1 -0
  40. package/dist/assets/setting-row-CuozlnJd.js +1 -0
  41. package/dist/assets/side-dock-BcLau5S_.js +3 -0
  42. package/dist/assets/skeleton-shfmXuYp.js +1 -0
  43. package/dist/assets/switch-C1BWMU5c.js +1 -0
  44. package/dist/assets/tag-chip-DCUrQUCP.js +1 -0
  45. package/dist/assets/tooltip-DfAHHA76.js +1 -0
  46. package/dist/assets/use-config-DAdbT5QJ.js +1 -0
  47. package/dist/assets/use-confirm-dialog-eWcJ6V-s.js +1 -0
  48. package/dist/assets/x-DqL1mYLC.js +1 -0
  49. package/dist/index.html +53 -17
  50. package/index.html +25 -2
  51. package/package.json +9 -9
  52. package/src/app/components/layout/__tests__/app-layout.test.tsx +27 -0
  53. package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +9 -1
  54. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +6 -3
  55. package/src/app/components/layout/app-layout.tsx +10 -3
  56. package/src/app/components/layout/sidebar-items.tsx +8 -8
  57. package/src/app/components/layout/sidebar-rail.styles.ts +4 -2
  58. package/src/app/components/layout/sidebar.tsx +21 -28
  59. package/src/app/configs/app-navigation.config.ts +11 -5
  60. package/src/app/index.tsx +8 -0
  61. package/src/app/managers/__tests__/viewport-layout.manager.test.ts +59 -0
  62. package/src/app/managers/viewport-layout.manager.ts +39 -0
  63. package/src/app/presenters/app.presenter.ts +25 -2
  64. package/src/app/styles/design-system.css +91 -4
  65. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +86 -0
  66. package/src/features/chat/components/conversation/chat-conversation-content.tsx +47 -17
  67. package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +7 -0
  68. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +9 -5
  69. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +5 -5
  70. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +51 -2
  71. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +170 -1
  72. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +23 -1
  73. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +24 -7
  74. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +96 -0
  75. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +181 -0
  76. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +351 -89
  77. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +29 -4
  78. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +28 -3
  79. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +108 -8
  80. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +50 -14
  81. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +77 -6
  82. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +21 -12
  83. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +166 -0
  84. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +19 -11
  85. package/src/features/chat/features/message/components/chat-message-list.container.tsx +38 -64
  86. package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +59 -0
  87. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +56 -1
  88. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +0 -2
  89. package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +80 -0
  90. package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +12 -3
  91. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +60 -0
  92. package/src/features/chat/features/message/utils/chat-message.utils.ts +3 -0
  93. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +39 -0
  94. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -2
  95. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +1 -1
  96. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +3 -1
  97. package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
  98. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +270 -115
  99. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +121 -0
  100. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +75 -20
  101. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +70 -5
  102. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +83 -0
  103. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +79 -1
  104. package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +2 -0
  105. package/src/features/chat/managers/chat-thread.manager.ts +9 -3
  106. package/src/features/chat/managers/chat-ui.manager.ts +5 -1
  107. package/src/features/chat/presenters/chat.presenter.ts +2 -1
  108. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +4 -0
  109. package/src/features/chat/stores/chat-thread.store.ts +6 -64
  110. package/src/features/chat/types/chat-message.types.ts +4 -0
  111. package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +33 -26
  112. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
  113. package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +1 -0
  114. package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.tsx +115 -0
  115. package/src/features/marketplace/components/marketplace-detail-doc.tsx +441 -0
  116. package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +3 -3
  117. package/src/features/marketplace/components/mcp/mcp-marketplace-doc.ts +0 -60
  118. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +64 -29
  119. package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +42 -31
  120. package/src/features/marketplace/index.ts +3 -0
  121. package/src/features/marketplace/stores/marketplace-detail-doc.store.ts +38 -0
  122. package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +32 -2
  123. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +11 -0
  124. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
  125. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -0
  126. package/src/features/settings/pages/appearance-settings-page.tsx +40 -0
  127. package/src/features/side-dock/components/__tests__/side-dock.test.tsx +44 -2
  128. package/src/features/side-dock/components/side-dock.tsx +100 -13
  129. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +10 -0
  130. package/src/features/side-dock/managers/__tests__/side-dock.manager.test.ts +32 -2
  131. package/src/features/side-dock/managers/side-dock.manager.ts +17 -7
  132. package/src/features/side-dock/stores/side-dock.store.ts +9 -0
  133. package/src/features/side-dock/types/side-dock.types.ts +10 -3
  134. package/src/features/side-dock/utils/side-dock-item.utils.ts +15 -2
  135. package/src/index.css +501 -27
  136. package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
  137. package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +30 -1
  138. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +206 -38
  139. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +34 -11
  140. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -1
  141. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +23 -5
  142. package/src/shared/components/doc-browser/doc-browser.tsx +128 -75
  143. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +9 -1
  144. package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +83 -0
  145. package/src/shared/components/ui/__tests__/dialog.test.tsx +27 -0
  146. package/src/shared/components/ui/actions/icon-action-button.tsx +14 -2
  147. package/src/shared/components/ui/dialog.tsx +2 -2
  148. package/src/shared/components/ui/popover.tsx +1 -1
  149. package/src/shared/components/ui/select.tsx +1 -1
  150. package/src/shared/components/ui/switch.tsx +4 -3
  151. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +54 -0
  152. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +4 -5
  153. package/src/shared/components/ui/tooltip.tsx +1 -1
  154. package/src/shared/lib/api/utils/server-path.utils.ts +27 -1
  155. package/src/shared/lib/i18n/locales/en-US/chat.json +15 -6
  156. package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
  157. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -0
  158. package/src/shared/lib/i18n/locales/en-US/marketplace.json +5 -0
  159. package/src/shared/lib/i18n/locales/zh-CN/chat.json +15 -6
  160. package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
  161. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -0
  162. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +5 -0
  163. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +14 -0
  164. package/src/shared/lib/session-project/index.ts +1 -1
  165. package/src/shared/lib/session-project/{workspace-file-breadcrumb.ts → workspace-file-breadcrumb.utils.ts} +62 -8
  166. package/src/shared/lib/theme/index.ts +51 -13
  167. package/dist/assets/api-BP_8IaxB.js +0 -13
  168. package/dist/assets/channels-list-page-DEh5Jjqx.js +0 -8
  169. package/dist/assets/chat-page-BRB0N5WW.js +0 -105
  170. package/dist/assets/confirm-dialog-DsivFJ4r.js +0 -5
  171. package/dist/assets/desktop-update-config-B0w9p2ep.js +0 -1
  172. package/dist/assets/doc-browser-BoNJ7-tE.js +0 -1
  173. package/dist/assets/doc-browser-CdJ_d4ik.js +0 -1
  174. package/dist/assets/index-BiE-K8-q.css +0 -1
  175. package/dist/assets/index-D9BAE_L_.js +0 -105
  176. package/dist/assets/mcp-marketplace-page-B1SZNY5m.js +0 -40
  177. package/dist/assets/mcp-marketplace-page-Cq_Tw42T.js +0 -1
  178. package/dist/assets/model-config-page-DotaTUHb.js +0 -1
  179. package/dist/assets/plus-BbZqquJe.js +0 -1
  180. package/dist/assets/provider-scoped-model-input-DsFRuWJH.js +0 -1
  181. package/dist/assets/providers-config-page-DW4tQSLN.js +0 -1
  182. package/dist/assets/remote-B8TiUfwW.js +0 -1
  183. package/dist/assets/rotate-cw-DjHDTkCv.js +0 -1
  184. package/dist/assets/runtime-config-page-S6ErRHR8.js +0 -1
  185. package/dist/assets/search-config-page-DGj5am03.js +0 -1
  186. package/dist/assets/select-hoj8dK4K.js +0 -41
  187. package/dist/assets/tag-chip-T8-h-die.js +0 -1
  188. package/dist/assets/use-config-CjRV2hwL.js +0 -1
  189. package/dist/assets/x-D9PXs_xP.js +0 -1
  190. package/src/features/marketplace/components/detail-doc/__tests__/marketplace-detail-doc.test.ts +0 -40
  191. package/src/features/marketplace/components/detail-doc/marketplace-detail-doc-renderer.ts +0 -201
  192. package/src/features/marketplace/components/marketplace-detail-doc.ts +0 -142
  193. /package/dist/assets/{config-hints-BkYBc7z0.js → config-hints-fGnUjDe9.js} +0 -0
  194. /package/dist/assets/{middleware-CkFT3R4v.js → middleware-BILWbJE4.js} +0 -0
@@ -1,9 +1,24 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import {
3
+ getSideDockBuiltInItems,
4
+ SIDE_DOCK_GITHUB_PROJECT_ITEM_ID,
5
+ SIDE_DOCK_GITHUB_PROJECT_URL,
6
+ } from '@/features/side-dock/configs/side-dock-built-in-items.config';
2
7
  import { SideDockManager } from '@/features/side-dock/managers/side-dock.manager';
3
8
  import { useSideDockStore } from '@/features/side-dock/stores/side-dock.store';
4
9
  import type { SideDockItem } from '@/features/side-dock/types/side-dock.types';
5
10
  import type { DocBrowserManager } from '@/shared/components/doc-browser/managers/doc-browser.manager';
6
11
 
12
+ const mocks = vi.hoisted(() => ({
13
+ openExternalUrl: vi.fn(),
14
+ }));
15
+
16
+ vi.mock('@/shared/lib/host-capabilities', () => ({
17
+ hostCapabilityManager: {
18
+ openExternalUrl: mocks.openExternalUrl,
19
+ },
20
+ }));
21
+
7
22
  const customItem: SideDockItem = {
8
23
  builtIn: false,
9
24
  icon: { type: 'builtin', name: 'docs' },
@@ -16,14 +31,17 @@ const customItem: SideDockItem = {
16
31
  describe('SideDockManager', () => {
17
32
  beforeEach(() => {
18
33
  window.localStorage.clear();
34
+ mocks.openExternalUrl.mockReset();
35
+ mocks.openExternalUrl.mockResolvedValue({ opened: true });
36
+ useSideDockStore.getState().setVisible(true);
19
37
  useSideDockStore.getState().setPinnedItems([]);
20
38
  });
21
39
 
22
- it('opens right panel resource items through DocBrowserManager', () => {
40
+ it('opens right panel resource items through DocBrowserManager', async () => {
23
41
  const open = vi.fn();
24
42
  const manager = new SideDockManager({ open } as unknown as DocBrowserManager);
25
43
 
26
- manager.openItem(customItem);
44
+ await manager.openItem(customItem);
27
45
 
28
46
  expect(open).toHaveBeenCalledWith('nextclaw://docs/custom', {
29
47
  dockIcon: { type: 'builtin', name: 'docs' },
@@ -31,6 +49,18 @@ describe('SideDockManager', () => {
31
49
  });
32
50
  });
33
51
 
52
+ it('opens the GitHub project shortcut through host capabilities', async () => {
53
+ const open = vi.fn();
54
+ const manager = new SideDockManager({ open } as unknown as DocBrowserManager);
55
+ const githubItem = getSideDockBuiltInItems().find((item) => item.id === SIDE_DOCK_GITHUB_PROJECT_ITEM_ID);
56
+
57
+ expect(githubItem).toBeTruthy();
58
+ await manager.openItem(githubItem as SideDockItem);
59
+
60
+ expect(open).not.toHaveBeenCalled();
61
+ expect(mocks.openExternalUrl).toHaveBeenCalledWith(SIDE_DOCK_GITHUB_PROJECT_URL);
62
+ });
63
+
34
64
  it('pins removable items and keeps built-in items immutable', () => {
35
65
  const manager = new SideDockManager({ open: vi.fn() } as unknown as DocBrowserManager);
36
66
 
@@ -10,6 +10,7 @@ import {
10
10
  createPinnedSideDockItem,
11
11
  mergeSideDockItems,
12
12
  } from '@/features/side-dock/utils/side-dock-item.utils';
13
+ import { hostCapabilityManager } from '@/shared/lib/host-capabilities';
13
14
  import type { DocBrowserManager } from '@/shared/components/doc-browser/managers/doc-browser.manager';
14
15
  import type {
15
16
  DocBrowserDockIcon,
@@ -20,6 +21,7 @@ const SIDE_DOCK_PINNED_ITEM_ID_PREFIX = 'pinned';
20
21
  const SIDE_DOCK_ICON_NAMES = new Set<SideDockIconName>([
21
22
  'apps',
22
23
  'docs',
24
+ 'github',
23
25
  'new-tab',
24
26
  'panel-app',
25
27
  'service-apps',
@@ -78,12 +80,16 @@ export class SideDockManager {
78
80
  useSideDockStore.getState().pinnedItems,
79
81
  );
80
82
 
81
- openItem = (item: Pick<SideDockItem, 'icon' | 'label' | 'target'>): void => {
83
+ openItem = async (item: Pick<SideDockItem, 'icon' | 'label' | 'target'>): Promise<void> => {
82
84
  if (item.target.type === 'right-panel-resource') {
83
85
  this.docBrowserManager.open(item.target.uri, {
84
86
  dockIcon: item.icon,
85
87
  title: item.label,
86
88
  });
89
+ return;
90
+ }
91
+ if (item.target.type === 'external-url') {
92
+ await hostCapabilityManager.openExternalUrl(item.target.url);
87
93
  }
88
94
  };
89
95
 
@@ -130,18 +136,19 @@ export class SideDockManager {
130
136
  };
131
137
 
132
138
  pinItem = (item: SideDockItem, createdAt: string = new Date().toISOString()): void => {
133
- if (!item.removable) {
139
+ if (!item.removable || item.target.type !== 'right-panel-resource') {
134
140
  return;
135
141
  }
136
142
  const uri = normalizeSideDockResourceUri(item.target.uri);
143
+ const resourceItem = {
144
+ ...item,
145
+ target: { type: 'right-panel-resource' as const, uri },
146
+ };
137
147
  this.setPinnedItems((items) => {
138
148
  if (items.some((pinnedItem) => pinnedItem.id === item.id || normalizeSideDockResourceUri(pinnedItem.target.uri) === uri)) {
139
149
  return items;
140
150
  }
141
- return [...items, createPinnedSideDockItem({
142
- ...item,
143
- target: { ...item.target, uri },
144
- }, createdAt)];
151
+ return [...items, createPinnedSideDockItem(resourceItem, createdAt)];
145
152
  });
146
153
  };
147
154
 
@@ -176,6 +183,9 @@ export class SideDockManager {
176
183
 
177
184
  private findItemByUri = (uri: string): SideDockItem | undefined => {
178
185
  const normalized = normalizeSideDockResourceUri(uri);
179
- return this.getItems().find((item) => normalizeSideDockResourceUri(item.target.uri) === normalized);
186
+ return this.getItems().find((item) => (
187
+ item.target.type === 'right-panel-resource'
188
+ && normalizeSideDockResourceUri(item.target.uri) === normalized
189
+ ));
180
190
  };
181
191
  }
@@ -7,7 +7,9 @@ const SIDE_DOCK_STORAGE_KEY = 'nextclaw.side-dock.state';
7
7
  const SIDE_DOCK_STORAGE_VERSION = 1;
8
8
 
9
9
  type SideDockStore = {
10
+ isVisible: boolean;
10
11
  pinnedItems: SideDockPinnedItem[];
12
+ setVisible: (isVisible: boolean) => void;
11
13
  setPinnedItems: (items: SideDockPinnedItem[]) => void;
12
14
  };
13
15
 
@@ -18,7 +20,9 @@ function isRecord(value: unknown): value is Record<string, unknown> {
18
20
  export const useSideDockStore = create<SideDockStore>()(
19
21
  persist(
20
22
  (set) => ({
23
+ isVisible: true,
21
24
  pinnedItems: [],
25
+ setVisible: (isVisible) => set({ isVisible }),
22
26
  setPinnedItems: (items) => set({ pinnedItems: normalizeSideDockPinnedItems(items) }),
23
27
  }),
24
28
  {
@@ -26,14 +30,19 @@ export const useSideDockStore = create<SideDockStore>()(
26
30
  version: SIDE_DOCK_STORAGE_VERSION,
27
31
  storage: createJSONStorage(() => window.localStorage),
28
32
  partialize: (state) => ({
33
+ isVisible: state.isVisible,
29
34
  pinnedItems: state.pinnedItems,
30
35
  }),
31
36
  merge: (persistedState, currentState) => {
32
37
  const pinnedItems = isRecord(persistedState)
33
38
  ? normalizeSideDockPinnedItems(persistedState.pinnedItems)
34
39
  : [];
40
+ const isVisible = isRecord(persistedState) && typeof persistedState.isVisible === 'boolean'
41
+ ? persistedState.isVisible
42
+ : true;
35
43
  return {
36
44
  ...currentState,
45
+ isVisible,
37
46
  pinnedItems,
38
47
  };
39
48
  },
@@ -1,11 +1,18 @@
1
1
  export type SideDockItemId = string;
2
2
 
3
- export type SideDockItemTarget = {
3
+ export type SideDockResourceItemTarget = {
4
4
  type: 'right-panel-resource';
5
5
  uri: string;
6
6
  };
7
7
 
8
- export type SideDockIconName = 'apps' | 'docs' | 'new-tab' | 'panel-app' | 'service-apps';
8
+ export type SideDockExternalUrlTarget = {
9
+ type: 'external-url';
10
+ url: string;
11
+ };
12
+
13
+ export type SideDockItemTarget = SideDockResourceItemTarget | SideDockExternalUrlTarget;
14
+
15
+ export type SideDockIconName = 'apps' | 'docs' | 'github' | 'new-tab' | 'panel-app' | 'service-apps';
9
16
 
10
17
  export type SideDockItemIcon =
11
18
  | { type: 'builtin'; name: SideDockIconName }
@@ -26,7 +33,7 @@ export type SideDockPinnedItem = {
26
33
  icon: SideDockItemIcon;
27
34
  id: SideDockItemId;
28
35
  label: string;
29
- target: SideDockItemTarget;
36
+ target: SideDockResourceItemTarget;
30
37
  };
31
38
 
32
39
  export type SideDockResourceDockState = {
@@ -3,11 +3,13 @@ import type {
3
3
  SideDockItem,
4
4
  SideDockItemIcon,
5
5
  SideDockPinnedItem,
6
+ SideDockResourceItemTarget,
6
7
  } from '@/features/side-dock/types/side-dock.types';
7
8
 
8
9
  const SIDE_DOCK_BUILTIN_ICON_NAMES: SideDockIconName[] = [
9
10
  'apps',
10
11
  'docs',
12
+ 'github',
11
13
  'new-tab',
12
14
  'panel-app',
13
15
  'service-apps',
@@ -99,7 +101,10 @@ export function normalizeSideDockPinnedItems(value: unknown): SideDockPinnedItem
99
101
  return Array.from(itemsById.values());
100
102
  }
101
103
 
102
- export function createPinnedSideDockItem(item: SideDockItem, createdAt: string): SideDockPinnedItem {
104
+ export function createPinnedSideDockItem(
105
+ item: SideDockItem & { target: SideDockResourceItemTarget },
106
+ createdAt: string,
107
+ ): SideDockPinnedItem {
103
108
  return {
104
109
  createdAt,
105
110
  icon: item.icon,
@@ -109,6 +114,10 @@ export function createPinnedSideDockItem(item: SideDockItem, createdAt: string):
109
114
  };
110
115
  }
111
116
 
117
+ function getSideDockResourceUri(item: SideDockItem | SideDockPinnedItem): string | null {
118
+ return item.target.type === 'right-panel-resource' ? item.target.uri : null;
119
+ }
120
+
112
121
  export function createSideDockItemFromPinnedItem(item: SideDockPinnedItem): SideDockItem {
113
122
  return {
114
123
  builtIn: false,
@@ -125,7 +134,11 @@ export function mergeSideDockItems(
125
134
  pinnedItems: SideDockPinnedItem[],
126
135
  ): SideDockItem[] {
127
136
  const seenIds = new Set(builtInItems.map((item) => item.id));
128
- const seenUris = new Set(builtInItems.map((item) => item.target.uri));
137
+ const seenUris = new Set(
138
+ builtInItems
139
+ .map(getSideDockResourceUri)
140
+ .filter((uri): uri is string => uri !== null),
141
+ );
129
142
  const customItems = pinnedItems
130
143
  .filter((item) => !seenIds.has(item.id) && !seenUris.has(item.target.uri))
131
144
  .map(createSideDockItemFromPinnedItem);