@lapis-notes/lapis-plugin-docs 0.0.0-stage → 0.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 (196) hide show
  1. package/README.md +188 -2
  2. package/dist/docs/DocsBlockMarkdownEditor.svelte +37 -0
  3. package/dist/docs/DocsBlockMarkdownEditor.svelte.d.ts +13 -0
  4. package/dist/docs/DocsBlockRefPicker.svelte +75 -0
  5. package/dist/docs/DocsBlockRefPicker.svelte.d.ts +11 -0
  6. package/dist/docs/DocsBookEditor.svelte +141 -0
  7. package/dist/docs/DocsBookEditor.svelte.d.ts +10 -0
  8. package/dist/docs/DocsBookNavigation.svelte +838 -0
  9. package/dist/docs/DocsBookNavigation.svelte.d.ts +11 -0
  10. package/dist/docs/DocsBooksColumn.svelte +82 -0
  11. package/dist/docs/DocsBooksColumn.svelte.d.ts +10 -0
  12. package/dist/docs/DocsCardBackMatter.svelte +32 -0
  13. package/dist/docs/DocsCardBackMatter.svelte.d.ts +10 -0
  14. package/dist/docs/DocsCardBlockExtra.svelte +43 -0
  15. package/dist/docs/DocsCardBlockExtra.svelte.d.ts +9 -0
  16. package/dist/docs/DocsCardPreviewFlip.svelte +143 -0
  17. package/dist/docs/DocsCardPreviewFlip.svelte.d.ts +17 -0
  18. package/dist/docs/DocsCardPreviewStack.svelte +135 -0
  19. package/dist/docs/DocsCardPreviewStack.svelte.d.ts +13 -0
  20. package/dist/docs/DocsCardPrompt.svelte +33 -0
  21. package/dist/docs/DocsCardPrompt.svelte.d.ts +8 -0
  22. package/dist/docs/DocsCardReviewAnswer.svelte +45 -0
  23. package/dist/docs/DocsCardReviewAnswer.svelte.d.ts +8 -0
  24. package/dist/docs/DocsCardTagChips.svelte +19 -0
  25. package/dist/docs/DocsCardTagChips.svelte.d.ts +7 -0
  26. package/dist/docs/DocsCardsPanel.svelte +232 -0
  27. package/dist/docs/DocsCardsPanel.svelte.d.ts +17 -0
  28. package/dist/docs/DocsCardsReview.css +1050 -0
  29. package/dist/docs/DocsChapterFooter.svelte +248 -0
  30. package/dist/docs/DocsChapterFooter.svelte.d.ts +11 -0
  31. package/dist/docs/DocsChapterScrollRing.svelte +101 -0
  32. package/dist/docs/DocsChapterScrollRing.svelte.d.ts +6 -0
  33. package/dist/docs/DocsDisplayPanel.css +627 -0
  34. package/dist/docs/DocsDisplayPanel.svelte +78 -0
  35. package/dist/docs/DocsDisplayPanel.svelte.d.ts +16 -0
  36. package/dist/docs/DocsEditorPanel.css +325 -0
  37. package/dist/docs/DocsEditorPanel.svelte +570 -0
  38. package/dist/docs/DocsEditorPanel.svelte.d.ts +22 -0
  39. package/dist/docs/DocsFlipCard.svelte +68 -0
  40. package/dist/docs/DocsFlipCard.svelte.d.ts +8 -0
  41. package/dist/docs/DocsFolderDirectoryList.svelte +206 -0
  42. package/dist/docs/DocsFolderDirectoryList.svelte.d.ts +14 -0
  43. package/dist/docs/DocsFolderPanel.css +357 -0
  44. package/dist/docs/DocsFolderPanel.svelte +102 -0
  45. package/dist/docs/DocsFolderPanel.svelte.d.ts +17 -0
  46. package/dist/docs/DocsLinksPanel.css +61 -0
  47. package/dist/docs/DocsLinksPanel.svelte +55 -0
  48. package/dist/docs/DocsLinksPanel.svelte.d.ts +11 -0
  49. package/dist/docs/DocsMarkdownField.svelte +93 -0
  50. package/dist/docs/DocsMarkdownField.svelte.d.ts +11 -0
  51. package/dist/docs/DocsMarkdownPreview.svelte +278 -0
  52. package/dist/docs/DocsMarkdownPreview.svelte.d.ts +17 -0
  53. package/dist/docs/DocsReviewPanel.svelte +63 -0
  54. package/dist/docs/DocsReviewPanel.svelte.d.ts +11 -0
  55. package/dist/docs/DocsRichBlock.svelte +291 -0
  56. package/dist/docs/DocsRichBlock.svelte.d.ts +13 -0
  57. package/dist/docs/DocsRichDisplay.svelte +316 -0
  58. package/dist/docs/DocsRichDisplay.svelte.d.ts +14 -0
  59. package/dist/docs/DocsSlidesPreview.css +243 -0
  60. package/dist/docs/DocsSlidesPreview.svelte +206 -0
  61. package/dist/docs/DocsSlidesPreview.svelte.d.ts +10 -0
  62. package/dist/docs/DocsWorkspace.css +570 -0
  63. package/dist/docs/DocsWorkspace.svelte +831 -0
  64. package/dist/docs/DocsWorkspace.svelte.d.ts +48 -0
  65. package/dist/docs/DocsWorkspaceToolbar.svelte +211 -0
  66. package/dist/docs/DocsWorkspaceToolbar.svelte.d.ts +27 -0
  67. package/dist/docs/candidates/ChoiceMenu.svelte +114 -0
  68. package/dist/docs/candidates/ChoiceMenu.svelte.d.ts +10 -0
  69. package/dist/docs/candidates/FlipCardDeck.svelte +800 -0
  70. package/dist/docs/candidates/FlipCardDeck.svelte.d.ts +36 -0
  71. package/dist/docs/candidates/MarkdownFloatingToc.svelte +313 -0
  72. package/dist/docs/candidates/MarkdownFloatingToc.svelte.d.ts +14 -0
  73. package/dist/docs/candidates/SearchableChoicePicker.css +161 -0
  74. package/dist/docs/candidates/SearchableChoicePicker.svelte +208 -0
  75. package/dist/docs/candidates/SearchableChoicePicker.svelte.d.ts +19 -0
  76. package/dist/docs/candidates/StructuredTable.svelte +98 -0
  77. package/dist/docs/candidates/StructuredTable.svelte.d.ts +8 -0
  78. package/dist/docs/candidates/StructuredTableEditor.svelte +374 -0
  79. package/dist/docs/candidates/StructuredTableEditor.svelte.d.ts +12 -0
  80. package/dist/docs/candidates/TagEditor.svelte +31 -0
  81. package/dist/docs/candidates/TagEditor.svelte.d.ts +14 -0
  82. package/dist/docs/candidates/WorkspaceHistoryControls.svelte +112 -0
  83. package/dist/docs/candidates/WorkspaceHistoryControls.svelte.d.ts +8 -0
  84. package/dist/docs/candidates/markdown-floating-toc.css +151 -0
  85. package/dist/docs/candidates/markdown-floating-toc.d.ts +1 -0
  86. package/dist/docs/candidates/markdown-floating-toc.js +1 -0
  87. package/dist/docs/docs-anki-import.d.ts +34 -0
  88. package/dist/docs/docs-anki-import.js +516 -0
  89. package/dist/docs/docs-book-view-model.d.ts +9 -0
  90. package/dist/docs/docs-book-view-model.js +71 -0
  91. package/dist/docs/docs-card-answer-markdown.d.ts +1 -0
  92. package/dist/docs/docs-card-answer-markdown.js +63 -0
  93. package/dist/docs/docs-card-display.d.ts +10 -0
  94. package/dist/docs/docs-card-display.js +18 -0
  95. package/dist/docs/docs-display-focus.d.ts +7 -0
  96. package/dist/docs/docs-display-focus.js +42 -0
  97. package/dist/docs/docs-document-surface.d.ts +7 -0
  98. package/dist/docs/docs-document-surface.js +25 -0
  99. package/dist/docs/docs-folder-directory.d.ts +21 -0
  100. package/dist/docs/docs-folder-directory.js +56 -0
  101. package/dist/docs/docs-link-mode.d.ts +16 -0
  102. package/dist/docs/docs-link-mode.js +94 -0
  103. package/dist/docs/docs-workspace-helpers.d.ts +25 -0
  104. package/dist/docs/docs-workspace-helpers.js +195 -0
  105. package/dist/docs/docs.tokens.css +13 -0
  106. package/dist/docs/import-anki-package.d.ts +10 -0
  107. package/dist/docs/import-anki-package.js +30 -0
  108. package/dist/docs/lapis-markdown-extensions.d.ts +9 -0
  109. package/dist/docs/lapis-markdown-extensions.js +128 -0
  110. package/dist/docs/pin-reading-chrome.d.ts +4 -0
  111. package/dist/docs/pin-reading-chrome.js +32 -0
  112. package/dist/docs/sidebar/DocsFoldersSidebar.svelte +134 -0
  113. package/dist/docs/sidebar/DocsFoldersSidebar.svelte.d.ts +15 -0
  114. package/dist/docs/sidebar/DocsLinksSidebar.svelte +105 -0
  115. package/dist/docs/sidebar/DocsLinksSidebar.svelte.d.ts +11 -0
  116. package/dist/docs/sidebar/DocsSidebar.css +346 -0
  117. package/dist/docs/sidebar/DocsSidebar.svelte +430 -0
  118. package/dist/docs/sidebar/DocsSidebar.svelte.d.ts +18 -0
  119. package/dist/docs/sidebar/DocsSidebarHost.svelte +139 -0
  120. package/dist/docs/sidebar/DocsSidebarHost.svelte.d.ts +12 -0
  121. package/dist/docs/sidebar/DocsTagsSidebar.svelte +176 -0
  122. package/dist/docs/sidebar/DocsTagsSidebar.svelte.d.ts +12 -0
  123. package/dist/docs/sidebar/docs-book-note-tree.d.ts +23 -0
  124. package/dist/docs/sidebar/docs-book-note-tree.js +88 -0
  125. package/dist/docs/sidebar/docs-note-sort.d.ts +26 -0
  126. package/dist/docs/sidebar/docs-note-sort.js +54 -0
  127. package/dist/docs/sidebar/docs-sidebar-filters.d.ts +17 -0
  128. package/dist/docs/sidebar/docs-sidebar-filters.js +74 -0
  129. package/dist/docs/sidebar/docs-sidebar-ui-storage.d.ts +24 -0
  130. package/dist/docs/sidebar/docs-sidebar-ui-storage.js +87 -0
  131. package/dist/docs/sidebar/docs-source-folder-tree.d.ts +16 -0
  132. package/dist/docs/sidebar/docs-source-folder-tree.js +143 -0
  133. package/dist/docs/sidebar/docs-tag-tree.d.ts +17 -0
  134. package/dist/docs/sidebar/docs-tag-tree.js +84 -0
  135. package/dist/docs-cards-view.d.ts +18 -0
  136. package/dist/docs-cards-view.js +52 -0
  137. package/dist/docs-document-view.d.ts +24 -0
  138. package/dist/docs-document-view.js +127 -0
  139. package/dist/docs-document-workspace.svelte +215 -0
  140. package/dist/docs-document-workspace.svelte.d.ts +16 -0
  141. package/dist/docs-index.d.ts +5 -0
  142. package/dist/docs-index.js +49 -0
  143. package/dist/docs-path.d.ts +6 -0
  144. package/dist/docs-path.js +10 -0
  145. package/dist/docs-plugin.d.ts +21 -0
  146. package/dist/docs-plugin.js +140 -0
  147. package/dist/docs-repository.d.ts +15 -0
  148. package/dist/docs-repository.js +75 -0
  149. package/dist/docs-review-view.d.ts +18 -0
  150. package/dist/docs-review-view.js +52 -0
  151. package/dist/docs-settings-tab.d.ts +8 -0
  152. package/dist/docs-settings-tab.js +25 -0
  153. package/dist/docs-settings.d.ts +17 -0
  154. package/dist/docs-settings.js +38 -0
  155. package/dist/docs-view.d.ts +22 -0
  156. package/dist/docs-view.js +51 -0
  157. package/dist/docs-workspace.svelte +394 -0
  158. package/dist/docs-workspace.svelte.d.ts +15 -0
  159. package/dist/index.d.ts +12 -0
  160. package/dist/index.js +12 -0
  161. package/dist/model/book.d.ts +6 -0
  162. package/dist/model/book.js +101 -0
  163. package/dist/model/coding-runtime.d.ts +4 -0
  164. package/dist/model/coding-runtime.js +9 -0
  165. package/dist/model/docs-card-context.d.ts +32 -0
  166. package/dist/model/docs-card-context.js +89 -0
  167. package/dist/model/docs-card-preview-stack-motion.d.ts +51 -0
  168. package/dist/model/docs-card-preview-stack-motion.js +94 -0
  169. package/dist/model/docs-media-context.d.ts +9 -0
  170. package/dist/model/docs-media-context.js +18 -0
  171. package/dist/model/docs-note-list.d.ts +8 -0
  172. package/dist/model/docs-note-list.js +25 -0
  173. package/dist/model/docs-relative-media.d.ts +30 -0
  174. package/dist/model/docs-relative-media.js +116 -0
  175. package/dist/model/docs-review-constants.d.ts +4 -0
  176. package/dist/model/docs-review-constants.js +13 -0
  177. package/dist/model/fsrs.d.ts +14 -0
  178. package/dist/model/fsrs.js +153 -0
  179. package/dist/model/graph.d.ts +12 -0
  180. package/dist/model/graph.js +169 -0
  181. package/dist/model/index.d.ts +14 -0
  182. package/dist/model/index.js +14 -0
  183. package/dist/model/markdown-normalize.d.ts +11 -0
  184. package/dist/model/markdown-normalize.js +755 -0
  185. package/dist/model/markdown.d.ts +167 -0
  186. package/dist/model/markdown.js +365 -0
  187. package/dist/model/sample.d.ts +7 -0
  188. package/dist/model/sample.js +524 -0
  189. package/dist/model/types.d.ts +328 -0
  190. package/dist/model/types.js +1 -0
  191. package/dist/model/workspace.d.ts +6 -0
  192. package/dist/model/workspace.js +81 -0
  193. package/dist/storybook.css +52 -0
  194. package/dist/styles.css +1 -0
  195. package/manifest.json +11 -0
  196. package/package.json +147 -4
@@ -0,0 +1,570 @@
1
+ <script lang="ts">
2
+ import './DocsEditorPanel.css';
3
+ import ChipAutocomplete from "@lapismd/design-core/forms/ChipAutocomplete.svelte";
4
+ import TagEditor from "./candidates/TagEditor.svelte";
5
+ import {
6
+ AddSectionChooser,
7
+ CodeEditor,
8
+ CODE_LANGUAGE_OPTIONS,
9
+ EntryActions,
10
+ FormField,
11
+ FormSectionHeader,
12
+ normalizeCodeLanguage,
13
+ type SearchableChoiceOption,
14
+ autosizeTextarea
15
+ } from "@lapismd/design-core/forms";
16
+ import ChoiceMenu from "./candidates/ChoiceMenu.svelte";
17
+ import SearchableChoicePicker from "./candidates/SearchableChoicePicker.svelte";
18
+ import StructuredTableEditor from "./candidates/StructuredTableEditor.svelte";
19
+ import PlusIcon from "@lucide/svelte/icons/plus";
20
+ import DocsBookEditor from "./DocsBookEditor.svelte";
21
+ import DocsBlockMarkdownEditor from "./DocsBlockMarkdownEditor.svelte";
22
+ import DocsMarkdownField from "./DocsMarkdownField.svelte";
23
+ import type {
24
+ DocsCalloutTone,
25
+ DocsDocType,
26
+ DocsNote,
27
+ DocsResourceItem,
28
+ DocsRevisionBlock
29
+ } from '../model';
30
+ import {
31
+ BLOCK_TYPE_LABELS,
32
+ BLOCK_TYPE_OPTIONS,
33
+ CALLOUT_TONE_LABELS,
34
+ CALLOUT_TONE_OPTIONS,
35
+ DOC_TYPE_LABELS,
36
+ DOC_TYPE_OPTIONS,
37
+ docsBlockDisplayTitle,
38
+ linesToText,
39
+ textToLines
40
+ } from "./docs-workspace-helpers";
41
+
42
+ let {
43
+ draftNote,
44
+ propertiesOpen = $bindable(false),
45
+ blockOpenById = $bindable({}),
46
+ tagSuggestions,
47
+ aliasSuggestions,
48
+ bookChapterOptions = [],
49
+ setDraftField,
50
+ patchBlock,
51
+ updateBlockTitle,
52
+ updateBlockId,
53
+ moveBlock,
54
+ removeBlock,
55
+ addBlock,
56
+ onUploadAttachment
57
+ }: {
58
+ draftNote: DocsNote;
59
+ propertiesOpen: boolean;
60
+ blockOpenById: Record<string, boolean>;
61
+ tagSuggestions: string[];
62
+ aliasSuggestions: string[];
63
+ bookChapterOptions?: SearchableChoiceOption[];
64
+ setDraftField: <K extends keyof DocsNote>(key: K, value: DocsNote[K]) => void;
65
+ patchBlock: (blockId: string, patch: Record<string, unknown>) => void;
66
+ updateBlockTitle: (blockId: string, title: string) => void;
67
+ updateBlockId: (blockId: string, rawValue: string) => void;
68
+ moveBlock: (blockId: string, direction: -1 | 1) => void;
69
+ removeBlock: (blockId: string) => void;
70
+ addBlock: (type?: DocsRevisionBlock["type"], title?: string) => void;
71
+ onUploadAttachment?: (noteId: string, file: File) => Promise<string | null>;
72
+ } = $props();
73
+
74
+ const blockTypeOptions = BLOCK_TYPE_OPTIONS.map((type) => ({
75
+ value: type,
76
+ label: BLOCK_TYPE_LABELS[type]
77
+ }));
78
+
79
+ let pendingBlockTitle = $state<string | null>(null);
80
+
81
+ function blockDisplayTitle(block: DocsRevisionBlock) {
82
+ return docsBlockDisplayTitle(block);
83
+ }
84
+
85
+ function showPendingBlock() {
86
+ pendingBlockTitle ??= "New Section";
87
+ }
88
+
89
+ function cancelPendingBlock() {
90
+ pendingBlockTitle = null;
91
+ }
92
+
93
+ function commitPendingBlock(type: string) {
94
+ const title = pendingBlockTitle?.trim() || "New Section";
95
+ addBlock(type as DocsRevisionBlock["type"], title);
96
+ pendingBlockTitle = null;
97
+ }
98
+
99
+ function emptyResourceItem(): DocsResourceItem {
100
+ return { title: "", url: "", kind: "", notes: "" };
101
+ }
102
+
103
+ function patchResourceItem(
104
+ blockId: string,
105
+ items: DocsResourceItem[],
106
+ itemIndex: number,
107
+ patch: Partial<DocsResourceItem>
108
+ ) {
109
+ patchBlock(blockId, {
110
+ items: items.map((item, index) => (index === itemIndex ? { ...item, ...patch } : item))
111
+ });
112
+ }
113
+
114
+ function addResourceItem(blockId: string, items: DocsResourceItem[]) {
115
+ patchBlock(blockId, { items: [...items, emptyResourceItem()] });
116
+ }
117
+
118
+ function removeResourceItem(blockId: string, items: DocsResourceItem[], itemIndex: number) {
119
+ patchBlock(blockId, { items: items.filter((_, index) => index !== itemIndex) });
120
+ }
121
+
122
+ function moveResourceItem(
123
+ blockId: string,
124
+ items: DocsResourceItem[],
125
+ itemIndex: number,
126
+ direction: -1 | 1
127
+ ) {
128
+ const targetIndex = itemIndex + direction;
129
+ if (targetIndex < 0 || targetIndex >= items.length) return;
130
+ const nextItems = [...items];
131
+ const [item] = nextItems.splice(itemIndex, 1);
132
+ nextItems.splice(targetIndex, 0, item);
133
+ patchBlock(blockId, { items: nextItems });
134
+ }
135
+ </script>
136
+
137
+ <section class="docs-editor">
138
+ <div class="docs-form">
139
+ <section class="docs-properties" aria-label="Note properties">
140
+ <div class="docs-properties-header">
141
+ <FormSectionHeader
142
+ title={draftNote.title}
143
+ index={0}
144
+ total={1}
145
+ open={propertiesOpen}
146
+ editable={propertiesOpen}
147
+ titleToggleable={!propertiesOpen}
148
+ movable={false}
149
+ removable={false}
150
+ titleLabel="Note title"
151
+ titlePlaceholder="Untitled note"
152
+ titleClass="docs-properties-title"
153
+ onTitleChange={(title) => setDraftField("title", title)}
154
+ onToggle={() => (propertiesOpen = !propertiesOpen)}
155
+ />
156
+ </div>
157
+ {#if propertiesOpen}
158
+ <div class="docs-properties-fields">
159
+ <FormField label="Type" align="center" as="div">
160
+ <ChoiceMenu
161
+ value={draftNote.docType}
162
+ options={DOC_TYPE_OPTIONS}
163
+ labels={DOC_TYPE_LABELS}
164
+ ariaLabel="Document type"
165
+ onChange={(value) =>
166
+ setDraftField("docType", value as DocsDocType)}
167
+ />
168
+ </FormField>
169
+ <DocsMarkdownField
170
+ label="Summary"
171
+ value={draftNote.summary}
172
+ placeholder="Add Summary"
173
+ onChange={(value) => setDraftField("summary", value)}
174
+ />
175
+ <DocsMarkdownField
176
+ label="Learning goal"
177
+ value={draftNote.learningGoal}
178
+ placeholder="Add Learning Goal"
179
+ onChange={(value) => setDraftField("learningGoal", value)}
180
+ />
181
+ <FormField label="Aliases" as="div">
182
+ <ChipAutocomplete
183
+ id={`docs-note-aliases-${draftNote.id}`}
184
+ showLabel={false}
185
+ embedded
186
+ uppercase={false}
187
+ value={draftNote.aliases}
188
+ suggestions={aliasSuggestions}
189
+ label="Aliases"
190
+ placeholder="Add alias..."
191
+ onChange={(aliases) =>
192
+ setDraftField("aliases", aliases)}
193
+ />
194
+ </FormField>
195
+ <FormField label="Tags" as="div">
196
+ <TagEditor
197
+ id={`docs-note-tags-${draftNote.id}`}
198
+ showLabel={false}
199
+ embedded
200
+ value={draftNote.tags}
201
+ suggestions={tagSuggestions}
202
+ placeholder="Add tag..."
203
+ onChange={(tags) => setDraftField("tags", tags)}
204
+ />
205
+ </FormField>
206
+ {#if draftNote.docType === "index" || draftNote.book}
207
+ <FormField label="Book TOC" as="div">
208
+ <DocsBookEditor
209
+ book={draftNote.book ?? { chapters: [] }}
210
+ noteOptions={bookChapterOptions}
211
+ onChange={(book) => setDraftField("book", book)}
212
+ />
213
+ </FormField>
214
+ {/if}
215
+ </div>
216
+ {/if}
217
+ </section>
218
+ <section class="docs-blocks" aria-label="Structured revision blocks">
219
+ {#each draftNote.blocks as block, index (block.id)}
220
+ <article class="docs-block-editor">
221
+ <FormSectionHeader
222
+ title={blockDisplayTitle(block)}
223
+ titleLabel="Block title"
224
+ titlePlaceholder={BLOCK_TYPE_LABELS[block.type]}
225
+ {index}
226
+ total={draftNote.blocks.length}
227
+ open={blockOpenById[block.id] !== false}
228
+ editable={blockOpenById[block.id] !== false}
229
+ titleToggleable={blockOpenById[block.id] === false}
230
+ removeLabel={`Remove block ${block.id}`}
231
+ onTitleChange={(title) => updateBlockTitle(block.id, title)}
232
+ onToggle={() =>
233
+ (blockOpenById = {
234
+ ...blockOpenById,
235
+ [block.id]: blockOpenById[block.id] === false
236
+ })}
237
+ onMove={(direction) => moveBlock(block.id, direction)}
238
+ onRemove={() => removeBlock(block.id)}
239
+ >
240
+ {#snippet actions()}
241
+ <span class="docs-block-type-badge">{BLOCK_TYPE_LABELS[block.type]}</span>
242
+ {/snippet}
243
+ </FormSectionHeader>
244
+ {#if blockOpenById[block.id] !== false}
245
+ <div class="docs-block-fields">
246
+ <FormField label="Id">
247
+ <input
248
+ value={block.id}
249
+ spellcheck={false}
250
+ oninput={(event) => updateBlockId(block.id, event.currentTarget.value)}
251
+ />
252
+ </FormField>
253
+ {#if block.type === "text"}
254
+ <DocsMarkdownField
255
+ label="Body"
256
+ value={block.body}
257
+ placeholder="Add Body"
258
+ onChange={(value) => patchBlock(block.id, { body: value })}
259
+ />
260
+ {:else if block.type === "markdown"}
261
+ <FormField label="Body">
262
+ <DocsBlockMarkdownEditor
263
+ bind:value={
264
+ () => block.body,
265
+ (next) => patchBlock(block.id, { body: next })
266
+ }
267
+ ariaLabel={`${docsBlockDisplayTitle(block)} markdown`}
268
+ placeholder="Write section Markdown..."
269
+ minRows={8}
270
+ attachmentUpload={
271
+ onUploadAttachment
272
+ ? (file: File) => onUploadAttachment(draftNote.id, file)
273
+ : undefined
274
+ }
275
+ sourcePath={draftNote.sourcePath}
276
+ />
277
+ </FormField>
278
+ {:else if block.type === "concept"}
279
+ <DocsMarkdownField
280
+ label="Summary"
281
+ value={block.summary}
282
+ placeholder="Add Summary"
283
+ onChange={(value) => patchBlock(block.id, { summary: value })}
284
+ />
285
+ <FormField label="Key points">
286
+ <textarea
287
+ rows="1"
288
+ value={linesToText(block.keyPoints)}
289
+ placeholder="Add each key point on a new line"
290
+ use:autosizeTextarea={linesToText(block.keyPoints)}
291
+ oninput={(event) =>
292
+ patchBlock(block.id, { keyPoints: textToLines(event.currentTarget.value) })}
293
+ ></textarea>
294
+ </FormField>
295
+ <DocsMarkdownField
296
+ label="Details"
297
+ value={block.details}
298
+ placeholder="Add Details"
299
+ onChange={(value) => patchBlock(block.id, { details: value })}
300
+ />
301
+ {:else if block.type === "procedure"}
302
+ <FormField label="Steps">
303
+ <textarea
304
+ rows="1"
305
+ value={linesToText(block.steps)}
306
+ placeholder="Add each step on a new line"
307
+ use:autosizeTextarea={linesToText(block.steps)}
308
+ oninput={(event) =>
309
+ patchBlock(block.id, { steps: textToLines(event.currentTarget.value) })}
310
+ ></textarea>
311
+ </FormField>
312
+ <DocsMarkdownField
313
+ label="Notes"
314
+ value={block.notes}
315
+ placeholder="Add Notes"
316
+ onChange={(value) => patchBlock(block.id, { notes: value })}
317
+ />
318
+ {:else if block.type === "prompt"}
319
+ <DocsMarkdownField
320
+ label="Problem"
321
+ value={block.problem}
322
+ placeholder="Add Problem"
323
+ onChange={(value) => patchBlock(block.id, { problem: value })}
324
+ />
325
+ <FormField label="Requirements">
326
+ <textarea
327
+ rows="1"
328
+ value={linesToText(block.requirements)}
329
+ placeholder="Add each requirement on a new line"
330
+ use:autosizeTextarea={linesToText(block.requirements)}
331
+ oninput={(event) =>
332
+ patchBlock(block.id, { requirements: textToLines(event.currentTarget.value) })}
333
+ ></textarea>
334
+ </FormField>
335
+ <DocsMarkdownField
336
+ label="Approach"
337
+ value={block.approach}
338
+ placeholder="Add Approach"
339
+ onChange={(value) => patchBlock(block.id, { approach: value })}
340
+ />
341
+ <FormField label="Traps">
342
+ <textarea
343
+ rows="1"
344
+ value={linesToText(block.traps)}
345
+ placeholder="Add each trap on a new line"
346
+ use:autosizeTextarea={linesToText(block.traps)}
347
+ oninput={(event) =>
348
+ patchBlock(block.id, { traps: textToLines(event.currentTarget.value) })}
349
+ ></textarea>
350
+ </FormField>
351
+ <FormField label="Tests">
352
+ <textarea
353
+ rows="1"
354
+ value={linesToText(block.tests)}
355
+ placeholder="Add each test on a new line"
356
+ use:autosizeTextarea={linesToText(block.tests)}
357
+ oninput={(event) =>
358
+ patchBlock(block.id, { tests: textToLines(event.currentTarget.value) })}
359
+ ></textarea>
360
+ </FormField>
361
+ <DocsMarkdownField
362
+ label="Answer"
363
+ value={block.answer}
364
+ placeholder="Add Answer"
365
+ onChange={(value) => patchBlock(block.id, { answer: value })}
366
+ />
367
+ {:else if block.type === "code"}
368
+ <FormField label="Language" align="center" as="div">
369
+ <SearchableChoicePicker
370
+ value={block.language}
371
+ options={CODE_LANGUAGE_OPTIONS}
372
+ ariaLabel={`${blockDisplayTitle(block)} language`}
373
+ placeholder="Select language"
374
+ searchPlaceholder="Search languages..."
375
+ normalize={normalizeCodeLanguage}
376
+ onChange={(language) => patchBlock(block.id, { language })}
377
+ />
378
+ </FormField>
379
+ <FormField label="Caption">
380
+ <textarea
381
+ rows="1"
382
+ value={block.caption}
383
+ placeholder="Add Caption"
384
+ use:autosizeTextarea={block.caption}
385
+ oninput={(event) => patchBlock(block.id, { caption: event.currentTarget.value })}
386
+ ></textarea>
387
+ </FormField>
388
+ <FormField label="Code" as="div">
389
+ <CodeEditor
390
+ value={block.code}
391
+ language={block.language}
392
+ ariaLabel={`${blockDisplayTitle(block)} code`}
393
+ onChange={(code) => patchBlock(block.id, { code })}
394
+ />
395
+ </FormField>
396
+ {:else if block.type === "table"}
397
+ <FormField label="Table" as="div">
398
+ <StructuredTableEditor
399
+ columns={block.columns}
400
+ rows={block.rows}
401
+ onChange={(table) => patchBlock(block.id, table)}
402
+ />
403
+ </FormField>
404
+ {:else if block.type === "callout"}
405
+ <FormField label="Tone" align="center" as="div">
406
+ <ChoiceMenu
407
+ value={block.tone}
408
+ options={CALLOUT_TONE_OPTIONS}
409
+ labels={CALLOUT_TONE_LABELS}
410
+ ariaLabel="Callout tone"
411
+ onChange={(value) => patchBlock(block.id, { tone: value as DocsCalloutTone })}
412
+ />
413
+ </FormField>
414
+ <DocsMarkdownField
415
+ label="Body"
416
+ value={block.body}
417
+ placeholder="Add Body"
418
+ onChange={(value) => patchBlock(block.id, { body: value })}
419
+ />
420
+ {:else if block.type === "story"}
421
+ <FormField label="Use for">
422
+ <textarea
423
+ rows="1"
424
+ value={linesToText(block.useFor)}
425
+ placeholder="Add each use on a new line"
426
+ use:autosizeTextarea={linesToText(block.useFor)}
427
+ oninput={(event) =>
428
+ patchBlock(block.id, { useFor: textToLines(event.currentTarget.value) })}
429
+ ></textarea>
430
+ </FormField>
431
+ <DocsMarkdownField
432
+ label="Situation"
433
+ value={block.situation}
434
+ placeholder="Add Situation"
435
+ onChange={(value) => patchBlock(block.id, { situation: value })}
436
+ />
437
+ <DocsMarkdownField
438
+ label="Task"
439
+ value={block.task}
440
+ placeholder="Add Task"
441
+ onChange={(value) => patchBlock(block.id, { task: value })}
442
+ />
443
+ <DocsMarkdownField
444
+ label="Action"
445
+ value={block.action}
446
+ placeholder="Add Action"
447
+ onChange={(value) => patchBlock(block.id, { action: value })}
448
+ />
449
+ <DocsMarkdownField
450
+ label="Result"
451
+ value={block.result}
452
+ placeholder="Add Result"
453
+ onChange={(value) => patchBlock(block.id, { result: value })}
454
+ />
455
+ <FormField label="Metrics">
456
+ <textarea
457
+ rows="1"
458
+ value={linesToText(block.metrics)}
459
+ placeholder="Add each metric on a new line"
460
+ use:autosizeTextarea={linesToText(block.metrics)}
461
+ oninput={(event) =>
462
+ patchBlock(block.id, { metrics: textToLines(event.currentTarget.value) })}
463
+ ></textarea>
464
+ </FormField>
465
+ {:else if block.type === "resources"}
466
+ <FormField label="Items" as="div">
467
+ <div class="docs-resource-items">
468
+ {#each block.items as item, itemIndex (itemIndex)}
469
+ <EntryActions
470
+ index={itemIndex}
471
+ total={block.items.length}
472
+ removeLabel={`Remove resource ${item.title || item.url || itemIndex + 1}`}
473
+ onMove={(direction) =>
474
+ moveResourceItem(block.id, block.items, itemIndex, direction)}
475
+ onRemove={() => removeResourceItem(block.id, block.items, itemIndex)}
476
+ >
477
+ <div class="docs-resource-item-fields">
478
+ <FormField label="Title">
479
+ <input
480
+ value={item.title}
481
+ placeholder="Add title"
482
+ oninput={(event) =>
483
+ patchResourceItem(block.id, block.items, itemIndex, {
484
+ title: event.currentTarget.value
485
+ })}
486
+ />
487
+ </FormField>
488
+ <FormField label="URL">
489
+ <input
490
+ value={item.url}
491
+ placeholder="Add URL"
492
+ spellcheck={false}
493
+ oninput={(event) =>
494
+ patchResourceItem(block.id, block.items, itemIndex, {
495
+ url: event.currentTarget.value
496
+ })}
497
+ />
498
+ </FormField>
499
+ <FormField label="Kind">
500
+ <input
501
+ value={item.kind}
502
+ placeholder="Add kind"
503
+ oninput={(event) =>
504
+ patchResourceItem(block.id, block.items, itemIndex, {
505
+ kind: event.currentTarget.value
506
+ })}
507
+ />
508
+ </FormField>
509
+ <div class="docs-resource-item-notes">
510
+ <FormField label="Notes">
511
+ <textarea
512
+ rows="1"
513
+ value={item.notes}
514
+ placeholder="Add notes"
515
+ use:autosizeTextarea={item.notes}
516
+ oninput={(event) =>
517
+ patchResourceItem(block.id, block.items, itemIndex, {
518
+ notes: event.currentTarget.value
519
+ })}
520
+ ></textarea>
521
+ </FormField>
522
+ </div>
523
+ </div>
524
+ </EntryActions>
525
+ {/each}
526
+ <button
527
+ type="button"
528
+ class="docs-resource-add"
529
+ onclick={() => addResourceItem(block.id, block.items)}
530
+ >
531
+ <PlusIcon aria-hidden="true" />
532
+ <span>Add resource</span>
533
+ </button>
534
+ </div>
535
+ </FormField>
536
+ {/if}
537
+ </div>
538
+ {/if}
539
+ </article>
540
+ {/each}
541
+ <AddSectionChooser
542
+ open={pendingBlockTitle !== null}
543
+ title={pendingBlockTitle ?? ""}
544
+ options={blockTypeOptions}
545
+ addLabel="Add section"
546
+ optionLabel="Block Type"
547
+ onOpen={showPendingBlock}
548
+ onCancel={cancelPendingBlock}
549
+ onTitleChange={(value) => (pendingBlockTitle = value)}
550
+ onChoose={commitPendingBlock}
551
+ />
552
+ </section>
553
+ <div class="docs-markdown-editor">
554
+ <DocsBlockMarkdownEditor
555
+ bind:value={draftNote.body}
556
+ ariaLabel="Docs note markdown"
557
+ minRows={14}
558
+ compact={false}
559
+ floatingToc="auto"
560
+ placeholder="Write revision notes with Markdown..."
561
+ sourcePath={draftNote.sourcePath}
562
+ attachmentUpload={
563
+ onUploadAttachment
564
+ ? (file: File) => onUploadAttachment(draftNote.id, file)
565
+ : undefined
566
+ }
567
+ />
568
+ </div>
569
+ </div>
570
+ </section>
@@ -0,0 +1,22 @@
1
+ import './DocsEditorPanel.css';
2
+ import { type SearchableChoiceOption } from "@lapismd/design-core/forms";
3
+ import type { DocsNote, DocsRevisionBlock } from '../model';
4
+ type $$ComponentProps = {
5
+ draftNote: DocsNote;
6
+ propertiesOpen: boolean;
7
+ blockOpenById: Record<string, boolean>;
8
+ tagSuggestions: string[];
9
+ aliasSuggestions: string[];
10
+ bookChapterOptions?: SearchableChoiceOption[];
11
+ setDraftField: <K extends keyof DocsNote>(key: K, value: DocsNote[K]) => void;
12
+ patchBlock: (blockId: string, patch: Record<string, unknown>) => void;
13
+ updateBlockTitle: (blockId: string, title: string) => void;
14
+ updateBlockId: (blockId: string, rawValue: string) => void;
15
+ moveBlock: (blockId: string, direction: -1 | 1) => void;
16
+ removeBlock: (blockId: string) => void;
17
+ addBlock: (type?: DocsRevisionBlock["type"], title?: string) => void;
18
+ onUploadAttachment?: (noteId: string, file: File) => Promise<string | null>;
19
+ };
20
+ declare const DocsEditorPanel: import("svelte").Component<$$ComponentProps, {}, "propertiesOpen" | "blockOpenById">;
21
+ type DocsEditorPanel = ReturnType<typeof DocsEditorPanel>;
22
+ export default DocsEditorPanel;
@@ -0,0 +1,68 @@
1
+ <script lang="ts">
2
+ /**
3
+ * DESIGN_CORE_CANDIDATE: FlipCardDeck is listed under design-core Shared — later.
4
+ * Keep this plugin-local until a public design-core export exists.
5
+ */
6
+ let {
7
+ front,
8
+ back,
9
+ flipped = $bindable(false),
10
+ }: {
11
+ front: string;
12
+ back: string;
13
+ flipped?: boolean;
14
+ } = $props();
15
+ </script>
16
+
17
+ <button
18
+ type="button"
19
+ class="docs-flip-card"
20
+ data-ui-component="docs-flip-card"
21
+ data-flipped={flipped ? "true" : "false"}
22
+ aria-pressed={flipped}
23
+ onclick={() => (flipped = !flipped)}
24
+ >
25
+ <span class="docs-flip-card__face docs-flip-card__face--front">{front}</span>
26
+ <span class="docs-flip-card__face docs-flip-card__face--back">{back}</span>
27
+ </button>
28
+
29
+ <style>
30
+ .docs-flip-card {
31
+ position: relative;
32
+ display: grid;
33
+ width: 100%;
34
+ min-height: 12rem;
35
+ padding: 0;
36
+ border: 1px solid var(--ui-docs-border);
37
+ border-radius: var(--ui-docs-radius);
38
+ background: transparent;
39
+ color: var(--ui-docs-foreground);
40
+ cursor: pointer;
41
+ perspective: 1000px;
42
+ }
43
+
44
+ .docs-flip-card__face {
45
+ grid-area: 1 / 1;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ padding: 1.25rem;
50
+ border-radius: inherit;
51
+ background: var(--ui-docs-card);
52
+ text-align: center;
53
+ backface-visibility: hidden;
54
+ transition: transform 0.35s ease;
55
+ }
56
+
57
+ .docs-flip-card__face--back {
58
+ transform: rotateY(180deg);
59
+ }
60
+
61
+ .docs-flip-card[data-flipped="true"] .docs-flip-card__face--front {
62
+ transform: rotateY(180deg);
63
+ }
64
+
65
+ .docs-flip-card[data-flipped="true"] .docs-flip-card__face--back {
66
+ transform: rotateY(0deg);
67
+ }
68
+ </style>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ front: string;
3
+ back: string;
4
+ flipped?: boolean;
5
+ };
6
+ declare const DocsFlipCard: import("svelte").Component<$$ComponentProps, {}, "flipped">;
7
+ type DocsFlipCard = ReturnType<typeof DocsFlipCard>;
8
+ export default DocsFlipCard;