@lapismd/design-core 0.1.0 → 0.2.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 (124) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE.md +3 -3
  3. package/README.md +21 -1
  4. package/package.json +34 -15
  5. package/spec/src/styling-and-themes.md +11 -9
  6. package/src/shared/ai/chat.css +25 -1
  7. package/src/shared/ai/chat.ts +12 -0
  8. package/src/shared/ai/composer-input/ComposerInput.svelte +3 -2
  9. package/src/shared/ai/context.svelte.ts +2 -0
  10. package/src/shared/ai/experimental/reaction-bar/ReactionBar.svelte +1 -1
  11. package/src/shared/ai/layout/Layout.svelte +116 -10
  12. package/src/shared/ai/message-list/MessageList.svelte +123 -43
  13. package/src/shared/ai/message-list/MessageListHistoryFixture.svelte +112 -0
  14. package/src/shared/ai/message-list/MessageListLegacyFixture.svelte +52 -0
  15. package/src/shared/ai/message-list/VirtualizedMessageRows.svelte +299 -0
  16. package/src/shared/ai/message-list/position.ts +7 -0
  17. package/src/shared/ai/new-messages.svelte.ts +24 -5
  18. package/src/shared/ai/stream-scroll.svelte.ts +135 -6
  19. package/src/shared/ai/tool-call-detail/ToolCallDetail.svelte +71 -0
  20. package/src/shared/ai/tool-call-detail/tool-call-detail.ts +209 -0
  21. package/src/shared/ai/tool-calls/ToolCalls.svelte +1 -1
  22. package/src/shared/ai/trigger-menu.ts +2 -3
  23. package/src/shared/diff/core/merge/actions.ts +4 -3
  24. package/src/shared/diff/core/merge/render-model.ts +14 -10
  25. package/src/shared/diff/file-diff/FileDiff.css +68 -1
  26. package/src/shared/diff/file-diff/FileDiff.svelte +27 -14
  27. package/src/shared/diff/file-diff/FileDiffComposer.svelte +98 -16
  28. package/src/shared/diff/file-diff/index.ts +2 -0
  29. package/src/shared/diff/file-diff/types.ts +13 -0
  30. package/src/shared/diff/file-listing/FileListingViewModeToggle.css +42 -5
  31. package/src/shared/diff/index.ts +2 -0
  32. package/src/shared/diff/merge-editor/MergeEditor.svelte +2 -7
  33. package/src/shared/filter/filter-query/filters.ts +2 -2
  34. package/src/shared/filter/filter-query/parser.ts +9 -8
  35. package/src/shared/filter/power-search/PowerSearch.svelte +1 -1
  36. package/src/shared/filter/search-filter-bar/SearchFilterBar.svelte +2 -2
  37. package/src/shared/filter/search-filter-bar/SearchFilterInput.svelte +1 -1
  38. package/src/shared/filter/search-filter-bar/SearchFilterPredicateEditor.svelte +1 -1
  39. package/src/shared/filter/search-filter-bar/search-filter-syntax.ts +1 -1
  40. package/src/shared/forms/autocomplete-input/AutocompleteInput.svelte +19 -7
  41. package/src/shared/forms/confirm-button/ConfirmButton.svelte +38 -0
  42. package/src/shared/forms/confirm-button/ConfirmButtonExample.svelte +14 -0
  43. package/src/shared/forms/confirm-button/index.ts +1 -0
  44. package/src/shared/forms/confirm-dialog/ConfirmDialog.css +21 -0
  45. package/src/shared/forms/confirm-dialog/ConfirmDialog.svelte +116 -0
  46. package/src/shared/forms/confirm-dialog/ConfirmDialogExample.svelte +17 -0
  47. package/src/shared/forms/confirm-dialog/index.ts +2 -0
  48. package/src/shared/forms/dialog-frame/DialogFrame.css +130 -0
  49. package/src/shared/forms/dialog-frame/DialogFrame.svelte +108 -0
  50. package/src/shared/forms/dialog-frame/DialogFrameExample.svelte +37 -0
  51. package/src/shared/forms/dialog-frame/index.ts +6 -0
  52. package/src/shared/forms/dialog.ts +8 -0
  53. package/src/shared/forms/index.ts +11 -0
  54. package/src/shared/shadcn/button/button.svelte +4 -0
  55. package/src/shared/shadcn/code-block/highlightRanges.ts +10 -6
  56. package/src/shared/shadcn/code-block/syntax/defineSyntaxTheme.ts +4 -2
  57. package/src/shared/shadcn/code-block/tokenizer.ts +3 -3
  58. package/src/shared/shadcn/command/command-dialog.svelte +1 -1
  59. package/src/shared/shadcn/command/command-group.svelte +3 -1
  60. package/src/shared/shadcn/command-view/command-view.svelte +1 -6
  61. package/src/shared/shadcn/dialog/dialog-content.svelte +3 -4
  62. package/src/shared/shadcn/dialog/dialog-portal.svelte +10 -2
  63. package/src/shared/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -691
  64. package/src/shared/shadcn/dropdown-menu/dropdown-menu-content.svelte +691 -0
  65. package/src/shared/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +1 -1
  66. package/src/shared/shadcn/scroll-area/scroll-area.svelte +2 -0
  67. package/src/shared/shadcn/spinner/spinner.svelte +3 -3
  68. package/src/shared/shadcn/tooltip/tooltip-content.svelte +10 -4
  69. package/src/shared/shadcn/tooltip/tooltip.tokens.ts +4 -0
  70. package/src/shared/shell/app-shell/AppShell.css +112 -32
  71. package/src/shared/shell/app-shell/AppShell.demo.css +37 -0
  72. package/src/shared/shell/app-shell/AppShellBodyContent.svelte +5 -1
  73. package/src/shared/shell/app-shell/AppShellBodySidebar.svelte +5 -1
  74. package/src/shared/shell/app-shell/AppShellMain.svelte +12 -1
  75. package/src/shared/shell/app-shell/AppShellMobilePanelSelector.svelte +1 -1
  76. package/src/shared/shell/app-shell/AppShellRoot.svelte +27 -13
  77. package/src/shared/shell/app-shell/AppShellSidebar.svelte +121 -19
  78. package/src/shared/shell/app-shell/AppShellSidebarClose.svelte +17 -17
  79. package/src/shared/shell/app-shell/AppShellSidebarToggle.svelte +38 -58
  80. package/src/shared/shell/app-shell/AppShellSurfaceLayer.svelte +173 -0
  81. package/src/shared/shell/app-shell/AppShellSurfaceLayerBody.svelte +32 -0
  82. package/src/shared/shell/app-shell/AppShellSurfaceLayerHeader.svelte +26 -0
  83. package/src/shared/shell/app-shell/app-shell-context.svelte.ts +6 -3
  84. package/src/shared/shell/app-shell/app-shell-controller.svelte.ts +269 -11
  85. package/src/shared/shell/app-shell/app-shell-sidebar-context.svelte.ts +1 -0
  86. package/src/shared/shell/app-shell/examples/AppShellBodyDemo.svelte +4 -0
  87. package/src/shared/shell/app-shell/examples/AppShellCompleteDemo.svelte +1 -0
  88. package/src/shared/shell/app-shell/examples/AppShellConversationDemo.svelte +1 -0
  89. package/src/shared/shell/app-shell/examples/AppShellExamples.css +624 -0
  90. package/src/shared/shell/app-shell/examples/AppShellFilesSidebarDemo.svelte +1 -0
  91. package/src/shared/shell/app-shell/examples/AppShellMarkdownDocumentDemo.svelte +6 -1
  92. package/src/shared/shell/app-shell/examples/AppShellMarkdownFilesDemo.svelte +1 -0
  93. package/src/shared/shell/app-shell/examples/AppShellModeSwitchDemo.svelte +1 -0
  94. package/src/shared/shell/app-shell/examples/AppShellMultiPanelDemo.svelte +187 -0
  95. package/src/shared/shell/app-shell/examples/AppShellProjectSidebarDemo.svelte +1 -0
  96. package/src/shared/shell/app-shell/examples/AppShellSidebarDemo.svelte +1 -0
  97. package/src/shared/shell/app-shell/examples/AppShellToolbarDemo.svelte +1 -0
  98. package/src/shared/shell/app-shell/index.ts +14 -0
  99. package/src/shared/shell/shell.tokens.css +2 -0
  100. package/src/shared/shell/shell.tokens.ts +5 -0
  101. package/src/shared/workspace/menu/WorkspaceContextMenuItems.svelte +2 -1
  102. package/src/shared/workspace/menu/WorkspaceMenuItems.svelte +2 -1
  103. package/src/shared/workspace/problems/ProblemsQuickFix.svelte +1 -4
  104. package/src/shared/workspace/settings/AppSettingsContent.svelte +6 -0
  105. package/src/shared/workspace/settings/AppSettingsNavigation.svelte +7 -0
  106. package/src/shared/workspace/settings/CopyableValue.svelte +81 -0
  107. package/src/shared/workspace/settings/DeclarativeSettingsSection.svelte +20 -0
  108. package/src/shared/workspace/settings/WorkspaceSettingField.svelte +110 -41
  109. package/src/shared/workspace/settings/WorkspaceSettingMultiSelect.svelte +7 -6
  110. package/src/shared/workspace/settings/WorkspaceSettingObjectTable.svelte +187 -23
  111. package/src/shared/workspace/settings/WorkspaceSettingSelect.svelte +29 -29
  112. package/src/shared/workspace/settings/WorkspaceSettings.css +190 -4
  113. package/src/shared/workspace/settings/WorkspaceSettingsStoryCustomField.svelte +2 -2
  114. package/src/shared/workspace/settings/WorkspaceSettingsStoryCustomPage.svelte +13 -0
  115. package/src/shared/workspace/settings/WorkspaceSettingsSurface.svelte +50 -0
  116. package/src/shared/workspace/settings/configuration.ts +5 -0
  117. package/src/shared/workspace/settings/copyable-value.ts +10 -0
  118. package/src/shared/workspace/settings/index.ts +5 -0
  119. package/src/shared/workspace/settings/settings-controller.svelte.ts +349 -15
  120. package/src/shared/workspace/settings/types.ts +90 -8
  121. package/src/storybook/app-shell-reference/AppShellTwoExpandedSidebarsReference.css +25 -0
  122. package/src/storybook/app-shell-reference/AppShellTwoExpandedSidebarsReference.svelte +48 -0
  123. package/src/storybook/app-shell-reference/index.ts +1 -0
  124. package/src/theme.css +10 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @lapismd/design-core
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`737c657`](https://github.com/lapismd/design-core/commit/737c65727a6beaeb6955a652aba67c42b882b222) Thanks [@stevejuma](https://github.com/stevejuma)! - Add composable same-side App Shell panels, responsive main-surface replacement, transparent rails, external controller toggles, and structural surface layers.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Relicense the published Design Core package to Apache 2.0.
12
+
13
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Tighten shared Tooltip padding and type so compact overlays no longer inherit oversized content chrome.
14
+
15
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Expose controlled multi-file review state, host-owned line annotations, leading file headers, and gutter line actions. Diff helpers stay compatible with strict TypeScript consumers.
16
+
17
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep mobile App Shell panels visible while focus moves between the trigger and the opened surface.
18
+
19
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Add opt-in `MessageList` virtualization while keeping the default unvirtualized chat layout. History scrolling now preserves user intent across measurements, state saves, and parent updates.
20
+
21
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Add shared dialog frames, inset large frames, and retryable confirmations. Portal updates keep dialog controls mounted, and dialog headings stay out of document landmarks.
22
+
23
+ - [#7](https://github.com/lapismd/design-core/pull/7) [`80937e0`](https://github.com/lapismd/design-core/commit/80937e0f01e8dda50cbbed45da4948736ec3035b) Thanks [@stevejuma](https://github.com/stevejuma)! - Export shared `ToolCallDetail` presentation so hosts can render tool payloads, summaries, and expandable detail without forking chat markup.
24
+
25
+ ## 0.1.2
26
+
27
+ ### Patch Changes
28
+
29
+ - [#2](https://github.com/lapismd/design-core/pull/2) [`af3a0b3`](https://github.com/lapismd/design-core/commit/af3a0b3aaabd24be368acea795b150af8f4e5b89) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep workspace menu close handling lifecycle-safe when an action callback destroys its owning view.
30
+
31
+ - [#4](https://github.com/lapismd/design-core/pull/4) [`d153167`](https://github.com/lapismd/design-core/commit/d15316795ce7601a483796bce23f78bfc37188ae) Thanks [@stevejuma](https://github.com/stevejuma)! - Allow App Shell body content and body sidebar regions to configure scroll, hover, or always-on scrollbar visibility independently.
32
+
33
+ - [#4](https://github.com/lapismd/design-core/pull/4) [`31c7a2c`](https://github.com/lapismd/design-core/commit/31c7a2cb438213d75a5ef3193e957cd7c35dc34f) Thanks [@stevejuma](https://github.com/stevejuma)! - Inset vertical and horizontal Scroll Area thumbs by one pixel from their outer edge.
34
+
35
+ - [#4](https://github.com/lapismd/design-core/pull/4) [`24a6e98`](https://github.com/lapismd/design-core/commit/24a6e989354b9d00d2d93786e4255da088812ee9) Thanks [@stevejuma](https://github.com/stevejuma)! - Expose the canonical two-expanded-sidebars App Shell fixture, including bounded content extension points and an optional right-sidebar toggle, for downstream Storybook catalogs. Keep the focused Search Filter Bar source export compatible with strict consumers.
36
+
37
+ - [#4](https://github.com/lapismd/design-core/pull/4) [`413139d`](https://github.com/lapismd/design-core/commit/413139d1269d5f939d20c1f00de45f151372eae0) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep composed Shadcn, AI, and Filter source exports compatible with strict consumers that enable exact optional properties and unchecked indexed access.
38
+
39
+ - [#4](https://github.com/lapismd/design-core/pull/4) [`61fa863`](https://github.com/lapismd/design-core/commit/61fa86315a840717c39f0a0701d8cb208a187cb2) Thanks [@stevejuma](https://github.com/stevejuma)! - Keep the public App Shell source compatible with consumers that enable exact optional-property semantics.
40
+
41
+ ## 0.1.1
42
+
43
+ ### Patch Changes
44
+
45
+ - [`d1dc4a8`](https://github.com/lapismd/design-core/commit/d1dc4a8fb13eec9b59075712f964a89cee18a1a8) Thanks [@stevejuma](https://github.com/stevejuma)! - Allow custom settings pages to contribute dynamic search entries and reveal the selected row before Design Core scrolls to and highlights it.
46
+
3
47
  ## 0.1.0
4
48
 
5
49
  ### Initial public release
package/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- AGPL-3.0-or-later
1
+ Apache-2.0
2
2
 
3
- This project is licensed under the GNU Affero General Public License v3.0 or later.
4
- See <https://www.gnu.org/licenses/agpl-3.0.en.html>.
3
+ This project is licensed under the Apache License 2.0.
4
+ See <https://www.apache.org/licenses/LICENSE-2.0>.
package/README.md CHANGED
@@ -224,6 +224,26 @@ pnpm spec:search -- "tokens"
224
224
  pnpm spec:check
225
225
  ```
226
226
 
227
+ ### CI and local parity
228
+
229
+ `pnpm checks` runs the nonvisual governance, quality, unit, Storybook, pointer,
230
+ and AI browser lanes through Turbo. Governance, quality, and unit work executes
231
+ concurrently with half the available processors, capped at four. Resource-heavy
232
+ Storybook and browser suites run one at a time locally so catalog startup and
233
+ interaction readiness remain deterministic; set `TURBO_CONCURRENCY` to a
234
+ positive number or percentage to override the general task bound.
235
+
236
+ Local task output is cached under `.turbo/`. The same tasks use the signed
237
+ organization cache in CI and fail open to normal execution when it is
238
+ unavailable. To use the remote cache locally, copy `.env.example` to the ignored
239
+ root `.env` and provide `TURBO_TOKEN` and
240
+ `TURBO_REMOTE_CACHE_SIGNATURE_KEY`. Never commit those credentials.
241
+
242
+ CI fans the nine nonvisual lanes out in parallel, including separate Workspace,
243
+ Shell, Shadcn, and AI browser runners. It runs full and production dependency
244
+ audits after they pass and reports one stable `Validate` result. Visual
245
+ comparison remains an explicit, separate command.
246
+
227
247
  ## Release and validation
228
248
 
229
249
  Common validation commands:
@@ -246,7 +266,7 @@ pnpm release:plan --registry https://registry.npmjs.org
246
266
  pnpm packages:pack
247
267
  ```
248
268
 
249
- The first public npm publication is a manual bootstrap from reviewed tarballs.
269
+ `@lapismd/design-core@0.1.0` was manually bootstrapped from a reviewed tarball.
250
270
  Future releases use Changesets and the repository release workflow.
251
271
 
252
272
  ## Further reading
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@lapismd/design-core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Shared Lapis design system, forms, shell, and workspace primitives",
5
- "license": "AGPL-3.0-or-later",
5
+ "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/lapismd/design-core.git"
@@ -36,7 +36,6 @@
36
36
  "svelte": "^5.0.0"
37
37
  },
38
38
  "dependencies": {
39
- "@lapismd/mira": "^0.0.1",
40
39
  "@codemirror/autocomplete": "^6.18.0",
41
40
  "@codemirror/commands": "^6.8.0",
42
41
  "@codemirror/lang-java": "^6.0.2",
@@ -54,16 +53,19 @@
54
53
  "@iconify-json/lucide": "^1.2.63",
55
54
  "@iconify/utils": "^2.3.0",
56
55
  "@internationalized/date": "^3.12.2",
56
+ "@lapismd/mira": "^0.0.1",
57
57
  "@lezer/common": "^1.2.3",
58
58
  "@lezer/highlight": "^1.2.3",
59
59
  "@lezer/lr": "^1.4.2",
60
60
  "@lucide/svelte": "^1.21.0",
61
+ "@tanstack/svelte-virtual": "^3.13.39",
62
+ "@types/luxon": "^3.4.2",
61
63
  "bits-ui": "^2.18.1",
62
64
  "chrono-node": "^2.10.1",
63
65
  "clsx": "^2.1.1",
64
66
  "codemirror-lang-mermaid": "^0.5.0",
65
67
  "luxon": "^3.5.0",
66
- "minimatch": "^10.2.5",
68
+ "minimatch": "^10.2.6",
67
69
  "paneforge": "^1.0.2",
68
70
  "shadcn-svelte": "^1.3.0",
69
71
  "tailwind-merge": "^3.6.0",
@@ -90,28 +92,27 @@
90
92
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
91
93
  "@tailwindcss/cli": "^4.3.3",
92
94
  "@tailwindcss/vite": "^4.3.0",
93
- "@tobilu/qmd": "2.5.3",
94
95
  "@testing-library/jest-dom": "^7.0.0",
95
96
  "@testing-library/react": "^16.3.2",
96
97
  "@testing-library/user-event": "^14.6.1",
97
- "@types/luxon": "^3.4.2",
98
+ "@tobilu/qmd": "2.5.3",
98
99
  "@types/node": "^22.15.0",
99
100
  "@types/pixelmatch": "^5.2.6",
100
101
  "@types/pngjs": "^6.0.5",
101
102
  "@types/react": "^19.2.17",
102
103
  "@types/react-dom": "^19.2.3",
103
- "@vitest/browser": "^4.1.10",
104
- "@vitest/browser-playwright": "^4.1.10",
104
+ "@vitest/browser": "^4.1.11",
105
+ "@vitest/browser-playwright": "^4.1.11",
105
106
  "aria-query": "5.3.2",
106
107
  "execa": "^10.0.0",
107
108
  "highlight.js": "^11.11.1",
108
109
  "jsdom": "^29.1.1",
109
110
  "magic-string": "^1.0.0",
110
- "markdownlint-cli2": "^0.22.1",
111
+ "markdownlint-cli2": "^0.23.2",
111
112
  "pixelmatch": "^7.2.0",
112
113
  "playwright": "^1.52.0",
113
114
  "pngjs": "^7.0.0",
114
- "postcss": "^8.5.20",
115
+ "postcss": "^8.5.28",
115
116
  "postcss-selector-parser": "^7.1.4",
116
117
  "prettier": "3.3.3",
117
118
  "prettier-plugin-svelte": "^3.3.2",
@@ -124,9 +125,10 @@
124
125
  "svelte-check": "^4.6.0",
125
126
  "tailwindcss": "^4.3.0",
126
127
  "tsx": "^4.23.1",
128
+ "turbo": "^2.5.4",
127
129
  "typescript": "^5.7.2",
128
130
  "vite": "^6.2.2",
129
- "vitest": "^4.1.10",
131
+ "vitest": "^4.1.11",
130
132
  "zod": "^4.4.3"
131
133
  },
132
134
  "exports": {
@@ -174,6 +176,7 @@
174
176
  "./styles.css": "./src/styles.css",
175
177
  "./storybook.css": "./src/storybook.css",
176
178
  "./storybook/catalog-layout": "./src/storybook/catalog-layout.ts",
179
+ "./storybook/app-shell-reference": "./src/storybook/app-shell-reference/index.ts",
177
180
  "./themes/lapis.css": "./src/themes/lapis.css",
178
181
  "./ai": "./src/shared/ai/index.ts",
179
182
  "./ai/chat": "./src/shared/ai/chat.ts",
@@ -225,7 +228,8 @@
225
228
  "./workspace/view-header": "./src/shared/workspace/view-header/index.ts",
226
229
  "./workspace/tokens": "./src/shared/workspace/workspace.tokens.ts",
227
230
  "./workspace/view-host": "./src/shared/workspace/view-host/index.ts",
228
- "./workspace/workspace.tokens.css": "./src/shared/workspace/workspace.tokens.css"
231
+ "./workspace/workspace.tokens.css": "./src/shared/workspace/workspace.tokens.css",
232
+ "./forms/dialog": "./src/shared/forms/dialog.ts"
229
233
  },
230
234
  "scripts": {
231
235
  "storybook": "WATCHPACK_POLLING=250 node scripts/with-storybook-env.mjs node scripts/storybook-run.mjs",
@@ -233,9 +237,11 @@
233
237
  "storybook:stop": "node scripts/with-storybook-env.mjs bash scripts/storybook-stop.sh",
234
238
  "storybook:restart": "node scripts/with-storybook-env.mjs bash scripts/storybook-restart.sh",
235
239
  "storybook:check": "pnpm test:storybook && pnpm test:workspace:pointer && pnpm test:shell:pointer && pnpm test:shadcn:pointer && pnpm build-storybook && pnpm workspace:visual:audit",
236
- "build-storybook": "node -e \"require('node:fs').mkdirSync('.visual-delta/cache',{recursive:true})\" && node --max-old-space-size=6144 ./node_modules/storybook/dist/bin/dispatcher.js build --stats-json .visual-delta/cache",
240
+ "build-storybook": "node --env-file-if-exists=.env scripts/run-turbo.mjs build:storybook",
241
+ "build:storybook": "node -e \"require('node:fs').mkdirSync('.visual-delta/cache',{recursive:true})\" && node --max-old-space-size=6144 ./node_modules/storybook/dist/bin/dispatcher.js build --stats-json .visual-delta/cache",
237
242
  "changeset": "changeset",
238
243
  "test:unit": "vitest run --project unit",
244
+ "test:turbo": "node --test scripts/concurrency.test.mjs scripts/turbo-cache-report.test.mjs",
239
245
  "test:storybook": "vitest run --project storybook",
240
246
  "test:storybook:watch": "vitest --project storybook",
241
247
  "spec:build": "spec-validator build",
@@ -277,11 +283,24 @@
277
283
  "ui:visual:tag": "tsx scripts/ui-generator/cli.ts visual:tag",
278
284
  "test-unit": "pnpm test:unit",
279
285
  "test-storybook": "pnpm test:storybook",
280
- "check": "pnpm spec:validate && svelte-check --tsconfig ./tsconfig.json --fail-on-warnings",
286
+ "check": "pnpm spec:validate && pnpm check:types",
287
+ "check:types": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings && pnpm check:shell-consumer && pnpm check:filter-consumer && pnpm check:source-consumer",
288
+ "check:filter-consumer": "svelte-check --tsconfig ./tsconfig.filter-consumer.json --fail-on-warnings",
289
+ "check:shell-consumer": "svelte-check --tsconfig ./tsconfig.shell-consumer.json --fail-on-warnings",
290
+ "check:source-consumer": "svelte-check --tsconfig ./tsconfig.source-consumer.json --fail-on-warnings",
281
291
  "fmt": "prettier . --write",
282
292
  "fmt:check": "prettier . --check",
283
293
  "check:no-tailwind": "tsx scripts/check-no-tailwind-utilities.ts",
284
- "checks:nonvisual": "pnpm fmt:check && pnpm spec:check && pnpm check && pnpm check:no-tailwind && pnpm test:unit && pnpm test:storybook && pnpm test:workspace:pointer && pnpm test:shell:pointer && pnpm test:shadcn:pointer && pnpm build-storybook && pnpm workspace:visual:audit && pnpm test:ai-chat-browser",
294
+ "ci:governance": "pnpm spec:check",
295
+ "ci:quality": "pnpm fmt:check && pnpm check:types && pnpm check:no-tailwind",
296
+ "ci:unit": "pnpm test:unit && pnpm test:turbo",
297
+ "ci:storybook-static": "pnpm workspace:visual:audit -- --index=storybook-static/index.json",
298
+ "ci:storybook-interaction": "pnpm test:storybook",
299
+ "ci:browser-workspace": "pnpm test:workspace:pointer",
300
+ "ci:browser-shell": "pnpm test:shell:pointer",
301
+ "ci:browser-shadcn": "pnpm test:shadcn:pointer",
302
+ "ci:browser-ai": "pnpm test:ai-chat-browser",
303
+ "checks:nonvisual": "node --env-file-if-exists=.env scripts/run-turbo.mjs ci:governance ci:quality ci:unit --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:storybook-static --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:storybook-interaction --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:browser-workspace --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:browser-shell --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:browser-ai --summarize && node --env-file-if-exists=.env scripts/run-turbo.mjs ci:browser-shadcn --summarize",
285
304
  "checks:release": "pnpm spec:check && pnpm check && pnpm check:no-tailwind && pnpm test:unit && pnpm build-storybook && pnpm workspace:visual:audit",
286
305
  "checks": "pnpm checks:nonvisual",
287
306
  "checks:visual": "pnpm checks:nonvisual && pnpm test:visual",
@@ -107,15 +107,15 @@ pages.
107
107
 
108
108
  ### Token layers
109
109
 
110
- | Layer | Prefix | Defaults |
111
- | ------------------- | ------------------------------ | ------------------------------------------- |
112
- | Theme | `--background`, `--primary`, … | `src/theme.css` |
113
- | Shadcn family | `--ui-<family>-*` | `<family>.tokens.css` |
114
- | Forms | `--ui-form-*` | `src/shared/forms/form.tokens.css` |
115
- | Filter and AI | `--ui-*` / `--ui-ai-*` | colocated CSS and token maps |
116
- | Diff | `--ui-diff-*` | `src/shared/diff/diff.tokens.css` |
117
- | Structural shell | `--ui-shell-*` | `src/shared/shell/shell.tokens.css` |
118
- | Workspace framework | `--ui-workspace-*` | `src/shared/workspace/workspace.tokens.css` |
110
+ | Layer | Prefix | Defaults |
111
+ | ------------------- | ------------------------------ | ------------------------------------------------- |
112
+ | Theme | `--background`, `--primary`, … | `src/theme.css` |
113
+ | Shadcn family | `--ui-<family>-*` | `<family>.tokens.css` |
114
+ | Forms | `--ui-form-*` | `src/shared/forms/form.tokens.css` |
115
+ | Filter and AI | `--ui-*` / `--ui-ai-*` | colocated CSS and token maps |
116
+ | Diff | `--ui-diff-*` | `src/shared/diff/diff.tokens.css` |
117
+ | Structural shell | `--ui-shell-*` | `src/shared/shell/shell.tokens.css` |
118
+ | Workspace framework | `--ui-workspace-*` | `src/shared/workspace/workspace.tokens.css` |
119
119
  | Desktop drag | `data-desktop-drag-region` | `src/shared/workspace/workspace-desktop-drag.css` |
120
120
 
121
121
  Set public tokens on `:root` or an appropriate shared ancestor such as
@@ -186,3 +186,5 @@ refuses leftover utilities after native-CSS conversion.
186
186
  4. Stamp `data-ui-component` and `data-ui-part`.
187
187
  5. Document the tokens the component reads.
188
188
  6. Run `pnpm check:no-tailwind`, focused story checks, and the repository gate.
189
+
190
+ Dialog recipes consume runtime container and text tokens explicitly defined in the theme so packaged consumers retain geometry without generating Tailwind utilities.
@@ -1031,7 +1031,8 @@
1031
1031
 
1032
1032
  [data-ui-component="ai-chat-composer-drawer"]
1033
1033
  [data-ai-chat-part="drawer-content"]
1034
- [data-ui-part="attachment-remove"] svg {
1034
+ [data-ui-part="attachment-remove"]
1035
+ svg {
1035
1036
  width: 0.75rem;
1036
1037
  height: 0.75rem;
1037
1038
  }
@@ -1549,3 +1550,26 @@
1549
1550
  animation: none;
1550
1551
  }
1551
1552
  }
1553
+
1554
+ [data-ui-component="ai-chat-message-list"] [data-ui-part="virtual-content"] {
1555
+ position: relative;
1556
+ flex: 0 0 auto;
1557
+ width: 100%;
1558
+ overflow-anchor: none;
1559
+ }
1560
+ [data-ui-component="ai-chat-message-list"] [data-ui-part="virtual-row"] {
1561
+ position: absolute;
1562
+ top: 0;
1563
+ left: 0;
1564
+ width: 100%;
1565
+ padding-bottom: var(--ui-ai-chat-balanced-gap);
1566
+ overflow-anchor: none;
1567
+ }
1568
+ [data-ui-component="ai-chat-message-list"][data-density="compact"]
1569
+ [data-ui-part="virtual-row"] {
1570
+ padding-bottom: var(--ui-ai-chat-compact-gap);
1571
+ }
1572
+ [data-ui-component="ai-chat-message-list"][data-density="spacious"]
1573
+ [data-ui-part="virtual-row"] {
1574
+ padding-bottom: var(--ui-ai-chat-spacious-gap);
1575
+ }
@@ -12,6 +12,15 @@ export { default as SendButton } from "./send-button/SendButton.svelte";
12
12
  export { default as ComposerToken } from "./composer-token/ComposerToken.svelte";
13
13
  export { default as TokenizedText } from "./tokenized-text/TokenizedText.svelte";
14
14
  export { default as ToolCalls } from "./tool-calls/ToolCalls.svelte";
15
+ export { default as ToolCallDetail } from "./tool-call-detail/ToolCallDetail.svelte";
16
+ export {
17
+ isOneLineToolAlert,
18
+ presentToolPayload,
19
+ toolCallTarget,
20
+ type PresentedToolPayload,
21
+ type ToolPayloadHint,
22
+ type ToolPayloadLanguage,
23
+ } from "./tool-call-detail/tool-call-detail.js";
15
24
  export { default as DictationButton } from "./dictation-button/DictationButton.svelte";
16
25
 
17
26
  export {
@@ -19,6 +28,7 @@ export {
19
28
  type ScrollBehavior,
20
29
  type ScrollToBottomOptions,
21
30
  type StreamScrollController,
31
+ type PositionedStreamScrollController,
22
32
  type StreamScrollOptions,
23
33
  } from "./stream-scroll.svelte.js";
24
34
  export {
@@ -68,3 +78,5 @@ export type {
68
78
  ToolCallStatus,
69
79
  } from "./types.js";
70
80
  export type { ComposerToken as ComposerTokenValue } from "./types.js";
81
+
82
+ export type { MessageListPosition } from "./message-list/position.js";
@@ -331,9 +331,10 @@
331
331
  Math.max(1, searchState.items.length);
332
332
  return;
333
333
  }
334
- if (event.key === "Enter" && searchState.items[highlightedIndex]) {
334
+ const highlightedItem = searchState.items[highlightedIndex];
335
+ if (event.key === "Enter" && highlightedItem) {
335
336
  event.preventDefault();
336
- insertTriggerSelection(searchState.items[highlightedIndex]);
337
+ insertTriggerSelection(highlightedItem);
337
338
  return;
338
339
  }
339
340
  if (event.key === "Escape") {
@@ -12,6 +12,8 @@ export type LayoutContextValue = {
12
12
  getScrollContainer: () => HTMLElement | null;
13
13
  setScrollContainer: (element: HTMLElement | null) => void;
14
14
  setContent: (element: HTMLElement | null) => void;
15
+ /** A virtual list owns row resize anchoring; the layout still owns scroll writes. */
16
+ setVirtualized?: (active: boolean) => void;
15
17
  streamScroll: StreamScrollController;
16
18
  newMessages: NewMessagesController;
17
19
  };
@@ -74,7 +74,7 @@
74
74
  {/each}
75
75
  {#if onAdd}
76
76
  <EmojiPicker
77
- {emojis}
77
+ {...emojis === undefined ? {} : { emojis }}
78
78
  label={addLabel}
79
79
  triggerLabel={addLabel}
80
80
  onSelect={onAdd}
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import SparklesIcon from "@lucide/svelte/icons/sparkles";
3
- import type { Snippet } from "svelte";
3
+ import { untrack, type Snippet } from "svelte";
4
4
  import type { HTMLAttributes } from "svelte/elements";
5
5
  import * as Empty from "@lapismd/design-core/shadcn/empty";
6
6
  import { ScrollArea } from "@lapismd/design-core/shadcn/scroll-area";
@@ -16,6 +16,12 @@
16
16
  scrollRef = $bindable(null),
17
17
  density = "balanced",
18
18
  isEmpty = false,
19
+ scrollMode = "stream",
20
+ conversationKey,
21
+ hasNewMessages: controlledNewMessages,
22
+ hasNewer = false,
23
+ onLoadLatest,
24
+ onViewportChange,
19
25
  emptyState,
20
26
  scrollButton,
21
27
  composer,
@@ -27,6 +33,13 @@
27
33
  scrollRef?: HTMLElement | null;
28
34
  density?: Density;
29
35
  isEmpty?: boolean;
36
+ /** Keep streaming scroll behavior by default; opt in to anchored cached-history navigation. */
37
+ scrollMode?: "stream" | "history";
38
+ conversationKey?: string;
39
+ hasNewMessages?: boolean;
40
+ hasNewer?: boolean;
41
+ onLoadLatest?: () => Promise<void>;
42
+ onViewportChange?: (state: { atLatest: boolean; active: boolean }) => void;
30
43
  emptyState?: Snippet;
31
44
  scrollButton?: Snippet<
32
45
  [
@@ -43,12 +56,20 @@
43
56
 
44
57
  let messageAreaRef = $state<HTMLElement | null>(null);
45
58
  let contentRef = $state<HTMLElement | null>(null);
46
- const streamScroll = createStreamScroll();
59
+ let virtualized = false;
60
+ const streamScroll = createStreamScroll({
61
+ anchorOnResize: () => scrollMode === "history",
62
+ });
47
63
  const newMessages = createNewMessages({
64
+ deferResize: () => scrollMode === "history",
48
65
  isLocked: () => streamScroll.isLocked,
49
66
  onResize: () => {
50
- streamScroll.scrollIfLocked();
51
- streamScroll.update();
67
+ if (scrollMode === "history")
68
+ streamScroll.contentResized({ restoreAnchor: !virtualized });
69
+ else {
70
+ streamScroll.scrollIfLocked();
71
+ streamScroll.update();
72
+ }
52
73
  },
53
74
  });
54
75
 
@@ -57,10 +78,89 @@
57
78
  newMessages.attach(element);
58
79
  }
59
80
 
81
+ let pendingJump = false;
82
+ let jumpVersion = 0;
83
+ let priorConversationKey: string | undefined;
84
+ let conversationInitialized = false;
85
+ let jumpError = $state<string | undefined>();
86
+ let documentActive = $state(true);
87
+ const unread = $derived(controlledNewMessages ?? newMessages.hasNewMessages);
88
+ const showNewMessages = $derived(
89
+ unread && (!streamScroll.isLocked || hasNewer || !documentActive),
90
+ );
91
+ const showScrollButton = $derived(
92
+ streamScroll.isScrolledUp || showNewMessages || hasNewer,
93
+ );
60
94
  function scrollToBottom(): void {
61
- streamScroll.scrollToBottom();
62
- newMessages.dismiss();
95
+ if (pendingJump) return;
96
+ if (scrollMode === "stream" && !onLoadLatest) {
97
+ newMessages.dismiss();
98
+ streamScroll.scrollToBottom();
99
+ return;
100
+ }
101
+ const key = conversationKey;
102
+ const version = ++jumpVersion;
103
+ const revision = streamScroll.positionRevision;
104
+ pendingJump = true;
105
+ jumpError = undefined;
106
+ void (async () => {
107
+ try {
108
+ await onLoadLatest?.();
109
+ if (
110
+ key === conversationKey &&
111
+ version === jumpVersion &&
112
+ revision === streamScroll.positionRevision
113
+ )
114
+ streamScroll.scrollToBottom({
115
+ behavior: scrollMode === "history" ? "instant" : "spring",
116
+ });
117
+ } catch (cause) {
118
+ if (key === conversationKey && version === jumpVersion)
119
+ jumpError =
120
+ cause instanceof Error
121
+ ? cause.message
122
+ : "Could not load latest messages";
123
+ } finally {
124
+ if (version === jumpVersion) pendingJump = false;
125
+ }
126
+ })();
63
127
  }
128
+ $effect(() => {
129
+ const key = conversationKey;
130
+ untrack(() => {
131
+ if (conversationInitialized && key === priorConversationKey) return;
132
+ conversationInitialized = true;
133
+ priorConversationKey = key;
134
+ jumpVersion++;
135
+ pendingJump = false;
136
+ if (key === undefined) return;
137
+ newMessages.attach(contentRef ?? messageAreaRef);
138
+ streamScroll.attach(null);
139
+ streamScroll.attach(scrollRef);
140
+ jumpError = undefined;
141
+ });
142
+ return () => {
143
+ void key;
144
+ };
145
+ });
146
+ $effect(() => {
147
+ if (streamScroll.isLocked && !hasNewer && documentActive)
148
+ newMessages.dismiss();
149
+ const observation = {
150
+ atLatest: streamScroll.isLocked && !hasNewer,
151
+ active: documentActive,
152
+ };
153
+ const notify = onViewportChange;
154
+ untrack(() => notify?.(observation));
155
+ });
156
+ $effect(() => {
157
+ const update = () => {
158
+ documentActive = document.visibilityState !== "hidden";
159
+ };
160
+ update();
161
+ document.addEventListener("visibilitychange", update);
162
+ return () => document.removeEventListener("visibilitychange", update);
163
+ });
64
164
 
65
165
  setLayoutContext({
66
166
  getScrollContainer: () => scrollRef,
@@ -69,6 +169,9 @@
69
169
  streamScroll.attach(element);
70
170
  },
71
171
  setContent,
172
+ setVirtualized(active) {
173
+ virtualized = active;
174
+ },
72
175
  streamScroll,
73
176
  newMessages,
74
177
  });
@@ -118,16 +221,19 @@
118
221
  {/if}
119
222
  </div>
120
223
  </ScrollArea>
224
+ {#if jumpError}<p role="alert" data-ui-part="history-error">
225
+ {jumpError}
226
+ </p>{/if}
121
227
  {#if scrollButton}
122
228
  {@render scrollButton({
123
- isVisible: streamScroll.isScrolledUp || newMessages.hasNewMessages,
124
- hasNewMessages: newMessages.hasNewMessages,
229
+ isVisible: showScrollButton,
230
+ hasNewMessages: showNewMessages,
125
231
  scrollToBottom,
126
232
  })}
127
233
  {:else}
128
234
  <LayoutScrollButton
129
- isVisible={streamScroll.isScrolledUp || newMessages.hasNewMessages}
130
- hasNewMessages={newMessages.hasNewMessages}
235
+ isVisible={showScrollButton}
236
+ hasNewMessages={showNewMessages}
131
237
  onClick={scrollToBottom}
132
238
  />
133
239
  {/if}