@plures/design-dojo 0.5.2 → 0.7.1

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 (250) hide show
  1. package/dist/app/CanvasBreadcrumb.svelte +146 -0
  2. package/dist/app/CanvasBreadcrumb.svelte.d.ts +19 -0
  3. package/dist/app/CanvasBreadcrumb.types.js +1 -0
  4. package/dist/app/ChatInput.svelte +296 -0
  5. package/dist/app/ChatInput.svelte.d.ts +15 -0
  6. package/dist/app/ChatView.svelte +542 -0
  7. package/dist/app/ChatView.svelte.d.ts +19 -0
  8. package/dist/app/ChatView.types.js +1 -0
  9. package/dist/app/ConversationGraph.svelte +471 -0
  10. package/dist/app/ConversationGraph.svelte.d.ts +20 -0
  11. package/dist/app/FirstRunWizard.svelte +542 -0
  12. package/dist/app/FirstRunWizard.svelte.d.ts +10 -0
  13. package/dist/app/FirstRunWizard.types.js +1 -0
  14. package/dist/app/MemorySidebar.svelte +258 -0
  15. package/dist/app/MemorySidebar.svelte.d.ts +9 -0
  16. package/dist/app/MemorySidebar.types.js +1 -0
  17. package/dist/app/PeerStatusPanel.svelte +464 -0
  18. package/dist/app/PeerStatusPanel.svelte.d.ts +16 -0
  19. package/dist/app/ProcedureCanvas.svelte +994 -0
  20. package/dist/app/ProcedureCanvas.svelte.d.ts +12 -0
  21. package/dist/app/ProcedureCanvas.types.js +1 -0
  22. package/dist/app/ProcedureEditor.svelte +494 -0
  23. package/dist/app/ProcedureEditor.svelte.d.ts +11 -0
  24. package/dist/app/ProcedureEditor.types.js +1 -0
  25. package/dist/app/ProcedureInspector.svelte +520 -0
  26. package/dist/app/ProcedureInspector.svelte.d.ts +15 -0
  27. package/dist/app/ProcedureNode.svelte +283 -0
  28. package/dist/app/ProcedureNode.svelte.d.ts +26 -0
  29. package/dist/app/Realm.types.js +1 -0
  30. package/dist/app/RealmIndicator.svelte +81 -0
  31. package/dist/app/RealmIndicator.svelte.d.ts +10 -0
  32. package/dist/app/RealmSwitcher.svelte +354 -0
  33. package/dist/app/RealmSwitcher.svelte.d.ts +16 -0
  34. package/dist/app/SemanticConversation.types.js +1 -0
  35. package/dist/app/SemanticSearchInput.svelte +630 -0
  36. package/dist/app/SemanticSearchInput.svelte.d.ts +20 -0
  37. package/dist/app/SemanticSearchInput.types.js +1 -0
  38. package/dist/app/SemanticTimeline.svelte +426 -0
  39. package/dist/app/SemanticTimeline.svelte.d.ts +13 -0
  40. package/dist/app/SettingsPanel.svelte +330 -0
  41. package/dist/app/SettingsPanel.svelte.d.ts +12 -0
  42. package/dist/app/SettingsPanel.types.js +1 -0
  43. package/dist/app/SubCanvas.svelte +457 -0
  44. package/dist/app/SubCanvas.svelte.d.ts +48 -0
  45. package/dist/app/SubCanvas.types.js +1 -0
  46. package/dist/app/Sync.types.js +1 -0
  47. package/dist/app/SyncIndicator.svelte +219 -0
  48. package/dist/app/SyncIndicator.svelte.d.ts +15 -0
  49. package/dist/app/SyncTimeline.svelte +299 -0
  50. package/dist/app/SyncTimeline.svelte.d.ts +12 -0
  51. package/dist/app/TagCloud.svelte +287 -0
  52. package/dist/app/TagCloud.svelte.d.ts +12 -0
  53. package/dist/app/WorkModeToggle.svelte +188 -0
  54. package/dist/app/WorkModeToggle.svelte.d.ts +17 -0
  55. package/dist/data/List.svelte +104 -0
  56. package/dist/data/List.svelte.d.ts +18 -0
  57. package/dist/data/ListItem.svelte +130 -0
  58. package/dist/data/ListItem.svelte.d.ts +12 -0
  59. package/dist/data/Table.svelte +241 -0
  60. package/dist/data/Table.svelte.d.ts +17 -0
  61. package/dist/data/index.d.ts +3 -0
  62. package/dist/data/index.js +3 -0
  63. package/dist/disclosure/Accordion.svelte +48 -0
  64. package/dist/disclosure/Accordion.svelte.d.ts +15 -0
  65. package/dist/feedback/Badge.svelte +60 -0
  66. package/dist/feedback/Badge.svelte.d.ts +14 -0
  67. package/dist/feedback/Callout.svelte +52 -0
  68. package/dist/feedback/Callout.svelte.d.ts +12 -0
  69. package/dist/feedback/EmptyState.svelte +47 -0
  70. package/dist/feedback/EmptyState.svelte.d.ts +12 -0
  71. package/dist/feedback/ProgressBar.svelte +95 -0
  72. package/dist/feedback/ProgressBar.svelte.d.ts +16 -0
  73. package/dist/forms/FileUpload.svelte +99 -0
  74. package/dist/forms/FileUpload.svelte.d.ts +18 -0
  75. package/dist/forms/RadioGroup.svelte +84 -0
  76. package/dist/forms/RadioGroup.svelte.d.ts +19 -0
  77. package/dist/icons/NerdFont.svelte +44 -0
  78. package/dist/icons/NerdFont.svelte.d.ts +13 -0
  79. package/dist/icons/index.d.ts +1 -0
  80. package/dist/icons/index.js +1 -0
  81. package/dist/index.d.ts +76 -0
  82. package/dist/index.js +70 -6212
  83. package/dist/layout/Box.svelte +207 -0
  84. package/dist/layout/Box.svelte.d.ts +22 -0
  85. package/dist/layout/Sidebar.svelte +210 -0
  86. package/dist/layout/Sidebar.svelte.d.ts +22 -0
  87. package/dist/layout/SplitPane.svelte +64 -0
  88. package/dist/layout/SplitPane.svelte.d.ts +12 -0
  89. package/dist/layout/StatusBar.svelte +83 -0
  90. package/dist/layout/StatusBar.svelte.d.ts +12 -0
  91. package/dist/layout/StatusBarItem.svelte +146 -0
  92. package/dist/layout/StatusBarItem.svelte.d.ts +15 -0
  93. package/dist/layout/StatusBarSpacer.svelte +38 -0
  94. package/dist/layout/StatusBarSpacer.svelte.d.ts +3 -0
  95. package/dist/layout/Tabs.svelte +254 -0
  96. package/dist/layout/Tabs.svelte.d.ts +21 -0
  97. package/dist/layout/Tabs.types.js +1 -0
  98. package/dist/layout/TitleBar.svelte +422 -0
  99. package/dist/layout/TitleBar.svelte.d.ts +22 -0
  100. package/dist/layout/index.d.ts +9 -0
  101. package/dist/layout/index.js +8 -0
  102. package/dist/motion/index.d.ts +1 -0
  103. package/dist/motion/index.js +1 -0
  104. package/dist/motion/spring.js +116 -0
  105. package/dist/overlays/ContextMenu.svelte +268 -0
  106. package/dist/overlays/ContextMenu.svelte.d.ts +17 -0
  107. package/dist/overlays/Dialog.svelte +264 -0
  108. package/dist/overlays/Dialog.svelte.d.ts +20 -0
  109. package/dist/overlays/Menu.svelte +274 -0
  110. package/dist/overlays/Menu.svelte.d.ts +26 -0
  111. package/dist/overlays/Menu.types.js +1 -0
  112. package/dist/overlays/Popover.svelte +158 -0
  113. package/dist/overlays/Popover.svelte.d.ts +21 -0
  114. package/dist/overlays/Toast.svelte +179 -0
  115. package/dist/overlays/Toast.svelte.d.ts +19 -0
  116. package/dist/overlays/Tooltip.svelte +114 -0
  117. package/dist/overlays/Tooltip.svelte.d.ts +17 -0
  118. package/dist/overlays/index.d.ts +7 -0
  119. package/dist/overlays/index.js +6 -0
  120. package/dist/primitives/Button.svelte +217 -0
  121. package/dist/primitives/Button.svelte.d.ts +13 -0
  122. package/dist/primitives/ContextMenu.svelte +242 -0
  123. package/dist/primitives/ContextMenu.svelte.d.ts +18 -0
  124. package/dist/primitives/ContextMenu.types.js +1 -0
  125. package/dist/primitives/Input.svelte +468 -0
  126. package/dist/primitives/Input.svelte.d.ts +21 -0
  127. package/dist/primitives/MarkdownEditor.svelte +781 -0
  128. package/dist/primitives/MarkdownEditor.svelte.d.ts +21 -0
  129. package/dist/primitives/MarkdownEditor.types.js +1 -0
  130. package/dist/primitives/SearchInput.svelte +623 -0
  131. package/dist/primitives/SearchInput.svelte.d.ts +24 -0
  132. package/dist/primitives/Select.svelte +336 -0
  133. package/dist/primitives/Select.svelte.d.ts +18 -0
  134. package/dist/primitives/Text.svelte +177 -0
  135. package/dist/primitives/Text.svelte.d.ts +26 -0
  136. package/dist/primitives/Toggle.svelte +138 -0
  137. package/dist/primitives/Toggle.svelte.d.ts +9 -0
  138. package/dist/primitives/index.d.ts +9 -0
  139. package/dist/primitives/index.js +7 -0
  140. package/dist/primitives/search-input-types.js +1 -0
  141. package/dist/surfaces/ChatPane.svelte +520 -0
  142. package/dist/surfaces/ChatPane.svelte.d.ts +15 -0
  143. package/dist/surfaces/ChatPane.types.js +1 -0
  144. package/dist/surfaces/GlassPanel.svelte +118 -0
  145. package/dist/surfaces/GlassPanel.svelte.d.ts +19 -0
  146. package/dist/surfaces/Pane.svelte +172 -0
  147. package/dist/surfaces/Pane.svelte.d.ts +25 -0
  148. package/dist/surfaces/index.d.ts +4 -0
  149. package/dist/surfaces/index.js +3 -0
  150. package/dist/telemetry/correlation.js +26 -0
  151. package/dist/telemetry/index.d.ts +4 -4
  152. package/dist/telemetry/index.js +20 -101
  153. package/dist/telemetry/sampling.js +58 -0
  154. package/dist/telemetry/tracer.d.ts +16 -1
  155. package/dist/telemetry/tracer.js +112 -0
  156. package/dist/tokens.css +123 -0
  157. package/dist/tui-tokens.css +36 -0
  158. package/dist/useTui.js +31 -0
  159. package/package.json +32 -22
  160. package/dist/design-dojo.css +0 -1
  161. package/dist/enforce/index.d.ts +0 -75
  162. package/dist/enforce/known-components.d.ts +0 -7
  163. package/dist/enforce/rules/no-local-components.d.ts +0 -29
  164. package/dist/enforce/rules/prefer-design-dojo-imports.d.ts +0 -27
  165. package/dist/enforce.js +0 -132
  166. package/dist/lib/app/CanvasBreadcrumb.svelte.d.ts +0 -1
  167. package/dist/lib/app/ChatInput.svelte.d.ts +0 -1
  168. package/dist/lib/app/ChatView.svelte.d.ts +0 -1
  169. package/dist/lib/app/ConversationGraph.svelte.d.ts +0 -1
  170. package/dist/lib/app/FirstRunWizard.svelte.d.ts +0 -1
  171. package/dist/lib/app/MemorySidebar.svelte.d.ts +0 -1
  172. package/dist/lib/app/PeerStatusPanel.svelte.d.ts +0 -1
  173. package/dist/lib/app/ProcedureCanvas.svelte.d.ts +0 -1
  174. package/dist/lib/app/ProcedureEditor.svelte.d.ts +0 -1
  175. package/dist/lib/app/ProcedureInspector.svelte.d.ts +0 -1
  176. package/dist/lib/app/ProcedureNode.svelte.d.ts +0 -1
  177. package/dist/lib/app/RealmIndicator.svelte.d.ts +0 -1
  178. package/dist/lib/app/RealmSwitcher.svelte.d.ts +0 -1
  179. package/dist/lib/app/SemanticSearchInput.svelte.d.ts +0 -1
  180. package/dist/lib/app/SemanticTimeline.svelte.d.ts +0 -1
  181. package/dist/lib/app/SettingsPanel.svelte.d.ts +0 -1
  182. package/dist/lib/app/SubCanvas.svelte.d.ts +0 -1
  183. package/dist/lib/app/SyncIndicator.svelte.d.ts +0 -1
  184. package/dist/lib/app/SyncTimeline.svelte.d.ts +0 -1
  185. package/dist/lib/app/TagCloud.svelte.d.ts +0 -1
  186. package/dist/lib/app/WorkModeToggle.svelte.d.ts +0 -1
  187. package/dist/lib/data/List.svelte.d.ts +0 -1
  188. package/dist/lib/data/ListItem.svelte.d.ts +0 -1
  189. package/dist/lib/data/Table.svelte.d.ts +0 -1
  190. package/dist/lib/data/index.d.ts +0 -3
  191. package/dist/lib/disclosure/Accordion.svelte.d.ts +0 -1
  192. package/dist/lib/feedback/Badge.svelte.d.ts +0 -1
  193. package/dist/lib/feedback/Callout.svelte.d.ts +0 -1
  194. package/dist/lib/feedback/EmptyState.svelte.d.ts +0 -1
  195. package/dist/lib/feedback/ProgressBar.svelte.d.ts +0 -1
  196. package/dist/lib/forms/FileUpload.svelte.d.ts +0 -1
  197. package/dist/lib/forms/RadioGroup.svelte.d.ts +0 -1
  198. package/dist/lib/icons/NerdFont.svelte.d.ts +0 -1
  199. package/dist/lib/icons/index.d.ts +0 -1
  200. package/dist/lib/index.d.ts +0 -76
  201. package/dist/lib/layout/Box.svelte.d.ts +0 -1
  202. package/dist/lib/layout/Sidebar.svelte.d.ts +0 -1
  203. package/dist/lib/layout/SplitPane.svelte.d.ts +0 -1
  204. package/dist/lib/layout/StatusBar.svelte.d.ts +0 -1
  205. package/dist/lib/layout/StatusBarItem.svelte.d.ts +0 -1
  206. package/dist/lib/layout/StatusBarSpacer.svelte.d.ts +0 -1
  207. package/dist/lib/layout/Tabs.svelte.d.ts +0 -1
  208. package/dist/lib/layout/TitleBar.svelte.d.ts +0 -1
  209. package/dist/lib/layout/index.d.ts +0 -9
  210. package/dist/lib/motion/index.d.ts +0 -1
  211. package/dist/lib/overlays/ContextMenu.svelte.d.ts +0 -1
  212. package/dist/lib/overlays/Dialog.svelte.d.ts +0 -1
  213. package/dist/lib/overlays/Menu.svelte.d.ts +0 -1
  214. package/dist/lib/overlays/Popover.svelte.d.ts +0 -1
  215. package/dist/lib/overlays/Toast.svelte.d.ts +0 -1
  216. package/dist/lib/overlays/Tooltip.svelte.d.ts +0 -1
  217. package/dist/lib/overlays/index.d.ts +0 -7
  218. package/dist/lib/primitives/Button.svelte.d.ts +0 -1
  219. package/dist/lib/primitives/ContextMenu.svelte.d.ts +0 -1
  220. package/dist/lib/primitives/Input.svelte.d.ts +0 -1
  221. package/dist/lib/primitives/MarkdownEditor.svelte.d.ts +0 -1
  222. package/dist/lib/primitives/SearchInput.svelte.d.ts +0 -1
  223. package/dist/lib/primitives/Select.svelte.d.ts +0 -1
  224. package/dist/lib/primitives/Text.svelte.d.ts +0 -1
  225. package/dist/lib/primitives/Toggle.svelte.d.ts +0 -1
  226. package/dist/lib/primitives/index.d.ts +0 -9
  227. package/dist/lib/surfaces/ChatPane.svelte.d.ts +0 -1
  228. package/dist/lib/surfaces/GlassPanel.svelte.d.ts +0 -1
  229. package/dist/lib/surfaces/Pane.svelte.d.ts +0 -1
  230. package/dist/lib/surfaces/index.d.ts +0 -4
  231. /package/dist/{lib/app → app}/CanvasBreadcrumb.types.d.ts +0 -0
  232. /package/dist/{lib/app → app}/ChatView.types.d.ts +0 -0
  233. /package/dist/{lib/app → app}/FirstRunWizard.types.d.ts +0 -0
  234. /package/dist/{lib/app → app}/MemorySidebar.types.d.ts +0 -0
  235. /package/dist/{lib/app → app}/ProcedureCanvas.types.d.ts +0 -0
  236. /package/dist/{lib/app → app}/ProcedureEditor.types.d.ts +0 -0
  237. /package/dist/{lib/app → app}/Realm.types.d.ts +0 -0
  238. /package/dist/{lib/app → app}/SemanticConversation.types.d.ts +0 -0
  239. /package/dist/{lib/app → app}/SemanticSearchInput.types.d.ts +0 -0
  240. /package/dist/{lib/app → app}/SettingsPanel.types.d.ts +0 -0
  241. /package/dist/{lib/app → app}/SubCanvas.types.d.ts +0 -0
  242. /package/dist/{lib/app → app}/Sync.types.d.ts +0 -0
  243. /package/dist/{lib/layout → layout}/Tabs.types.d.ts +0 -0
  244. /package/dist/{lib/motion → motion}/spring.d.ts +0 -0
  245. /package/dist/{lib/overlays → overlays}/Menu.types.d.ts +0 -0
  246. /package/dist/{lib/primitives → primitives}/ContextMenu.types.d.ts +0 -0
  247. /package/dist/{lib/primitives → primitives}/MarkdownEditor.types.d.ts +0 -0
  248. /package/dist/{lib/primitives → primitives}/search-input-types.d.ts +0 -0
  249. /package/dist/{lib/surfaces → surfaces}/ChatPane.types.d.ts +0 -0
  250. /package/dist/{lib/useTui.d.ts → useTui.d.ts} +0 -0
@@ -0,0 +1,146 @@
1
+ <!--
2
+ CanvasBreadcrumb — Path / depth indicator for nested canvas navigation.
3
+
4
+ Shows the current canvas path as clickable breadcrumb segments.
5
+ Clicking a segment navigates back to that depth.
6
+
7
+ Features:
8
+ - Root segment always shown (first entry)
9
+ - Separator chevrons between segments
10
+ - Click / Enter / Space to navigate to a segment
11
+ - Keyboard accessible: role="navigation" with role="list"
12
+ - Current (last) segment is non-interactive and aria-current="page"
13
+ -->
14
+ <script lang="ts">
15
+ import type { CanvasPathSegment } from "./CanvasBreadcrumb.types.js";
16
+
17
+ interface Props {
18
+ /**
19
+ * Full navigation path from root to current canvas.
20
+ * The last entry is the currently visible canvas.
21
+ * The first entry is the root canvas.
22
+ */
23
+ path: CanvasPathSegment[];
24
+ /**
25
+ * Fired when the user clicks on a segment (by index).
26
+ * Index 0 = root. The last index is the current canvas and is non-clickable.
27
+ */
28
+ onnavigate?: (index: number) => void;
29
+ /** Custom CSS class. */
30
+ class?: string;
31
+ }
32
+
33
+ let { path, onnavigate, class: className = "" }: Props = $props();
34
+
35
+ function navigate(index: number) {
36
+ // Don't navigate to the current (last) segment
37
+ if (index >= path.length - 1) return;
38
+ onnavigate?.(index);
39
+ }
40
+
41
+ function handleKeydown(e: KeyboardEvent, index: number) {
42
+ if (e.key === "Enter" || e.key === " ") {
43
+ e.preventDefault();
44
+ navigate(index);
45
+ }
46
+ }
47
+ </script>
48
+
49
+ <nav
50
+ class="canvas-breadcrumb {className}"
51
+ aria-label="Canvas navigation path"
52
+ >
53
+ <ol class="canvas-breadcrumb__list" role="list">
54
+ {#each path as segment, i}
55
+ {@const isCurrent = i === path.length - 1}
56
+ {@const isClickable = !isCurrent}
57
+ {#if i > 0}
58
+ <li class="canvas-breadcrumb__sep" aria-hidden="true">›</li>
59
+ {/if}
60
+ <li class="canvas-breadcrumb__item">
61
+ {#if isClickable}
62
+ <span
63
+ class="canvas-breadcrumb__seg canvas-breadcrumb__seg--link"
64
+ role="button"
65
+ tabindex="0"
66
+ onclick={() => navigate(i)}
67
+ onkeydown={(e) => handleKeydown(e, i)}
68
+ aria-label="Navigate to {segment.label}"
69
+ >{segment.label}</span>
70
+ {:else}
71
+ <span
72
+ class="canvas-breadcrumb__seg canvas-breadcrumb__seg--current"
73
+ aria-current="page"
74
+ >{segment.label}</span>
75
+ {/if}
76
+ </li>
77
+ {/each}
78
+ </ol>
79
+ </nav>
80
+
81
+ <style>
82
+ .canvas-breadcrumb {
83
+ display: flex;
84
+ align-items: center;
85
+ padding: 0 var(--space-3, 12px);
86
+ height: 32px;
87
+ background: var(--surface-2, #1e1e1e);
88
+ border-bottom: 1px solid var(--color-border, #2a2a2a);
89
+ flex-shrink: 0;
90
+ overflow: hidden;
91
+ }
92
+
93
+ .canvas-breadcrumb__list {
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 2px;
97
+ list-style: none;
98
+ margin: 0;
99
+ padding: 0;
100
+ font-size: 12px;
101
+ white-space: nowrap;
102
+ overflow: hidden;
103
+ text-overflow: ellipsis;
104
+ }
105
+
106
+ .canvas-breadcrumb__item {
107
+ display: flex;
108
+ align-items: center;
109
+ }
110
+
111
+ .canvas-breadcrumb__sep {
112
+ color: var(--color-text-subtle, #555);
113
+ font-size: 14px;
114
+ padding: 0 2px;
115
+ user-select: none;
116
+ line-height: 1;
117
+ }
118
+
119
+ .canvas-breadcrumb__seg {
120
+ padding: 2px 6px;
121
+ border-radius: var(--radius-sm, 6px);
122
+ line-height: 1.4;
123
+ max-width: 160px;
124
+ overflow: hidden;
125
+ text-overflow: ellipsis;
126
+ white-space: nowrap;
127
+ }
128
+
129
+ .canvas-breadcrumb__seg--link {
130
+ color: var(--color-text-muted, #888);
131
+ cursor: pointer;
132
+ outline: none;
133
+ transition: color 0.1s, background 0.1s;
134
+ }
135
+
136
+ .canvas-breadcrumb__seg--link:hover,
137
+ .canvas-breadcrumb__seg--link:focus-visible {
138
+ color: var(--color-text, #e8e8e8);
139
+ background: var(--alpha-10, rgba(255, 255, 255, 0.1));
140
+ }
141
+
142
+ .canvas-breadcrumb__seg--current {
143
+ color: var(--color-text, #e8e8e8);
144
+ font-weight: 600;
145
+ }
146
+ </style>
@@ -0,0 +1,19 @@
1
+ import type { CanvasPathSegment } from "./CanvasBreadcrumb.types.js";
2
+ interface Props {
3
+ /**
4
+ * Full navigation path from root to current canvas.
5
+ * The last entry is the currently visible canvas.
6
+ * The first entry is the root canvas.
7
+ */
8
+ path: CanvasPathSegment[];
9
+ /**
10
+ * Fired when the user clicks on a segment (by index).
11
+ * Index 0 = root. The last index is the current canvas and is non-clickable.
12
+ */
13
+ onnavigate?: (index: number) => void;
14
+ /** Custom CSS class. */
15
+ class?: string;
16
+ }
17
+ declare const CanvasBreadcrumb: import("svelte").Component<Props, {}, "">;
18
+ type CanvasBreadcrumb = ReturnType<typeof CanvasBreadcrumb>;
19
+ export default CanvasBreadcrumb;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,296 @@
1
+ <!--
2
+ ChatInput — Multiline chat input with send button and optional file attachment.
3
+
4
+ Features:
5
+ - Auto-growing textarea (up to maxRows)
6
+ - Send on Enter (Shift+Enter inserts newline)
7
+ - File attachment button triggers hidden <input type="file">
8
+ - Shows attached filename; clears on send
9
+ - Disabled state
10
+ - onsend(value, attachment?) callback
11
+ -->
12
+ <script lang="ts">
13
+ interface Props {
14
+ /** Current text value (two-way bindable). */
15
+ value?: string;
16
+ placeholder?: string;
17
+ disabled?: boolean;
18
+ /** Maximum visible rows before scrolling. Default: 5 */
19
+ maxRows?: number;
20
+ /** Custom CSS class. */
21
+ class?: string;
22
+ /** Fired when the user sends the message. */
23
+ onsend?: (value: string, attachment?: File) => void;
24
+ }
25
+
26
+ let {
27
+ value = $bindable(""),
28
+ placeholder = "Type a message…",
29
+ disabled = false,
30
+ maxRows = 5,
31
+ class: className = "",
32
+ onsend,
33
+ }: Props = $props();
34
+
35
+ let textareaEl: HTMLTextAreaElement | undefined = $state();
36
+ let fileInputEl: HTMLInputElement | undefined = $state();
37
+ let attachment: File | undefined = $state();
38
+
39
+ // Auto-grow logic
40
+ function resize() {
41
+ const el = textareaEl;
42
+ if (!el) return;
43
+ el.style.height = "auto";
44
+ const lineH = parseInt(getComputedStyle(el).lineHeight || "20", 10);
45
+ const max = lineH * maxRows + parseInt(getComputedStyle(el).paddingTop || "0", 10) * 2;
46
+ el.style.height = Math.min(el.scrollHeight, max) + "px";
47
+ }
48
+
49
+ function handleInput(e: Event) {
50
+ value = (e.target as HTMLTextAreaElement).value;
51
+ resize();
52
+ }
53
+
54
+ function handleKeydown(e: KeyboardEvent) {
55
+ if (e.key === "Enter" && !e.shiftKey) {
56
+ e.preventDefault();
57
+ send();
58
+ }
59
+ }
60
+
61
+ function send() {
62
+ const trimmed = (value ?? "").trim();
63
+ if (!trimmed && !attachment) return;
64
+ onsend?.(trimmed, attachment);
65
+ value = "";
66
+ attachment = undefined;
67
+ if (textareaEl) {
68
+ textareaEl.style.height = "auto";
69
+ }
70
+ }
71
+
72
+ function openFilePicker() {
73
+ fileInputEl?.click();
74
+ }
75
+
76
+ function handleFileChange(e: Event) {
77
+ const input = e.target as HTMLInputElement;
78
+ attachment = input.files?.[0];
79
+ // Reset so the same file can be re-selected if removed
80
+ input.value = "";
81
+ }
82
+
83
+ function removeAttachment() {
84
+ attachment = undefined;
85
+ }
86
+ </script>
87
+
88
+ <div class="chat-input {className}" class:chat-input--disabled={disabled}>
89
+ {#if attachment}
90
+ <div class="chat-input__attachment">
91
+ <span class="chat-input__attachment-name">📎 {attachment.name}</span>
92
+ <button
93
+ class="chat-input__attachment-remove"
94
+ onclick={removeAttachment}
95
+ aria-label="Remove attachment"
96
+ type="button"
97
+ >✕</button>
98
+ </div>
99
+ {/if}
100
+
101
+ <div class="chat-input__row">
102
+ <button
103
+ class="chat-input__attach-btn"
104
+ onclick={openFilePicker}
105
+ {disabled}
106
+ type="button"
107
+ aria-label="Attach file"
108
+ title="Attach file"
109
+ >📎</button>
110
+
111
+ <textarea
112
+ bind:this={textareaEl}
113
+ class="chat-input__textarea"
114
+ {placeholder}
115
+ {disabled}
116
+ rows="1"
117
+ value={value}
118
+ oninput={handleInput}
119
+ onkeydown={handleKeydown}
120
+ aria-label="Message input"
121
+ aria-multiline="true"
122
+ ></textarea>
123
+
124
+ <button
125
+ class="chat-input__send-btn"
126
+ onclick={send}
127
+ disabled={disabled || (!(value ?? "").trim() && !attachment)}
128
+ type="button"
129
+ aria-label="Send message"
130
+ >
131
+ <svg class="chat-input__send-icon" viewBox="0 0 20 20" fill="none" aria-hidden="true">
132
+ <path d="M3 10L17 3L10 17L9 11L3 10Z" fill="currentColor" stroke="currentColor" stroke-width="0.5" stroke-linejoin="round"/>
133
+ </svg>
134
+ </button>
135
+ </div>
136
+
137
+ <!-- Hidden file input -->
138
+ <input
139
+ bind:this={fileInputEl}
140
+ class="chat-input__file-hidden"
141
+ type="file"
142
+ onchange={handleFileChange}
143
+ aria-hidden="true"
144
+ tabindex="-1"
145
+ />
146
+ </div>
147
+
148
+ <style>
149
+ .chat-input {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: var(--space-2, 8px);
153
+ padding: var(--space-3, 12px);
154
+ background: var(--surface-1, #141414);
155
+ border-top: 1px solid var(--color-border, #2a2a2a);
156
+ }
157
+
158
+ /* ── Attachment pill ── */
159
+ .chat-input__attachment {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: var(--space-2, 8px);
163
+ background: color-mix(in srgb, var(--color-accent, #6366f1) 12%, var(--surface-2, #1e1e1e));
164
+ border: 1px solid color-mix(in srgb, var(--color-accent, #6366f1) 30%, transparent);
165
+ border-radius: var(--radius-full, 9999px);
166
+ padding: 4px var(--space-3, 12px);
167
+ width: fit-content;
168
+ max-width: 100%;
169
+ }
170
+
171
+ .chat-input__attachment-name {
172
+ font-size: var(--text-xs, 12px);
173
+ color: var(--color-text, #e8e8e8);
174
+ white-space: nowrap;
175
+ overflow: hidden;
176
+ text-overflow: ellipsis;
177
+ max-width: 260px;
178
+ }
179
+
180
+ .chat-input__attachment-remove {
181
+ background: none;
182
+ border: none;
183
+ color: var(--color-text-muted, #888);
184
+ cursor: pointer;
185
+ font-size: var(--text-xs, 12px);
186
+ padding: 0;
187
+ line-height: 1;
188
+ flex-shrink: 0;
189
+ }
190
+
191
+ .chat-input__attachment-remove:hover { color: var(--color-danger, #ef4444); }
192
+
193
+ /* ── Input row ── */
194
+ .chat-input__row {
195
+ display: flex;
196
+ align-items: flex-end;
197
+ gap: var(--space-2, 8px);
198
+ background: var(--surface-2, #1e1e1e);
199
+ border: 1.5px solid var(--color-border, #2a2a2a);
200
+ border-radius: var(--radius-lg, 16px);
201
+ padding: var(--space-2, 8px) var(--space-2, 8px) var(--space-2, 8px) var(--space-1, 4px);
202
+ transition: border-color 0.15s ease;
203
+ }
204
+
205
+ .chat-input__row:focus-within {
206
+ border-color: var(--color-accent, #6366f1);
207
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent, #6366f1) 15%, transparent);
208
+ }
209
+
210
+ /* ── Textarea ── */
211
+ .chat-input__textarea {
212
+ flex: 1;
213
+ min-width: 0;
214
+ background: transparent;
215
+ border: none;
216
+ outline: none;
217
+ resize: none;
218
+ color: var(--color-text, #e8e8e8);
219
+ font-family: inherit;
220
+ font-size: var(--text-base, 16px);
221
+ line-height: 1.5;
222
+ padding: 4px var(--space-2, 8px);
223
+ overflow-y: auto;
224
+ max-height: 160px;
225
+ }
226
+
227
+ .chat-input__textarea::placeholder {
228
+ color: var(--color-text-muted, #888);
229
+ }
230
+
231
+ /* ── Attach button ── */
232
+ .chat-input__attach-btn {
233
+ background: none;
234
+ border: none;
235
+ color: var(--color-text-muted, #888);
236
+ cursor: pointer;
237
+ font-size: 18px;
238
+ padding: 4px;
239
+ border-radius: var(--radius-sm, 6px);
240
+ line-height: 1;
241
+ transition: color 0.1s, background 0.1s;
242
+ flex-shrink: 0;
243
+ }
244
+
245
+ .chat-input__attach-btn:hover:not(:disabled) {
246
+ color: var(--color-accent, #6366f1);
247
+ background: color-mix(in srgb, var(--color-accent, #6366f1) 10%, transparent);
248
+ }
249
+
250
+ /* ── Send button ── */
251
+ .chat-input__send-btn {
252
+ width: 36px;
253
+ height: 36px;
254
+ border-radius: 50%;
255
+ border: none;
256
+ background: var(--color-accent, #6366f1);
257
+ color: white;
258
+ cursor: pointer;
259
+ display: flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ flex-shrink: 0;
263
+ transition: background 0.15s, opacity 0.15s;
264
+ }
265
+
266
+ .chat-input__send-btn:hover:not(:disabled) {
267
+ background: var(--color-accent-hover, #818cf8);
268
+ }
269
+
270
+ .chat-input__send-btn:disabled {
271
+ opacity: 0.35;
272
+ cursor: not-allowed;
273
+ }
274
+
275
+ .chat-input__send-icon {
276
+ width: 18px;
277
+ height: 18px;
278
+ }
279
+
280
+ /* ── Disabled state ── */
281
+ .chat-input--disabled .chat-input__row {
282
+ opacity: 0.45;
283
+ pointer-events: none;
284
+ }
285
+
286
+ /* ── Hidden file input ── */
287
+ .chat-input__file-hidden {
288
+ position: absolute;
289
+ opacity: 0;
290
+ width: 1px;
291
+ height: 1px;
292
+ overflow: hidden;
293
+ clip: rect(0, 0, 0, 0);
294
+ pointer-events: none;
295
+ }
296
+ </style>
@@ -0,0 +1,15 @@
1
+ interface Props {
2
+ /** Current text value (two-way bindable). */
3
+ value?: string;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ /** Maximum visible rows before scrolling. Default: 5 */
7
+ maxRows?: number;
8
+ /** Custom CSS class. */
9
+ class?: string;
10
+ /** Fired when the user sends the message. */
11
+ onsend?: (value: string, attachment?: File) => void;
12
+ }
13
+ declare const ChatInput: import("svelte").Component<Props, {}, "value">;
14
+ type ChatInput = ReturnType<typeof ChatInput>;
15
+ export default ChatInput;