@vespercms/admin 0.0.0-stage → 0.1.0-next.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 (207) hide show
  1. package/README.md +119 -2
  2. package/dist/BodyEditor-_mkh0BSQ.js +3047 -0
  3. package/dist/a11y/FieldNotes.d.ts +23 -0
  4. package/dist/a11y/NotesDrawer.d.ts +23 -0
  5. package/dist/a11y/pdf.d.ts +3 -0
  6. package/dist/a11y/run.d.ts +3 -0
  7. package/dist/a11y/useNotes.d.ts +28 -0
  8. package/dist/app/App.d.ts +2 -0
  9. package/dist/app/Screen.d.ts +88 -0
  10. package/dist/app/UserMenu.d.ts +7 -0
  11. package/dist/app/api.d.ts +75 -0
  12. package/dist/app/appearance.d.ts +10 -0
  13. package/dist/app/browser-api.d.ts +17 -0
  14. package/dist/app/collection-icon.d.ts +12 -0
  15. package/dist/app/context.d.ts +23 -0
  16. package/dist/app/entries-changed.d.ts +11 -0
  17. package/dist/app/file-url.d.ts +13 -0
  18. package/dist/app/format.d.ts +4 -0
  19. package/dist/app/idb.d.ts +7 -0
  20. package/dist/app/load.d.ts +16 -0
  21. package/dist/app/media.d.ts +5 -0
  22. package/dist/app/navigation.d.ts +4 -0
  23. package/dist/app/pane-context.d.ts +19 -0
  24. package/dist/app/panes.d.ts +14 -0
  25. package/dist/app/router.d.ts +62 -0
  26. package/dist/body/BodyEditor.d.ts +42 -0
  27. package/dist/body/TableView.d.ts +2 -0
  28. package/dist/body/catalog.d.ts +47 -0
  29. package/dist/body/checks.d.ts +15 -0
  30. package/dist/body/chips.d.ts +31 -0
  31. package/dist/body/commands.d.ts +31 -0
  32. package/dist/body/document.d.ts +42 -0
  33. package/dist/body/insert.d.ts +69 -0
  34. package/dist/body/nodes.d.ts +38 -0
  35. package/dist/body/reveal.d.ts +13 -0
  36. package/dist/body/scroll.d.ts +16 -0
  37. package/dist/body/settings.d.ts +19 -0
  38. package/dist/body/shortcuts.d.ts +13 -0
  39. package/dist/body/table.d.ts +31 -0
  40. package/dist/body/ui.d.ts +30 -0
  41. package/dist/body/views.d.ts +9 -0
  42. package/dist/checks-C-0xDu-I.js +57 -0
  43. package/dist/cloud/CloudGate.d.ts +14 -0
  44. package/dist/cloud/access.d.ts +14 -0
  45. package/dist/cloud/changes.d.ts +26 -0
  46. package/dist/cloud/connection.d.ts +100 -0
  47. package/dist/cloud/editor.d.ts +44 -0
  48. package/dist/cloud/session.d.ts +62 -0
  49. package/dist/cloud/storage.d.ts +45 -0
  50. package/dist/editor/Field.d.ts +24 -0
  51. package/dist/editor/PagePicker.d.ts +20 -0
  52. package/dist/editor/arrange.d.ts +13 -0
  53. package/dist/editor/context.d.ts +23 -0
  54. package/dist/editor/copy.d.ts +9 -0
  55. package/dist/editor/reveal.d.ts +7 -0
  56. package/dist/editor/target.d.ts +21 -0
  57. package/dist/editor/values.d.ts +18 -0
  58. package/dist/geo-map-CT3OGD7O.js +110 -0
  59. package/dist/github/GitHubGate.d.ts +15 -0
  60. package/dist/github/api.d.ts +20 -0
  61. package/dist/github/cache.d.ts +2 -0
  62. package/dist/github/session.d.ts +35 -0
  63. package/dist/index.d.ts +3 -0
  64. package/dist/markdown-4fVeej3c.js +211 -0
  65. package/dist/media/MediaPicker.d.ts +28 -0
  66. package/dist/media/file-icon.d.ts +3 -0
  67. package/dist/media/formats.d.ts +23 -0
  68. package/dist/media/hygiene.d.ts +37 -0
  69. package/dist/media/upload.d.ts +31 -0
  70. package/dist/mount.d.ts +3 -0
  71. package/dist/pdf-m5s_uXJ3.js +24 -0
  72. package/dist/run-y4XRkg17.js +7 -0
  73. package/dist/schedule/ScheduleDialog.d.ts +28 -0
  74. package/dist/schedule/ScheduleSummary.d.ts +12 -0
  75. package/dist/schedule/ScheduledView.d.ts +8 -0
  76. package/dist/schedule/cloud.d.ts +21 -0
  77. package/dist/schedule/now.d.ts +5 -0
  78. package/dist/schedule/times.d.ts +35 -0
  79. package/dist/screens/EntryEditor.d.ts +13 -0
  80. package/dist/screens/EntryList.d.ts +16 -0
  81. package/dist/screens/Home.d.ts +6 -0
  82. package/dist/screens/ListPane.d.ts +6 -0
  83. package/dist/screens/MediaBrowser.d.ts +7 -0
  84. package/dist/screens/NotFound.d.ts +3 -0
  85. package/dist/screens/PageTree.d.ts +10 -0
  86. package/dist/screens/Redirects.d.ts +1 -0
  87. package/dist/screens/Unavailable.d.ts +8 -0
  88. package/dist/screens/status.d.ts +36 -0
  89. package/dist/shortcuts-CarKeDqp.js +4666 -0
  90. package/dist/studio.css +3 -0
  91. package/dist/studio.js +6449 -0
  92. package/dist/types.d.ts +76 -0
  93. package/dist/widgets/basic.d.ts +25 -0
  94. package/dist/widgets/blank-date.d.ts +8 -0
  95. package/dist/widgets/crops.d.ts +43 -0
  96. package/dist/widgets/focal-point.d.ts +15 -0
  97. package/dist/widgets/geo-map.d.ts +10 -0
  98. package/dist/widgets/geo.d.ts +16 -0
  99. package/dist/widgets/group.d.ts +41 -0
  100. package/dist/widgets/icon.d.ts +9 -0
  101. package/dist/widgets/image.d.ts +16 -0
  102. package/dist/widgets/markdown-lazy.d.ts +2 -0
  103. package/dist/widgets/markdown.d.ts +2 -0
  104. package/dist/widgets/reference.d.ts +12 -0
  105. package/dist/widgets/registry.d.ts +32 -0
  106. package/dist/widgets/special.d.ts +20 -0
  107. package/dist/widgets/union.d.ts +36 -0
  108. package/package.json +63 -4
  109. package/src/a11y/FieldNotes.tsx +56 -0
  110. package/src/a11y/NotesDrawer.tsx +81 -0
  111. package/src/a11y/pdf.ts +45 -0
  112. package/src/a11y/run.ts +12 -0
  113. package/src/a11y/useNotes.ts +139 -0
  114. package/src/app/App.tsx +156 -0
  115. package/src/app/Screen.tsx +416 -0
  116. package/src/app/UserMenu.tsx +95 -0
  117. package/src/app/api.ts +176 -0
  118. package/src/app/appearance.ts +54 -0
  119. package/src/app/browser-api.ts +173 -0
  120. package/src/app/collection-icon.ts +15 -0
  121. package/src/app/context.tsx +32 -0
  122. package/src/app/entries-changed.ts +48 -0
  123. package/src/app/file-url.tsx +44 -0
  124. package/src/app/format.ts +39 -0
  125. package/src/app/idb.ts +82 -0
  126. package/src/app/load.ts +40 -0
  127. package/src/app/media.ts +20 -0
  128. package/src/app/navigation.ts +4 -0
  129. package/src/app/pane-context.ts +25 -0
  130. package/src/app/panes.tsx +513 -0
  131. package/src/app/router.ts +158 -0
  132. package/src/body/BodyEditor.tsx +1139 -0
  133. package/src/body/TableView.tsx +700 -0
  134. package/src/body/catalog.ts +198 -0
  135. package/src/body/checks.ts +83 -0
  136. package/src/body/chips.ts +150 -0
  137. package/src/body/commands.ts +191 -0
  138. package/src/body/document.ts +388 -0
  139. package/src/body/insert.tsx +301 -0
  140. package/src/body/nodes.ts +287 -0
  141. package/src/body/reveal.ts +93 -0
  142. package/src/body/scroll.ts +53 -0
  143. package/src/body/settings.tsx +510 -0
  144. package/src/body/shortcuts.ts +31 -0
  145. package/src/body/table.ts +192 -0
  146. package/src/body/ui.ts +77 -0
  147. package/src/body/views.tsx +268 -0
  148. package/src/build.ts +6 -0
  149. package/src/cloud/CloudGate.tsx +245 -0
  150. package/src/cloud/access.ts +52 -0
  151. package/src/cloud/changes.tsx +474 -0
  152. package/src/cloud/connection.ts +281 -0
  153. package/src/cloud/editor.tsx +250 -0
  154. package/src/cloud/session.ts +289 -0
  155. package/src/cloud/storage.ts +212 -0
  156. package/src/editor/Field.tsx +182 -0
  157. package/src/editor/PagePicker.tsx +102 -0
  158. package/src/editor/arrange.ts +98 -0
  159. package/src/editor/context.tsx +31 -0
  160. package/src/editor/copy.ts +30 -0
  161. package/src/editor/reveal.ts +38 -0
  162. package/src/editor/target.ts +104 -0
  163. package/src/editor/values.ts +71 -0
  164. package/src/github/GitHubGate.tsx +298 -0
  165. package/src/github/api.ts +81 -0
  166. package/src/github/cache.ts +64 -0
  167. package/src/github/session.ts +146 -0
  168. package/src/index.ts +10 -0
  169. package/src/media/MediaPicker.tsx +302 -0
  170. package/src/media/file-icon.ts +17 -0
  171. package/src/media/formats.ts +262 -0
  172. package/src/media/hygiene.ts +220 -0
  173. package/src/media/upload.ts +47 -0
  174. package/src/mount.tsx +20 -0
  175. package/src/schedule/ScheduleDialog.tsx +303 -0
  176. package/src/schedule/ScheduleSummary.tsx +59 -0
  177. package/src/schedule/ScheduledView.tsx +294 -0
  178. package/src/schedule/cloud.ts +66 -0
  179. package/src/schedule/now.ts +18 -0
  180. package/src/schedule/times.ts +122 -0
  181. package/src/screens/EntryEditor.tsx +1817 -0
  182. package/src/screens/EntryList.tsx +272 -0
  183. package/src/screens/Home.tsx +131 -0
  184. package/src/screens/ListPane.tsx +477 -0
  185. package/src/screens/MediaBrowser.tsx +253 -0
  186. package/src/screens/NotFound.tsx +19 -0
  187. package/src/screens/PageTree.tsx +738 -0
  188. package/src/screens/Redirects.tsx +522 -0
  189. package/src/screens/Unavailable.tsx +27 -0
  190. package/src/screens/status.tsx +104 -0
  191. package/src/studio.css +168 -0
  192. package/src/types.ts +82 -0
  193. package/src/widgets/basic.tsx +315 -0
  194. package/src/widgets/blank-date.ts +32 -0
  195. package/src/widgets/crops.tsx +399 -0
  196. package/src/widgets/focal-point.tsx +240 -0
  197. package/src/widgets/geo-map.tsx +160 -0
  198. package/src/widgets/geo.tsx +170 -0
  199. package/src/widgets/group.tsx +827 -0
  200. package/src/widgets/icon.tsx +47 -0
  201. package/src/widgets/image.tsx +407 -0
  202. package/src/widgets/markdown-lazy.tsx +22 -0
  203. package/src/widgets/markdown.tsx +267 -0
  204. package/src/widgets/reference.tsx +196 -0
  205. package/src/widgets/registry.ts +104 -0
  206. package/src/widgets/special.tsx +290 -0
  207. package/src/widgets/union.tsx +233 -0
@@ -0,0 +1,31 @@
1
+ import type { Editor, JSONContent } from '@tiptap/core';
2
+ import { type EditorState } from '@tiptap/pm/state';
3
+ import type { BlockKind, BlockShape } from './catalog.ts';
4
+ import type { ContainerTypes } from './nodes.ts';
5
+ /** Node types that are blocks the studio manages: tags, components and protected blocks. */
6
+ export declare function isManaged(type: string, containers: ContainerTypes): boolean;
7
+ /** The managed node the selection is on (a node selection), or null. */
8
+ export declare function selectedNode(editor: Editor, containers: ContainerTypes): {
9
+ pos: number;
10
+ type: string;
11
+ } | null;
12
+ /** The innermost container the cursor is inside, or null. */
13
+ export declare function enclosingContainer(editor: Editor, containers: ContainerTypes): number | null;
14
+ /** Selects the block at `pos` as a whole, so its frame shows focus and Enter opens its settings. */
15
+ export declare function selectBlock(editor: Editor, pos: number): void;
16
+ /** Moves the block at `pos` past its previous or next sibling. Returns its new position, or null. */
17
+ export declare function moveBlock(editor: Editor, pos: number, direction: -1 | 1): number | null;
18
+ /** Removes the block or chip at `pos`, leaving the cursor where it was. */
19
+ export declare function removeBlock(editor: Editor, pos: number): void;
20
+ /** The editor node a new block of `kind` starts as. */
21
+ export declare function newNode(kind: BlockKind, shape: BlockShape, containers: ContainerTypes, attributes?: Readonly<Record<string, unknown>>, inner?: string): JSONContent;
22
+ /** Where a kind can go at the cursor: as a block, inline, or not at all. Reads only the state. */
23
+ export declare function placementAt(state: EditorState, kind: BlockKind, containers: ContainerTypes): BlockShape | null;
24
+ /**
25
+ * Inserts a new block or chip of `kind` at the cursor (replacing `range`,
26
+ * the "/" text, when given) and returns its position.
27
+ */
28
+ export declare function insertBlock(editor: Editor, kind: BlockKind, shape: BlockShape, containers: ContainerTypes, range?: {
29
+ from: number;
30
+ to: number;
31
+ }, attributes?: Readonly<Record<string, unknown>>): number | null;
@@ -0,0 +1,42 @@
1
+ import type { JSONContent } from '@tiptap/core';
2
+ import type { MarkdownManager } from '@tiptap/markdown';
3
+ import { type BodyFormat, type BodyItem, type MarkdocParseOptions, type ParsedBody, type TableEdit, type TableValue } from '@vespercms/core';
4
+ import type { Catalog } from './catalog.ts';
5
+ import { type ContainerTypes } from './nodes.ts';
6
+ export type BodyContext = {
7
+ format: BodyFormat;
8
+ markdown: MarkdownManager;
9
+ catalog: Catalog;
10
+ containers: ContainerTypes;
11
+ markdoc?: MarkdocParseOptions;
12
+ };
13
+ export type BodyDocument = {
14
+ /** The text the document was built from, and its blocks. */
15
+ parsed: ParsedBody;
16
+ doc: JSONContent;
17
+ };
18
+ /** Builds the editor document for a body. */
19
+ export declare function buildDocument(body: string, context: BodyContext): BodyDocument;
20
+ /** Text with `indent` taken off every line after the first; null when a line isn't indented so. */
21
+ export declare function dedent(text: string, indent: string): string | null;
22
+ /** How every node with a path first looked: its JSON, or for a container its opening. */
23
+ export declare function snapshot(doc: JSONContent, containers: ContainerTypes): Map<string, string>;
24
+ type ItemsContext = Pick<BodyContext, 'markdown' | 'containers'> & {
25
+ parsed?: ParsedBody;
26
+ /** How the body parses, for checking a changed table reads back. */
27
+ format?: BodyFormat;
28
+ markdoc?: MarkdocParseOptions;
29
+ };
30
+ /** The edited body as items: unchanged blocks keep their bytes, changed ones are written. */
31
+ export declare function bodyItems(doc: JSONContent, initial: ReadonlyMap<string, string>, context: ItemsContext): BodyItem[];
32
+ /**
33
+ * A table's text as it will be saved: spliced into the table it was loaded
34
+ * from (`path`), or written new. Refused when it wouldn't read back as this
35
+ * table.
36
+ */
37
+ export declare function tableEdit(path: string | null, value: TableValue, context: ItemsContext): TableEdit;
38
+ /** A node's text as it will be saved. */
39
+ export declare function nodeText(node: JSONContent, markdown: MarkdownManager): string;
40
+ /** A plain-language name for a protected block, from its parser type. */
41
+ export declare function sourceLabel(blockType: string): string;
42
+ export {};
@@ -0,0 +1,69 @@
1
+ import { Extension, type Editor } from '@tiptap/core';
2
+ import { PluginKey, type EditorState } from '@tiptap/pm/state';
3
+ import { type BlockKind, type BlockShape, type Catalog } from './catalog.ts';
4
+ import type { ContainerTypes } from './nodes.ts';
5
+ export type InsertChoice = {
6
+ kind: BlockKind;
7
+ shape: BlockShape;
8
+ };
9
+ type SlashState = {
10
+ active: false;
11
+ } | {
12
+ active: true;
13
+ from: number;
14
+ index: number;
15
+ };
16
+ export declare const slashKey: PluginKey<SlashState>;
17
+ /** What can be inserted at the cursor right now, by friendly name. */
18
+ export declare function choicesAt(state: EditorState, catalog: Catalog, containers: ContainerTypes): InsertChoice[];
19
+ export declare function slashQuery(state: EditorState): string | null;
20
+ export declare function filterChoices(choices: readonly InsertChoice[], query: string): InsertChoice[];
21
+ export type SlashOptions = {
22
+ listboxId: string;
23
+ optionId: (index: number) => string;
24
+ /** The options for the current query (computed by the caller, which knows the catalog). */
25
+ choices: (state: EditorState) => InsertChoice[];
26
+ choose: (choice: InsertChoice, range: {
27
+ from: number;
28
+ to: number;
29
+ }) => void;
30
+ disabled: () => boolean;
31
+ };
32
+ /**
33
+ * The "/" plugin: opens on "/" typed at a line's start or after a space in a
34
+ * paragraph, closes on Escape, a space, or the cursor leaving the query, and
35
+ * sets the editor's role and ARIA state while it's open.
36
+ */
37
+ export declare function SlashCommands(options: SlashOptions): Extension<any, any>;
38
+ /**
39
+ * The text's role and ARIA state: a multi-line textbox, or a combobox while
40
+ * the "/" list is open. Given to the editor through `editorProps.attributes`,
41
+ * which is the only place that can replace Tiptap's own `role="textbox"`.
42
+ */
43
+ export declare function slashAttributes(state: EditorState, options: Pick<SlashOptions, 'listboxId' | 'optionId'> & {
44
+ count: (query: string) => number;
45
+ }): Record<string, string>;
46
+ /** The "/" list, shown under the cursor while the plugin is open. */
47
+ export declare function SlashList({ editor, listboxId, optionId, choices, onChoose, }: {
48
+ editor: Editor;
49
+ listboxId: string;
50
+ optionId: (index: number) => string;
51
+ choices: (state: EditorState) => InsertChoice[];
52
+ onChoose: (choice: InsertChoice, range: {
53
+ from: number;
54
+ to: number;
55
+ }) => void;
56
+ }): import("react").JSX.Element | null;
57
+ /** The toolbar's Insert menu: the same list, for people who don't use "/". */
58
+ export declare function InsertMenu({ editor, choices, onChoose, disabled, }: {
59
+ editor: Editor;
60
+ choices: (state: EditorState) => InsertChoice[];
61
+ onChoose: (choice: InsertChoice) => void;
62
+ disabled: boolean;
63
+ }): import("react").JSX.Element | null;
64
+ /** Stable ids for the "/" listbox and its options. */
65
+ export declare function useSlashIds(): {
66
+ listboxId: string;
67
+ optionId: (index: number) => string;
68
+ };
69
+ export {};
@@ -0,0 +1,38 @@
1
+ import { Extension, Node, type AnyExtension } from '@tiptap/core';
2
+ import type { BlockKind, Catalog } from './catalog.ts';
3
+ /** Editor node types a Markdown block may become. Anything else stays source. */
4
+ export declare const WRITABLE_NODES: Set<string>;
5
+ /** Remembers which original block a Markdown node came from. */
6
+ export declare const BlockIndex: Extension<any, any>;
7
+ /** A block the writing view can't show: kept exactly, editable as raw text. */
8
+ export declare const SourceBlock: Node<any, any>;
9
+ /** A tag or component without editable content (spec §6.5.1: "a compact card"). Its whole text is `source`. */
10
+ export declare const VesperCard: Node<any, any>;
11
+ /**
12
+ * A pipe table the writing view edits cell by cell (DECISIONS.md, "Tables
13
+ * are edited as a grid, and a save splices cells"). One atom, whose view is
14
+ * a grid that owns its own focus; `value` is the table as `table.ts` holds it.
15
+ */
16
+ export declare const VesperTable: Node<any, any>;
17
+ /**
18
+ * An inline chip: an inline tag, a variable, an annotation, inline JSX or an
19
+ * expression. Its text is `source`. To the Markdown parser and serializer it
20
+ * is one placeholder (see chips.ts).
21
+ */
22
+ export declare const VesperInline: Node<any, any>;
23
+ export type ContainerTypes = {
24
+ /** Editor node types for containers, one per kind that has content. */
25
+ extensions: AnyExtension[];
26
+ /** The editor node type for a kind's container. */
27
+ typeOf: (kind: BlockKind) => string | undefined;
28
+ /** The kind behind a container node type. */
29
+ kindOf: (type: string) => BlockKind | undefined;
30
+ /** Whether a node of `type` may sit directly inside a container of `kind`. */
31
+ allows: (kind: BlockKind, type: string) => boolean;
32
+ };
33
+ /**
34
+ * One container node type per tag or component with content. Its content
35
+ * expression comes from Markdoc's `children`, so only allowed node types
36
+ * fit inside (spec §6.5.1), and protected blocks always do.
37
+ */
38
+ export declare function containerTypes(catalog: Catalog): ContainerTypes;
@@ -0,0 +1,13 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ import { type BodyBlock, type BodyFormat, type MarkdocParseOptions } from '@vespercms/core';
3
+ import type { ContainerTypes } from './nodes.ts';
4
+ /** The index of the block holding `offset` at each level, going into containers' children. */
5
+ export declare function blockPathAt(blocks: readonly BodyBlock[], offset: number): number[];
6
+ /** Selects the block at `offset` of `text` in the writing view, focuses it and scrolls to it. */
7
+ export declare function revealInEditor(editor: Editor, text: string, offset: number, options: {
8
+ format: BodyFormat;
9
+ markdoc?: MarkdocParseOptions;
10
+ containers: ContainerTypes;
11
+ }): void;
12
+ /** Selects a range of the source view's text, focuses it and scrolls to it. */
13
+ export declare function revealInSource(textarea: HTMLTextAreaElement, start: number, end: number): void;
@@ -0,0 +1,16 @@
1
+ import type { EditorView } from '@tiptap/pm/view';
2
+ /**
3
+ * A formatting toolbar inside its editor's frame, stuck below whatever
4
+ * already floats at the top of the scroller (`--sticky-top`, set by the
5
+ * screen or the pane), and only while its editor is on screen. Outside them,
6
+ * in a dialog, it sticks to the dialog's top.
7
+ */
8
+ export declare const stuckToolbar = "sticky top-[var(--sticky-top,0px)] z-1 rounded-t-md border-b border-border bg-surface-raised p-1";
9
+ /**
10
+ * ProseMirror keeps the caret inside its scroller's box, but a stuck
11
+ * formatting toolbar (and the glass above it) covers that box's top, so the
12
+ * caret could move up beneath them out of sight. Before ProseMirror scrolls,
13
+ * this scrolls a caret that's under the toolbar clear of it; ProseMirror's
14
+ * own scrolling then does the rest. Always false, so it never replaces it.
15
+ */
16
+ export declare function scrollClearOfToolbar(view: EditorView, toolbar: HTMLElement | null): boolean;
@@ -0,0 +1,19 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ export declare function BlockSettings({ editor, pos, onClose, }: {
3
+ editor: Editor;
4
+ pos: number | null;
5
+ onClose: () => void;
6
+ }): import("react").JSX.Element;
7
+ /** Partial-like files, as paths relative to the entry (how the tag stores them), labelled by site path. */
8
+ export declare function partialOptions(files: readonly string[], entryPath: string, current: string): {
9
+ value: string;
10
+ label: string;
11
+ }[];
12
+ /**
13
+ * Markdoc files named like partials (`_name.mdoc`, or in a `partials`
14
+ * folder), nearest the entry first: its own folder, then the folders above
15
+ * it, then everything else.
16
+ */
17
+ export declare function partialFiles(files: readonly string[], entryPath: string): string[];
18
+ /** `to` relative to the folder holding `from`, both site-relative: `_x.mdoc`, `../_x.mdoc`. */
19
+ export declare function relativePath(from: string, to: string): string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A keyboard shortcut as people read it ("Ctrl+B", "⌘B") and as
3
+ * `aria-keyshortcuts` writes it ("Control+B", "Meta+B"). `Mod` is Command on
4
+ * a Mac and Control elsewhere, as in Tiptap's own keymaps.
5
+ */
6
+ export type Shortcut = {
7
+ text: string;
8
+ aria: string;
9
+ };
10
+ export declare const isMac: boolean;
11
+ type Modifier = 'Mod' | 'Alt' | 'Shift';
12
+ export declare function shortcut(...keys: [...Modifier[], string]): Shortcut;
13
+ export {};
@@ -0,0 +1,31 @@
1
+ import type { JSONContent } from '@tiptap/core';
2
+ import type { MarkdownManager } from '@tiptap/markdown';
3
+ import { type BodyTable, type TableAlign, type TableValue } from '@vespercms/core';
4
+ /** A cell's inline nodes, from its text as written (pipes escaped, chips as placeholders). */
5
+ export declare function parseCell(markdown: MarkdownManager, text: string): JSONContent[] | null;
6
+ /** Inline nodes as a cell's text: one line, every pipe escaped. */
7
+ export declare function cellText(markdown: MarkdownManager, inline: readonly JSONContent[]): string;
8
+ /**
9
+ * The cell's inline nodes when the writing view can own it: one line of
10
+ * inline Markdown that reads back the same once written. Null otherwise.
11
+ */
12
+ export declare function cellFits(markdown: MarkdownManager, text: string): JSONContent[] | null;
13
+ /** The table as loaded, each cell with its chips as placeholders. */
14
+ export declare function editorValue(text: string, table: BodyTable): TableValue;
15
+ /** The table with every chip's text back in place, for core's writer. */
16
+ export declare function savedValue(value: TableValue): TableValue;
17
+ /** A new table: a header row and one row, two columns, all empty. */
18
+ export declare function newTableValue(): TableValue;
19
+ export declare function setCell(value: TableValue, row: number, column: number, text: string): TableValue;
20
+ /** A new empty row at `at` (1 or more: the header stays first). */
21
+ export declare function insertRow(value: TableValue, at: number): TableValue;
22
+ /** A new empty column at `at`. */
23
+ export declare function insertColumn(value: TableValue, at: number): TableValue;
24
+ /** Moves a row below the header to `to` (also below the header). */
25
+ export declare function moveRow(value: TableValue, from: number, to: number): TableValue;
26
+ export declare function moveColumn(value: TableValue, from: number, to: number): TableValue;
27
+ /** Deletes a row below the header. */
28
+ export declare function deleteRow(value: TableValue, row: number): TableValue;
29
+ /** Deletes a column, unless it's the only one. */
30
+ export declare function deleteColumn(value: TableValue, column: number): TableValue;
31
+ export declare function alignColumn(value: TableValue, column: number, align: TableAlign): TableValue;
@@ -0,0 +1,30 @@
1
+ import type { BodyIssue, TableEdit, TableValue } from '@vespercms/core';
2
+ import type { BlockKind, Catalog } from './catalog.ts';
3
+ import type { BodyChecks } from './checks.ts';
4
+ import type { ContainerTypes } from './nodes.ts';
5
+ export type BodyActions = {
6
+ openSettings: (pos: number) => void;
7
+ move: (pos: number, direction: -1 | 1) => void;
8
+ remove: (pos: number) => void;
9
+ };
10
+ export type BodyUi = BodyActions & {
11
+ catalog: Catalog;
12
+ containers: ContainerTypes;
13
+ disabled: boolean;
14
+ checks: BodyChecks;
15
+ /** Site-relative paths of the files a partial can point at, once loaded. */
16
+ partials: readonly string[] | null;
17
+ entryPath: string;
18
+ /** Says something in the body's polite status ("Row added below row 3."). */
19
+ announce: (message: string) => void;
20
+ /** Whether a table's change would save, through core's writer, against the table it was loaded from. */
21
+ checkTable: (path: string | null, value: TableValue) => TableEdit;
22
+ /** True once, for the table just inserted, so it starts with its first cell being edited. */
23
+ takeNewTable: () => boolean;
24
+ };
25
+ export declare const BodyUiContext: import("react").Context<BodyUi | null>;
26
+ export declare function useBodyUi(): BodyUi;
27
+ /** One block's issues, from its own text: synchronous for Markdoc, after a tick for MDX. */
28
+ export declare function useBlockIssues(text: string): BodyIssue[];
29
+ /** The header's summary: the summary attribute's value, shown with its option label. */
30
+ export declare function summaryOf(kind: BlockKind | undefined, values: Readonly<Record<string, unknown>>): string;
@@ -0,0 +1,9 @@
1
+ import { type ReactNodeViewProps } from '@tiptap/react';
2
+ /** A tag with editable content: the header, then the content, inside a hairline frame. */
3
+ export declare function ContainerView({ node, getPos, selected, editor }: ReactNodeViewProps): import("react").JSX.Element;
4
+ /** A tag or component without editable content. */
5
+ export declare function CardView({ node, getPos, selected }: ReactNodeViewProps): import("react").JSX.Element;
6
+ /** An inline chip. Tags and components show their friendly name and text; the rest show their source. */
7
+ export declare function InlineView({ node, selected }: ReactNodeViewProps): import("react").JSX.Element;
8
+ /** A protected block: named, shown as source, editable as raw text. */
9
+ export declare function SourceView({ node, getPos, selected }: ReactNodeViewProps): import("react").JSX.Element;
@@ -0,0 +1,57 @@
1
+ import { issuesFromZod as e, markdocConfigFromManifest as t, partialChecker as n, validateMarkdocBody as r, validateMdxBody as i } from "@vespercms/core";
2
+ //#region \0rolldown/runtime.js
3
+ var a = Object.defineProperty, o = /* @__PURE__ */ ((e, t) => {
4
+ let n = {};
5
+ for (var r in e) a(n, r, {
6
+ get: e[r],
7
+ enumerable: !0
8
+ });
9
+ return t || a(n, Symbol.toStringTag, { value: "Module" }), n;
10
+ })({ bodyChecks: () => s });
11
+ function s(a) {
12
+ let { manifest: o, format: s, partials: c, entryPath: l } = a;
13
+ if (s === "mdoc") {
14
+ let e = t(o.markdoc), i = (t) => r(t, {
15
+ config: e,
16
+ ...o.markdoc ? {
17
+ parse: o.markdoc.options,
18
+ tags: o.markdoc.tags
19
+ } : {},
20
+ ...c ? { partialExists: n(l, c) } : {}
21
+ });
22
+ return {
23
+ validate: (e) => Promise.resolve(i(e)),
24
+ validateSync: i
25
+ };
26
+ }
27
+ if (s === "mdx" && (o.mdx?.components.length ?? 0) > 0) {
28
+ let t = null, n = () => (t ??= a.loadMdxProps().then((t) => {
29
+ let n = /* @__PURE__ */ new Map();
30
+ for (let r of o.mdx?.components ?? []) {
31
+ let i = t[r.name], a = i?.safeParseAsync?.bind(i);
32
+ a && n.set(r.name, {
33
+ label: r.label,
34
+ fields: r.fields,
35
+ validate: async (t) => {
36
+ let n = await a(t, { reportInput: !0 });
37
+ return n.success ? [] : e(n.error?.issues ?? []);
38
+ }
39
+ });
40
+ }
41
+ return n;
42
+ }), t);
43
+ return {
44
+ validate: async (e) => {
45
+ let t = await n();
46
+ return t.size > 0 ? i(e, t) : [];
47
+ },
48
+ validateSync: () => null
49
+ };
50
+ }
51
+ return {
52
+ validate: () => Promise.resolve([]),
53
+ validateSync: () => []
54
+ };
55
+ }
56
+ //#endregion
57
+ export { o as n, s as t };
@@ -0,0 +1,14 @@
1
+ import { type GitHubStudioSettings } from '../github/session.ts';
2
+ import type { StudioOptions } from '../types.ts';
3
+ import { type CloudSettings, type StoredSession } from './session.ts';
4
+ import type { KeyValueStore } from '../app/idb.ts';
5
+ type GateOptions = Omit<StudioOptions, 'api' | 'github' | 'account' | 'cloud' | 'connection'>;
6
+ export declare function CloudGate({ settings, github, options, fetcher, store, }: {
7
+ settings: CloudSettings;
8
+ github?: GitHubStudioSettings | null;
9
+ options: GateOptions;
10
+ fetcher?: typeof fetch;
11
+ /** Where the key and tokens are kept: IndexedDB unless a test passes its own. */
12
+ store?: KeyValueStore<StoredSession>;
13
+ }): import("react").JSX.Element;
14
+ export {};
@@ -0,0 +1,14 @@
1
+ import { type CollectionManifest } from '@vespercms/core';
2
+ import type { CloudConnection } from './connection.ts';
3
+ /** True when a collection belongs on this member's menus and home screen. */
4
+ export declare function shownTo(cloud: CloudConnection | undefined, collection: string): boolean;
5
+ /** True when an entry file belongs in this member's lists. */
6
+ export declare function entryShownTo(cloud: CloudConnection | undefined, path: string): boolean;
7
+ /** True when the member may start a new entry at the top of a collection (or in `parent`, in a tree). */
8
+ export declare function mayCreateIn(cloud: CloudConnection | undefined, collection: CollectionManifest, parent?: string): boolean;
9
+ /**
10
+ * True when the member may change a page at this place in a tree (spec
11
+ * §6.15.2, §7.5). A tree path is a grant key: `admissions` is the
12
+ * Admissions section page, `admissions/apply` a page under it.
13
+ */
14
+ export declare function treeCovered(cloud: CloudConnection | undefined, collection: string, path: string): boolean;
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from 'react';
2
+ type ChangesState = {
3
+ open: boolean;
4
+ setOpen: (open: boolean) => void;
5
+ };
6
+ /**
7
+ * `linked` is the `#/changes` address, where review emails send an approver:
8
+ * it opens the drawer, and closing the drawer goes back to `#/`, so a reload
9
+ * or Back doesn't open it again.
10
+ */
11
+ export declare function ChangesProvider({ children, linked }: {
12
+ children: ReactNode;
13
+ linked: boolean;
14
+ }): import("react").JSX.Element;
15
+ /** Opens and closes the drawer; null when the studio isn't connected. */
16
+ export declare function useChanges(): ChangesState | null;
17
+ /** The top bar's button: "3 changes", and how many wait for this member's review. */
18
+ export declare function ChangesButton(): import("react").JSX.Element | null;
19
+ /** How many drafts wait to be published: none when the studio isn't connected. */
20
+ export declare function useDraftsWaiting(): number;
21
+ /**
22
+ * The line about drafts waiting to be published, which opens Changes: on
23
+ * Home, or among panes beside the collections.
24
+ */
25
+ export declare function DraftsWaiting(): import("react").JSX.Element | null;
26
+ export {};
@@ -0,0 +1,100 @@
1
+ import { type CloudClient, type CloudUser, type CommitTrailers, type DeployStatus, type Draft, type HoldResponse, type Manifest, type Member, type PublishResponse, type Review, type ScheduleResponse } from '@vespercms/core';
2
+ import { type StudioApi } from '../app/api.ts';
3
+ import { createBrowserApi } from '../app/browser-api.ts';
4
+ import type { CloudSettings } from './session.ts';
5
+ import { CloudStorage } from './storage.ts';
6
+ export type CloudConnection = {
7
+ user: CloudUser;
8
+ member: Member;
9
+ siteName: string;
10
+ dashboardUrl: string;
11
+ client: CloudClient;
12
+ storage: CloudStorage;
13
+ drafts: DraftsStore;
14
+ /** What the member may do to a file. */
15
+ access: (path: string) => {
16
+ edit: boolean;
17
+ publish: boolean;
18
+ };
19
+ /** What the member may do with a whole draft: the API refuses the same (spec §7.5). */
20
+ draftAccess: (draft: Draft) => {
21
+ edit: boolean;
22
+ publish: boolean;
23
+ };
24
+ /** True when the member's grants cover the whole site (the redirects, settings outside collections). */
25
+ wholeSite: boolean;
26
+ /** True when the member may change anything at all. */
27
+ editsAnything: boolean;
28
+ };
29
+ export type DraftsSnapshot = {
30
+ drafts: readonly Draft[];
31
+ reviews: readonly Review[];
32
+ version: string;
33
+ /**
34
+ * How the site publishes on schedule, and where each change stands (spec
35
+ * §6.17). Absent on a site with no scheduled collection, or until read.
36
+ */
37
+ schedule?: ScheduleResponse;
38
+ };
39
+ /** Drafts and reviews as screens see them, kept current after every change. */
40
+ export declare class DraftsStore {
41
+ private snapshot;
42
+ private readonly listeners;
43
+ private readonly client;
44
+ private readonly storage;
45
+ private readonly scheduled;
46
+ /** `scheduled`: the site has a scheduled collection, so the schedule is read with the drafts. */
47
+ constructor(client: CloudClient, storage: CloudStorage, scheduled?: boolean);
48
+ subscribe: (listener: () => void) => (() => void);
49
+ get: () => DraftsSnapshot;
50
+ /** Called by the storage whenever the drafts change. */
51
+ setDrafts(drafts: readonly Draft[], version: string): void;
52
+ private emit;
53
+ /** Asks the cloud for the drafts and reviews again, and the schedule where the site has one. */
54
+ refresh(): Promise<void>;
55
+ /** The draft holding a file, if one does. */
56
+ draftOf(path: string): Draft | undefined;
57
+ publish(ids: readonly string[]): Promise<PublishResponse>;
58
+ discard(id: string): Promise<void>;
59
+ keepMine(id: string, paths: readonly string[]): Promise<void>;
60
+ requestReview(ids: readonly string[], note: string): Promise<Review>;
61
+ answer(id: string, action: 'comment' | 'request-changes', body: string): Promise<Review>;
62
+ status(commit: string): Promise<DeployStatus>;
63
+ /** Holds drafts in the cloud until `at`, to be published then (`holdScheduledDrafts`). */
64
+ hold(ids: readonly string[], at: Date): Promise<HoldResponse>;
65
+ /** A held draft stops waiting, and stays a draft. */
66
+ release(event: string): Promise<void>;
67
+ /**
68
+ * Cancels a published entry's time to go live (call 9): one publish takes
69
+ * its file out of the site, and its content comes back as a draft. The
70
+ * file isn't live yet, so visitors see no change.
71
+ */
72
+ backToDraft(path: string, message: {
73
+ summary: string;
74
+ trailers?: CommitTrailers;
75
+ }): Promise<{
76
+ ok: true;
77
+ } | {
78
+ ok: false;
79
+ message: string;
80
+ }>;
81
+ }
82
+ /** The cloud's errors in the studio's terms: plain words, and a status the screens understand. */
83
+ export declare function cloudApiError(error: unknown, onExpired?: () => void): unknown;
84
+ export type ConnectOptions = {
85
+ settings: CloudSettings;
86
+ client: CloudClient;
87
+ user: CloudUser;
88
+ member: Member;
89
+ siteName: string;
90
+ manifest: Manifest;
91
+ loadMdxProps: () => Promise<Readonly<Record<string, unknown>>>;
92
+ loadUrlFunctions?: Parameters<typeof createBrowserApi>[0]['loadUrlFunctions'];
93
+ routes?: readonly (readonly [string, string])[];
94
+ onExpired?: () => void;
95
+ };
96
+ /** The studio's back end over the cloud, and the connection screens use. */
97
+ export declare function connect(options: ConnectOptions): {
98
+ api: StudioApi;
99
+ connection: CloudConnection;
100
+ };
@@ -0,0 +1,44 @@
1
+ import { type DeployStatus, type Draft, type Review } from '@vespercms/core';
2
+ import type { DraftsSnapshot } from './connection.ts';
3
+ /** The drafts and reviews, current after every change. Empty when not connected. */
4
+ export declare function useDrafts(): DraftsSnapshot;
5
+ /** The draft holding a file, and the review that draft waits in. */
6
+ export declare function useEntryDraft(path: string): {
7
+ draft: Draft | undefined;
8
+ review: Review | undefined;
9
+ };
10
+ /** What the host reported for a published commit, asked again until the build is done. */
11
+ export declare function useDeployStatus(commit: string | null): DeployStatus | null;
12
+ /** The plain sentence after a publish (spec §6.16): building, live, failed, or nothing reported. */
13
+ export declare function DeployLine({ commit }: {
14
+ commit: string;
15
+ }): import("react").JSX.Element;
16
+ /**
17
+ * A change published since the draft began (spec §7.4): the published file
18
+ * and the draft side by side, and the editor's choice. Nothing is
19
+ * overwritten silently.
20
+ */
21
+ export declare function ConflictCompare({ path, draft, publishedVersion, onKeep, onDiscard, }: {
22
+ path: string;
23
+ draft: Draft;
24
+ publishedVersion: string | null;
25
+ onKeep: () => void;
26
+ onDiscard: () => void;
27
+ }): import("react").JSX.Element;
28
+ /** Asks for review, with an optional note for the approvers. */
29
+ export declare function RequestReviewDialog({ isOpen, onOpenChange, onSend, }: {
30
+ isOpen: boolean;
31
+ onOpenChange: (open: boolean) => void;
32
+ onSend: (note: string) => Promise<void>;
33
+ }): import("react").JSX.Element;
34
+ /**
35
+ * Unsaved changes kept on this device while the cloud can't be reached
36
+ * (spec §6.9): written as they're typed, cleared once a draft is saved.
37
+ */
38
+ export type KeptChanges = {
39
+ values: unknown;
40
+ body: string;
41
+ raw: string;
42
+ };
43
+ export declare function keptChanges(): import("../app/idb.ts").KeyValueStore<KeptChanges>;
44
+ export declare function keptKey(project: string, collection: string, id: string | null): string;
@@ -0,0 +1,62 @@
1
+ import { CloudClient, type CloudStudioSettings, type CloudTokens, type CloudUser, type Member } from '@vespercms/core';
2
+ import { type KeyValueStore } from '../app/idb.ts';
3
+ /** The cloud's settings, as the build writes them into the studio. */
4
+ export type CloudSettings = CloudStudioSettings & {
5
+ /** The site's schemas built for the browser. */
6
+ schemas: string;
7
+ };
8
+ export type StoredSession = {
9
+ key: CryptoKeyPair;
10
+ tokens: CloudTokens | null;
11
+ user?: CloudUser;
12
+ member?: Member;
13
+ siteName?: string;
14
+ };
15
+ export declare function sessionStore(): KeyValueStore<StoredSession>;
16
+ /** The stored key, made and kept on first use: a sign-in must exchange its code with the key it started with. */
17
+ export declare function sessionKey(project: string, store?: KeyValueStore<StoredSession>): Promise<StoredSession>;
18
+ /** The studio's own address, which the app sends the browser back to. */
19
+ export declare function studioAddress(): string;
20
+ /** Goes to the app's sign-in. `route` is the studio's hash route to come back to. */
21
+ export declare function startCloudSignIn(settings: CloudSettings, route: string, store?: KeyValueStore<StoredSession>): Promise<void>;
22
+ /** The callback's parameters, when the app sent the browser back here. */
23
+ export declare function callbackParams(hash?: string): URLSearchParams | null;
24
+ export type CloudSignedIn = {
25
+ client: CloudClient;
26
+ user: CloudUser;
27
+ member: Member;
28
+ siteName: string;
29
+ };
30
+ export type CloudSignIn = ({
31
+ ok: true;
32
+ route: string;
33
+ } & CloudSignedIn) | {
34
+ ok: false;
35
+ message: string;
36
+ };
37
+ /**
38
+ * Finishes a sign-in when the app sent the browser back here, or returns
39
+ * null when it didn't. The code leaves the address either way.
40
+ */
41
+ export declare function finishCloudSignIn(settings: CloudSettings, options?: {
42
+ store?: KeyValueStore<StoredSession>;
43
+ fetch?: typeof fetch;
44
+ }): Promise<CloudSignIn | null>;
45
+ export type Resumed = ({
46
+ kind: 'signed-in';
47
+ } & CloudSignedIn) | {
48
+ kind: 'signed-out';
49
+ } | {
50
+ kind: 'unreachable';
51
+ };
52
+ /**
53
+ * Picks up a sign-in this browser already has. The refresh brings fresh
54
+ * tokens and the person's current role and grants, which may have changed
55
+ * in the dashboard since.
56
+ */
57
+ export declare function resumeCloudSession(settings: CloudSettings, options?: {
58
+ store?: KeyValueStore<StoredSession>;
59
+ fetch?: typeof fetch;
60
+ }): Promise<Resumed>;
61
+ /** Ends the sign-in, here and in the cloud. The key goes too: the next sign-in makes a new one. */
62
+ export declare function endCloudSession(settings: CloudSettings, client: CloudClient | null, store?: KeyValueStore<StoredSession>): Promise<void>;