@placeholderco/placeholder-ui 1.0.12 → 2.1.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 (278) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +86 -135
  99. package/dist/form/Autocomplete.svelte.d.ts +6 -8
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +59 -54
  103. package/dist/form/Checkbox.svelte.d.ts +14 -5
  104. package/dist/form/Chips.svelte +29 -13
  105. package/dist/form/Chips.svelte.d.ts +16 -2
  106. package/dist/form/ColorPicker.svelte +957 -0
  107. package/dist/form/ColorPicker.svelte.d.ts +78 -0
  108. package/dist/form/ComboBox.svelte +30 -20
  109. package/dist/form/ComboBox.svelte.d.ts +1 -1
  110. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  111. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  112. package/dist/form/ComboBoxMulti.svelte +38 -24
  113. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  114. package/dist/form/CronBuilder.svelte +84 -67
  115. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  116. package/dist/form/DatePicker.svelte +148 -195
  117. package/dist/form/DateRangePicker.svelte +101 -98
  118. package/dist/form/DateTimePicker.svelte +167 -208
  119. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  120. package/dist/form/FormGroup.svelte +33 -10
  121. package/dist/form/FormGroup.svelte.d.ts +12 -2
  122. package/dist/form/Number.svelte +70 -47
  123. package/dist/form/Number.svelte.d.ts +36 -6
  124. package/dist/form/Radio.svelte +18 -18
  125. package/dist/form/RadioGroup.svelte +23 -12
  126. package/dist/form/RadioGroup.svelte.d.ts +13 -2
  127. package/dist/form/SegmentedControl.svelte +76 -56
  128. package/dist/form/SegmentedControl.svelte.d.ts +9 -4
  129. package/dist/form/Select.svelte +150 -233
  130. package/dist/form/Select.svelte.d.ts +29 -5
  131. package/dist/form/SelectMulti.svelte +163 -277
  132. package/dist/form/SelectMulti.svelte.d.ts +36 -7
  133. package/dist/form/Slider.svelte +183 -269
  134. package/dist/form/Slider.svelte.d.ts +32 -17
  135. package/dist/form/StringArrayBuilder.svelte +249 -0
  136. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  137. package/dist/form/Switch.svelte +164 -106
  138. package/dist/form/Switch.svelte.d.ts +22 -3
  139. package/dist/form/TextArea.svelte +49 -29
  140. package/dist/form/TextArea.svelte.d.ts +23 -3
  141. package/dist/form/Textbox.svelte +69 -68
  142. package/dist/form/Textbox.svelte.d.ts +32 -4
  143. package/dist/form/TimePicker.svelte +42 -47
  144. package/dist/form/TimePicker.svelte.d.ts +12 -2
  145. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  146. package/dist/form/comboBoxCore.svelte.js +243 -0
  147. package/dist/icon/Icon.svelte +8 -3
  148. package/dist/icon/Icon.svelte.d.ts +8 -3
  149. package/dist/icon/file-type-bmp.svg +1 -0
  150. package/dist/icon/file-type-csv.svg +1 -0
  151. package/dist/icon/file-type-doc.svg +1 -0
  152. package/dist/icon/file-type-docx.svg +1 -0
  153. package/dist/icon/file-type-jpg.svg +1 -0
  154. package/dist/icon/file-type-pdf.svg +1 -0
  155. package/dist/icon/file-type-png.svg +1 -0
  156. package/dist/icon/file-type-ppt.svg +1 -0
  157. package/dist/icon/file-type-svg.svg +1 -0
  158. package/dist/icon/file-type-txt.svg +1 -0
  159. package/dist/icon/file-type-xls.svg +1 -0
  160. package/dist/icon/file-type-zip.svg +1 -0
  161. package/dist/icon/index.d.ts +166 -151
  162. package/dist/icon/index.js +166 -152
  163. package/dist/icon/logo-placeholder.svg +6 -0
  164. package/dist/icon/source-code.svg +1 -0
  165. package/dist/icon/sun-moon.svg +1 -0
  166. package/dist/icon/table.svg +6 -0
  167. package/dist/index.d.ts +146 -82
  168. package/dist/index.js +77 -78
  169. package/dist/layout/AppShell.svelte +12 -162
  170. package/dist/layout/AppShell.svelte.d.ts +6 -27
  171. package/dist/layout/CustomNavbar.svelte +9 -7
  172. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  173. package/dist/layout/Navbar.svelte +161 -129
  174. package/dist/layout/Navbar.svelte.d.ts +29 -15
  175. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  176. package/dist/layout/NavbarItemView.svelte +182 -0
  177. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  178. package/dist/layout/Sidenav.svelte +280 -341
  179. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  180. package/dist/models/ComboBoxItem.d.ts +2 -0
  181. package/dist/models/Employee.d.ts +53 -0
  182. package/dist/models/Employee.js +23 -0
  183. package/dist/models/Hyperlink.d.ts +3 -1
  184. package/dist/models/LinkCollectionItem.d.ts +3 -1
  185. package/dist/models/NavbarItem.d.ts +6 -2
  186. package/dist/theme.svelte.d.ts +0 -1
  187. package/dist/theme.svelte.js +46 -58
  188. package/dist/ui/Accordion.svelte +81 -242
  189. package/dist/ui/Accordion.svelte.d.ts +37 -14
  190. package/dist/ui/AccordionItem.svelte +303 -0
  191. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  192. package/dist/ui/ActionIcon.svelte +25 -21
  193. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  194. package/dist/ui/Badge.svelte +163 -171
  195. package/dist/ui/Badge.svelte.d.ts +13 -5
  196. package/dist/ui/Button.svelte +178 -205
  197. package/dist/ui/Button.svelte.d.ts +19 -2
  198. package/dist/ui/ButtonVariant.d.ts +15 -1
  199. package/dist/ui/ContextMenu.svelte +146 -0
  200. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  201. package/dist/ui/ContextMenuList.svelte +188 -0
  202. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  203. package/dist/ui/Dialog.svelte +183 -111
  204. package/dist/ui/Dialog.svelte.d.ts +21 -4
  205. package/dist/ui/Drawer.svelte +237 -184
  206. package/dist/ui/Drawer.svelte.d.ts +20 -2
  207. package/dist/ui/Dropdown.svelte +24 -14
  208. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  209. package/dist/ui/Dropzone.svelte +159 -57
  210. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  211. package/dist/ui/Link.svelte +12 -4
  212. package/dist/ui/Link.svelte.d.ts +8 -2
  213. package/dist/ui/Loader.svelte +23 -18
  214. package/dist/ui/Loader.svelte.d.ts +5 -2
  215. package/dist/ui/Logo.svelte +37 -0
  216. package/dist/ui/Logo.svelte.d.ts +16 -0
  217. package/dist/ui/MultiSortable.svelte +287 -0
  218. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  219. package/dist/ui/Pagination.svelte +15 -9
  220. package/dist/ui/Pagination.svelte.d.ts +6 -2
  221. package/dist/ui/Popover.svelte +43 -65
  222. package/dist/ui/Popover.svelte.d.ts +14 -5
  223. package/dist/ui/ProgressBar.svelte +28 -30
  224. package/dist/ui/ProgressBarVariant.d.ts +7 -1
  225. package/dist/ui/SegmentedControl.svelte +0 -0
  226. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  227. package/dist/ui/Sortable.svelte +163 -0
  228. package/dist/ui/Sortable.svelte.d.ts +47 -0
  229. package/dist/ui/Table.svelte +383 -80
  230. package/dist/ui/Table.svelte.d.ts +43 -2
  231. package/dist/ui/Tabs.svelte +22 -18
  232. package/dist/ui/Tabs.svelte.d.ts +6 -2
  233. package/dist/ui/ThemeSwitcher.svelte +26 -7
  234. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  235. package/dist/ui/Tooltip.svelte +312 -180
  236. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  237. package/dist/uiTheme.svelte.d.ts +86 -0
  238. package/dist/uiTheme.svelte.js +214 -0
  239. package/dist/util/DateFunctions.js +1 -1
  240. package/dist/util/DragDrop.d.ts +50 -0
  241. package/dist/util/DragDrop.js +154 -0
  242. package/dist/util/NavigateTo.js +2 -2
  243. package/dist/util/dayjs.d.ts +4 -0
  244. package/dist/util/dayjs.js +15 -0
  245. package/package.json +66 -26
  246. package/dist/display/Alert.svelte +0 -179
  247. package/dist/display/Alert.svelte.d.ts +0 -13
  248. package/dist/form/FileInput.svelte +0 -235
  249. package/dist/form/FileInput.svelte.d.ts +0 -23
  250. package/dist/form/PasswordInput.svelte +0 -253
  251. package/dist/form/PasswordInput.svelte.d.ts +0 -34
  252. package/dist/form/Rating.svelte +0 -235
  253. package/dist/form/Rating.svelte.d.ts +0 -21
  254. package/dist/styles/components.css +0 -199
  255. package/dist/styles/dark.css +0 -146
  256. package/dist/styles/index.css +0 -116
  257. package/dist/styles/reset.css +0 -110
  258. package/dist/styles/semantic.css +0 -86
  259. package/dist/styles/tokens.css +0 -203
  260. package/dist/styles/utilities.css +0 -523
  261. package/dist/ui/Breadcrumbs.svelte +0 -131
  262. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  263. package/dist/ui/LoadingOverlay.svelte +0 -53
  264. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  265. package/dist/ui/Progress.svelte +0 -191
  266. package/dist/ui/Progress.svelte.d.ts +0 -15
  267. package/dist/ui/RingProgress.svelte +0 -141
  268. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  269. package/dist/ui/Skeleton.svelte +0 -85
  270. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  271. package/dist/ui/Stepper.svelte +0 -355
  272. package/dist/ui/Stepper.svelte.d.ts +0 -24
  273. package/dist/ui/Timeline.svelte +0 -225
  274. package/dist/ui/Timeline.svelte.d.ts +0 -23
  275. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  276. /package/dist/{actions → util}/ClickOutside.js +0 -0
  277. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  278. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -0,0 +1,306 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte'
3
+ import {
4
+ EditorView,
5
+ lineNumbers as lineNumbersGutter,
6
+ highlightActiveLineGutter,
7
+ highlightSpecialChars,
8
+ drawSelection,
9
+ dropCursor,
10
+ rectangularSelection,
11
+ crosshairCursor,
12
+ highlightActiveLine,
13
+ keymap
14
+ } from '@codemirror/view'
15
+ import { EditorState, Compartment } from '@codemirror/state'
16
+ import {
17
+ foldGutter,
18
+ foldKeymap,
19
+ indentOnInput,
20
+ syntaxHighlighting,
21
+ defaultHighlightStyle,
22
+ bracketMatching
23
+ } from '@codemirror/language'
24
+ import { history, defaultKeymap, historyKeymap } from '@codemirror/commands'
25
+ import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
26
+ import {
27
+ closeBrackets,
28
+ closeBracketsKeymap,
29
+ autocompletion,
30
+ completionKeymap
31
+ } from '@codemirror/autocomplete'
32
+ import { lintKeymap } from '@codemirror/lint'
33
+ import { javascript } from '@codemirror/lang-javascript'
34
+ import { html } from '@codemirror/lang-html'
35
+ import { css } from '@codemirror/lang-css'
36
+ import { json } from '@codemirror/lang-json'
37
+ import { markdown } from '@codemirror/lang-markdown'
38
+ import { oneDark } from '@codemirror/theme-one-dark'
39
+ import { themeState } from '../theme.svelte.js'
40
+ import ActionIcon from '../ui/ActionIcon.svelte'
41
+ import { iconCopy, iconCheck } from '../icon/index.js'
42
+
43
+ export interface CodeMirrorProps {
44
+ /** Editor content value (bindable) */
45
+ value?: string
46
+ /** Programming language for syntax highlighting */
47
+ language?: 'javascript' | 'html' | 'css' | 'json' | 'typescript' | 'markdown'
48
+ /** Color theme for the editor (defaults to app theme) */
49
+ theme?: 'light' | 'dark' | 'auto'
50
+ /** Disable editing */
51
+ readOnly?: boolean
52
+ /** Show line numbers in the gutter */
53
+ lineNumbers?: boolean
54
+ /** Show a button that copies the editor contents to the clipboard */
55
+ copyButton?: boolean
56
+ /** Additional CSS classes */
57
+ class?: string
58
+ /** Height of the editor */
59
+ height?: string
60
+ /** Callback when content changes */
61
+ onChange?: (value: string) => void
62
+ /** Callback after the contents are copied to the clipboard */
63
+ onCopy?: (value: string) => void
64
+ }
65
+
66
+ let {
67
+ value = $bindable(''),
68
+ language = 'javascript',
69
+ theme = 'auto',
70
+ readOnly = false,
71
+ lineNumbers = true,
72
+ copyButton = false,
73
+ class: className = '',
74
+ height = '500px',
75
+ onChange,
76
+ onCopy
77
+ }: CodeMirrorProps = $props()
78
+
79
+ let editorContainer: HTMLDivElement | undefined = $state()
80
+ let view: EditorView | null = $state(null)
81
+ let themeCompartment = new Compartment()
82
+ let gutterCompartment = new Compartment()
83
+ let copied = $state(false)
84
+ let copiedTimeout: ReturnType<typeof setTimeout>
85
+
86
+ // basicSetup minus the gutters, which we swap in via a compartment so the
87
+ // `lineNumbers` prop can be toggled after the editor is created.
88
+ const baseSetup = [
89
+ highlightSpecialChars(),
90
+ history(),
91
+ drawSelection(),
92
+ dropCursor(),
93
+ EditorState.allowMultipleSelections.of(true),
94
+ indentOnInput(),
95
+ syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
96
+ bracketMatching(),
97
+ closeBrackets(),
98
+ autocompletion(),
99
+ rectangularSelection(),
100
+ crosshairCursor(),
101
+ highlightActiveLine(),
102
+ highlightSelectionMatches(),
103
+ keymap.of([
104
+ ...closeBracketsKeymap,
105
+ ...defaultKeymap,
106
+ ...searchKeymap,
107
+ ...historyKeymap,
108
+ ...foldKeymap,
109
+ ...completionKeymap,
110
+ ...lintKeymap
111
+ ])
112
+ ]
113
+
114
+ const getGutterExtension = (show: boolean) =>
115
+ show ? [lineNumbersGutter(), highlightActiveLineGutter(), foldGutter()] : []
116
+
117
+ // Derive effective dark mode from prop or app theme
118
+ let isDark = $derived(
119
+ theme === 'auto' ? themeState.isDarkMode : theme === 'dark'
120
+ )
121
+
122
+ const getLanguageExtension = (lang: string) => {
123
+ switch (lang) {
124
+ case 'javascript':
125
+ case 'typescript':
126
+ return javascript()
127
+ case 'html':
128
+ return html()
129
+ case 'css':
130
+ return css()
131
+ case 'json':
132
+ return json()
133
+ case 'markdown':
134
+ return markdown()
135
+ default:
136
+ return javascript()
137
+ }
138
+ }
139
+
140
+ onMount(() => {
141
+ if (!editorContainer) return
142
+
143
+ const extensions = [
144
+ gutterCompartment.of(getGutterExtension(lineNumbers)),
145
+ baseSetup,
146
+ getLanguageExtension(language),
147
+ EditorView.lineWrapping,
148
+ themeCompartment.of(isDark ? oneDark : [])
149
+ ]
150
+
151
+ if (readOnly) {
152
+ extensions.push(EditorState.readOnly.of(true))
153
+ }
154
+
155
+ // Always add update listener to sync bindable value
156
+ extensions.push(
157
+ EditorView.updateListener.of((update) => {
158
+ if (update.docChanged) {
159
+ const newValue = update.state.doc.toString()
160
+ value = newValue
161
+ if (onChange) {
162
+ onChange(newValue)
163
+ }
164
+ }
165
+ })
166
+ )
167
+
168
+ const startState = EditorState.create({
169
+ doc: value,
170
+ extensions
171
+ })
172
+
173
+ view = new EditorView({
174
+ state: startState,
175
+ parent: editorContainer
176
+ })
177
+ })
178
+
179
+ onDestroy(() => {
180
+ view?.destroy()
181
+ clearTimeout(copiedTimeout)
182
+ })
183
+
184
+ async function copyToClipboard() {
185
+ const text = view?.state.doc.toString() ?? value
186
+
187
+ try {
188
+ await navigator.clipboard.writeText(text)
189
+ copied = true
190
+ clearTimeout(copiedTimeout)
191
+ copiedTimeout = setTimeout(() => (copied = false), 2000)
192
+ onCopy?.(text)
193
+ } catch (err) {
194
+ console.error('CodeMirror: failed to copy to clipboard', err)
195
+ }
196
+ }
197
+
198
+ // Watch for theme changes and reconfigure
199
+ $effect(() => {
200
+ if (!view) return
201
+
202
+ view.dispatch({
203
+ effects: themeCompartment.reconfigure(isDark ? oneDark : [])
204
+ })
205
+ })
206
+
207
+ // Watch for lineNumbers changes and reconfigure the gutter
208
+ $effect(() => {
209
+ if (!view) return
210
+
211
+ view.dispatch({
212
+ effects: gutterCompartment.reconfigure(getGutterExtension(lineNumbers))
213
+ })
214
+ })
215
+
216
+ // Watch for external value changes and update editor
217
+ $effect(() => {
218
+ if (!view) return
219
+
220
+ const currentValue = view.state.doc.toString()
221
+ if (value !== currentValue) {
222
+ view.dispatch({
223
+ changes: { from: 0, to: currentValue.length, insert: value }
224
+ })
225
+ }
226
+ })
227
+
228
+ // Public API methods
229
+ export function getValue(): string {
230
+ return view?.state.doc.toString() || ''
231
+ }
232
+
233
+ export function setValue(newValue: string) {
234
+ if (!view) return
235
+ view.dispatch({
236
+ changes: { from: 0, to: view.state.doc.length, insert: newValue }
237
+ })
238
+ }
239
+
240
+ export function getView(): EditorView | null {
241
+ return view
242
+ }
243
+
244
+ export function focus() {
245
+ view?.focus()
246
+ }
247
+ </script>
248
+
249
+ <div class="codemirror-container {className}" style="height: {height}">
250
+ {#if copyButton}
251
+ <div class="codemirror-copy" class:copied class:dark={isDark}>
252
+ <ActionIcon
253
+ svg={copied ? iconCheck : iconCopy}
254
+ size="16px"
255
+ tooltip={copied ? 'Copied' : 'Copy to clipboard'}
256
+ ariaLabel={copied ? 'Copied to clipboard' : 'Copy to clipboard'}
257
+ onclick={copyToClipboard}
258
+ />
259
+ </div>
260
+ {/if}
261
+ <div class="codemirror-wrapper" bind:this={editorContainer}></div>
262
+ </div>
263
+
264
+ <style>
265
+ .codemirror-container {
266
+ position: relative;
267
+ width: 100%;
268
+ border: 1px solid var(--border-color, #ddd);
269
+ border-radius: 6px;
270
+ overflow: hidden;
271
+ background: var(--input-bg-color);
272
+ }
273
+
274
+ /* The ActionIcon supplies the button chrome; this only places it and gives it
275
+ an opaque backing so it stays readable over the code underneath. */
276
+ .codemirror-copy {
277
+ position: absolute;
278
+ top: 6px;
279
+ right: 6px;
280
+ z-index: 2;
281
+ display: inline-flex;
282
+ border-radius: 6px;
283
+ opacity: 0.65;
284
+ transition: opacity 0.15s ease;
285
+ }
286
+
287
+ .codemirror-container:hover .codemirror-copy,
288
+ .codemirror-copy:hover,
289
+ .codemirror-copy:focus-within,
290
+ .codemirror-copy.copied {
291
+ opacity: 1;
292
+ }
293
+
294
+ .codemirror-wrapper {
295
+ width: 100%;
296
+ height: 100%;
297
+ }
298
+
299
+ .codemirror-wrapper :global(.cm-editor) {
300
+ height: 100%;
301
+ }
302
+
303
+ .codemirror-wrapper :global(.cm-scroller) {
304
+ overflow: auto;
305
+ }
306
+ </style>
@@ -0,0 +1,31 @@
1
+ import { EditorView } from '@codemirror/view';
2
+ export interface CodeMirrorProps {
3
+ /** Editor content value (bindable) */
4
+ value?: string;
5
+ /** Programming language for syntax highlighting */
6
+ language?: 'javascript' | 'html' | 'css' | 'json' | 'typescript' | 'markdown';
7
+ /** Color theme for the editor (defaults to app theme) */
8
+ theme?: 'light' | 'dark' | 'auto';
9
+ /** Disable editing */
10
+ readOnly?: boolean;
11
+ /** Show line numbers in the gutter */
12
+ lineNumbers?: boolean;
13
+ /** Show a button that copies the editor contents to the clipboard */
14
+ copyButton?: boolean;
15
+ /** Additional CSS classes */
16
+ class?: string;
17
+ /** Height of the editor */
18
+ height?: string;
19
+ /** Callback when content changes */
20
+ onChange?: (value: string) => void;
21
+ /** Callback after the contents are copied to the clipboard */
22
+ onCopy?: (value: string) => void;
23
+ }
24
+ declare const CodeMirror: import("svelte").Component<CodeMirrorProps, {
25
+ getValue: () => string;
26
+ setValue: (newValue: string) => void;
27
+ getView: () => EditorView | null;
28
+ focus: () => void;
29
+ }, "value">;
30
+ type CodeMirror = ReturnType<typeof CodeMirror>;
31
+ export default CodeMirror;
@@ -0,0 +1,307 @@
1
+ <script lang="ts">
2
+ import type { Editor } from '@tiptap/core';
3
+ import { onMount } from 'svelte';
4
+ import {
5
+ Icon,
6
+ iconCheck,
7
+ iconEdit,
8
+ iconExternalLink,
9
+ iconTrash,
10
+ iconX
11
+ } from '../../icon/index.js';
12
+
13
+ interface Props {
14
+ editor: Editor;
15
+ editable?: boolean;
16
+ }
17
+
18
+ let { editor, editable = true }: Props = $props();
19
+
20
+ let href = $state('');
21
+ let target = $state<string | null>(null);
22
+ let isEditing = $state(false);
23
+ let draftHref = $state('');
24
+ let draftOpenInNewTab = $state(false);
25
+ let linkInputElement: HTMLInputElement | undefined = $state();
26
+
27
+ function refreshLinkState() {
28
+ const attrs = editor.getAttributes('link') as {
29
+ href?: string;
30
+ target?: string | null;
31
+ };
32
+
33
+ href = attrs.href || '';
34
+ target = attrs.target || null;
35
+
36
+ if (!isEditing) {
37
+ draftHref = href;
38
+ draftOpenInNewTab = target === '_blank';
39
+ }
40
+ }
41
+
42
+ function openLink() {
43
+ if (!href) return;
44
+
45
+ const linkTarget = target || '_self';
46
+ const openedWindow = window.open(
47
+ href,
48
+ linkTarget,
49
+ linkTarget === '_blank' ? 'noopener,noreferrer' : undefined
50
+ );
51
+
52
+ if (openedWindow && linkTarget === '_blank') {
53
+ openedWindow.opener = null;
54
+ }
55
+ }
56
+
57
+ function startEditing() {
58
+ if (!editable) return;
59
+
60
+ draftHref = href;
61
+ draftOpenInNewTab = target === '_blank';
62
+ isEditing = true;
63
+
64
+ setTimeout(() => {
65
+ linkInputElement?.focus();
66
+ linkInputElement?.select();
67
+ }, 0);
68
+ }
69
+
70
+ function applyLink() {
71
+ if (!editable) return;
72
+
73
+ const nextHref = draftHref.trim();
74
+
75
+ if (!nextHref) {
76
+ removeLink();
77
+ return;
78
+ }
79
+
80
+ editor
81
+ .chain()
82
+ .focus()
83
+ .extendMarkRange('link')
84
+ .setLink({
85
+ href: nextHref,
86
+ target: draftOpenInNewTab ? '_blank' : null,
87
+ rel: draftOpenInNewTab ? 'noopener noreferrer' : null
88
+ })
89
+ .run();
90
+
91
+ isEditing = false;
92
+ refreshLinkState();
93
+ }
94
+
95
+ function cancelEditing() {
96
+ isEditing = false;
97
+ refreshLinkState();
98
+ editor.commands.focus();
99
+ }
100
+
101
+ function removeLink() {
102
+ if (!editable) return;
103
+
104
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
105
+ isEditing = false;
106
+ refreshLinkState();
107
+ }
108
+
109
+ function handleEditKeydown(event: KeyboardEvent) {
110
+ if (event.key === 'Enter') {
111
+ event.preventDefault();
112
+ applyLink();
113
+ }
114
+
115
+ if (event.key === 'Escape') {
116
+ event.preventDefault();
117
+ cancelEditing();
118
+ }
119
+ }
120
+
121
+ onMount(() => {
122
+ refreshLinkState();
123
+ editor.on('selectionUpdate', refreshLinkState);
124
+ editor.on('transaction', refreshLinkState);
125
+
126
+ return () => {
127
+ editor.off('selectionUpdate', refreshLinkState);
128
+ editor.off('transaction', refreshLinkState);
129
+ };
130
+ });
131
+ </script>
132
+
133
+ <div class="link-action-menu">
134
+ {#if isEditing && editable}
135
+ <div class="link-edit-row">
136
+ <input
137
+ bind:this={linkInputElement}
138
+ bind:value={draftHref}
139
+ class="link-input"
140
+ onkeydown={handleEditKeydown}
141
+ placeholder="Enter URL"
142
+ type="text"
143
+ />
144
+ <button class="link-icon-button primary" onclick={applyLink} title="Apply" type="button">
145
+ <Icon svg={iconCheck} size="16px" />
146
+ </button>
147
+ <button class="link-icon-button" onclick={cancelEditing} title="Cancel" type="button">
148
+ <Icon svg={iconX} size="16px" />
149
+ </button>
150
+ </div>
151
+ <label class="link-checkbox-label">
152
+ <input bind:checked={draftOpenInNewTab} class="link-checkbox" type="checkbox" />
153
+ <span>Open in new tab</span>
154
+ </label>
155
+ {:else}
156
+ <button class="link-url" onclick={openLink} title={href} type="button">
157
+ {href}
158
+ </button>
159
+ <button class="link-icon-button primary" onclick={openLink} title="Open link" type="button">
160
+ <Icon svg={iconExternalLink} size="16px" />
161
+ </button>
162
+ {#if editable}
163
+ <button class="link-icon-button" onclick={startEditing} title="Edit link" type="button">
164
+ <Icon svg={iconEdit} size="16px" />
165
+ </button>
166
+ <button class="link-icon-button danger" onclick={removeLink} title="Remove link" type="button">
167
+ <Icon svg={iconTrash} size="16px" />
168
+ </button>
169
+ {/if}
170
+ {/if}
171
+ </div>
172
+
173
+ <style>
174
+ .link-action-menu {
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 6px;
178
+ max-width: min(560px, calc(100vw - 32px));
179
+ padding: 6px;
180
+ border: 1px solid var(--border-color);
181
+ border-radius: 6px;
182
+ background: var(--paper-title-bg);
183
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
184
+ color: var(--text-color);
185
+ font-family: var(--ui-font-family);
186
+ }
187
+
188
+ :global(.dark) .link-action-menu {
189
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
190
+ }
191
+
192
+ .link-url {
193
+ min-width: 0;
194
+ max-width: 320px;
195
+ padding: 5px 8px;
196
+ overflow: hidden;
197
+ border: 1px solid var(--border-color);
198
+ border-radius: 4px;
199
+ background: var(--paper-body-bg);
200
+ color: var(--ui-tertiary-dark);
201
+ cursor: pointer;
202
+ font: inherit;
203
+ font-size: 13px;
204
+ text-overflow: ellipsis;
205
+ white-space: nowrap;
206
+ }
207
+
208
+ :global(.dark) .link-url {
209
+ color: var(--ui-tertiary);
210
+ }
211
+
212
+ .link-edit-row {
213
+ display: flex;
214
+ align-items: center;
215
+ gap: 4px;
216
+ min-width: min(360px, calc(100vw - 80px));
217
+ }
218
+
219
+ .link-input {
220
+ flex: 1;
221
+ min-width: 0;
222
+ padding: 6px 10px;
223
+ border: 1px solid var(--border-color);
224
+ border-radius: 4px;
225
+ background: var(--paper-body-bg);
226
+ color: var(--text-color);
227
+ font: inherit;
228
+ font-size: 14px;
229
+ outline: none;
230
+ }
231
+
232
+ .link-input:focus {
233
+ border-color: var(--ui-primary);
234
+ box-shadow: 0 0 0 2px rgba(var(--ui-primary-rgbc), 0.1);
235
+ }
236
+
237
+ :global(.dark) .link-input:focus {
238
+ border-color: var(--ui-accent);
239
+ box-shadow: 0 0 0 2px rgba(var(--ui-accent-rgbc), 0.1);
240
+ }
241
+
242
+ .link-icon-button {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ width: 28px;
247
+ height: 28px;
248
+ padding: 0;
249
+ border: none;
250
+ border-radius: 4px;
251
+ background: transparent;
252
+ color: var(--text-color);
253
+ cursor: pointer;
254
+ transition:
255
+ background 0.15s ease,
256
+ color 0.15s ease;
257
+ }
258
+
259
+ .link-icon-button:hover {
260
+ background: rgba(var(--ui-primary-rgbc), 0.1);
261
+ color: var(--ui-primary);
262
+ }
263
+
264
+ :global(.dark) .link-icon-button:hover {
265
+ background: rgba(var(--ui-accent-rgbc), 0.1);
266
+ color: var(--ui-accent);
267
+ }
268
+
269
+ .link-icon-button.primary {
270
+ background: var(--ui-primary);
271
+ color: white;
272
+ }
273
+
274
+ .link-icon-button.primary:hover {
275
+ background: var(--ui-tertiary-dark);
276
+ color: white;
277
+ }
278
+
279
+ :global(.dark) .link-icon-button.primary {
280
+ background: var(--ui-accent);
281
+ color: var(--ui-primary);
282
+ }
283
+
284
+ .link-icon-button.danger:hover {
285
+ background: rgba(var(--bs-danger-rgb), 0.12);
286
+ color: var(--bs-danger);
287
+ }
288
+
289
+ .link-checkbox-label {
290
+ display: flex;
291
+ align-items: center;
292
+ gap: 6px;
293
+ font-size: 13px;
294
+ white-space: nowrap;
295
+ }
296
+
297
+ .link-checkbox {
298
+ width: 16px;
299
+ height: 16px;
300
+ cursor: pointer;
301
+ accent-color: var(--ui-primary);
302
+ }
303
+
304
+ :global(.dark) .link-checkbox {
305
+ accent-color: var(--ui-accent);
306
+ }
307
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ interface Props {
3
+ editor: Editor;
4
+ editable?: boolean;
5
+ }
6
+ declare const LinkActionMenu: import("svelte").Component<Props, {}, "">;
7
+ type LinkActionMenu = ReturnType<typeof LinkActionMenu>;
8
+ export default LinkActionMenu;