@synopackageland/ui 0.17.9 → 0.17.10

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 (41) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +51 -17
  3. package/dist/components/composite/comment-item/CommentItem.vue.d.ts +5 -1
  4. package/dist/components/composite/copy-button/CopyButton.vue.d.ts +2 -2
  5. package/dist/components/composite/file-picker/FilePicker.vue.d.ts +5 -2
  6. package/dist/components/composite/file-thumbs/FileThumbs.vue.d.ts +24 -0
  7. package/dist/components/composite/image-lightbox/ImageLightbox.vue.d.ts +19 -0
  8. package/dist/components/composite/mention-list/MentionList.vue.d.ts +31 -0
  9. package/dist/components/composite/rich-text-view/RichTextBlocks.vue.d.ts +14 -0
  10. package/dist/components/composite/rich-text-view/RichTextImage.vue.d.ts +12 -0
  11. package/dist/components/composite/rich-text-view/RichTextInline.vue.d.ts +23 -0
  12. package/dist/components/composite/rich-text-view/RichTextView.vue.d.ts +28 -0
  13. package/dist/components/composite/upload-queue/UploadQueue.vue.d.ts +36 -0
  14. package/dist/components/composite/wizard/WizardStep.vue.d.ts +1 -1
  15. package/dist/components/rich-text-editor/RichTextEditor.vue.d.ts +83 -0
  16. package/dist/components/rich-text-editor/extensions.d.ts +30 -0
  17. package/dist/components/rich-text-editor/markdown.d.ts +22 -0
  18. package/dist/i18n/filePicker.d.ts +3 -0
  19. package/dist/i18n/text.d.ts +48 -0
  20. package/dist/index.d.ts +15 -4
  21. package/dist/index.js +11143 -9907
  22. package/dist/json/catalog.d.ts +2 -2
  23. package/dist/json/registry.d.ts +34 -34
  24. package/dist/json-preview/preview.js +3436 -3004
  25. package/dist/lib/files.d.ts +28 -0
  26. package/dist/lib/mentions.d.ts +0 -10
  27. package/dist/lib/richText.d.ts +66 -0
  28. package/dist/lib/uploadQueue.d.ts +39 -0
  29. package/dist/reka-runtime.json +1 -1
  30. package/dist/rich-text.d.ts +3 -0
  31. package/dist/rich-text.js +1075 -0
  32. package/dist/theme.css +1 -1
  33. package/dist/themes/dsm7-embedded-theme.css +1 -1
  34. package/dist/themes/dsm7-embedded.css +1 -1
  35. package/dist/themes/dsm7.css +1 -1
  36. package/docs/theme-hooks.md +11 -2
  37. package/package.json +14 -1
  38. package/dist/components/composite/comment-item/CommentInlineParts.vue.d.ts +0 -7
  39. package/dist/components/composite/mention-textarea/MentionTextarea.vue.d.ts +0 -52
  40. package/dist/lib/commentMarkdown.d.ts +0 -34
  41. /package/dist/components/composite/{mention-textarea → comment-item}/PersonInitials.vue.d.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.10 (2026-10-30)
4
+
5
+ Breaking, with no compatibility layer: the comment Markdown functions are renamed, `MentionTextarea` is gone, and comment bodies read a little differently. A stored body that relied on the old reading needs a one-time conversion; the library keeps no old names or fallback.
6
+
7
+ - `commentMarkdownParts` and `commentMarkdownText` are renamed `richTextParts(body, { people, headings })` and `richTextText`, with no alias; `CommentBlock` and `CommentInline` are now `RichTextBlock` and `RichTextInline`. A list item is `{ parts, list? }` instead of an array of parts. Add `richTextFileIds`, the image files a body uses.
8
+ - The restricted Markdown grows: `~~strike~~`, `1. ` numbered lists (from any start), lists two levels deep, `> ` quotes, fenced code (a longer fence holds ``` lines), `## ` and `### ` headings with `headings`, and `![alt](file:ID)` images of stored files (any other address stays as typed). A blank line now starts a new paragraph. A backslash keeps the punctuation after it as typed, and `<`, `>`, `&` and ` ` read as their characters. `**` closes next to CJK punctuation, as in `**「引號」**後面`. Link addresses may hold one level of parentheses. HTML, tables and `---` still show as typed.
9
+ - `CommentItem` renders its body with the new `RichTextView` and takes `files(id)` to show images. Its inner hooks are renamed: `data-ui-comment-mention`, `-link`, `-code` and `-list` are now `data-ui-rich-mention`, `-link`, `-code` and `-list`; paragraphs are `data-ui-rich-paragraph`.
10
+ - Remove `MentionTextarea`, `mentionTokens` and `mentionText`. Comment boxes use the compact `RichTextEditor`.
11
+ - Add the `@synopackageland/ui/rich-text` entry with `RichTextEditor`, a Tiptap 3 editor (MIT parts only) that reads and writes the restricted Markdown, plus `markdownToDoc` and `docToMarkdown`. `variant="full"` has a toolbar that stays at the top and moves what does not fit into More; `variant="compact"` is a comment box that grows from one line, with Markdown as you type, Ctrl/⌘+B, I and K, a toolbar over selected text with a mouse, an "Aa" row of formats (one box at a time, Escape closes it), Ctrl/⌘+Enter or the send button to `submit`, and images under the text. Pasted, dropped and chosen images upload through the app's `upload`; HEIC, HEIF and AVIF are refused with a note, images from other websites are left out with a note, and the box cannot send while an image uploads (`pending`). Tiptap is a dependency loaded only by this entry.
12
+ - Add `RichTextView`, `FileThumbs` (thumbnails and +N), `ImageLightbox` (full-size files with arrow keys and swipes), `UploadQueue` (progress, retry, cancel) with `useUploadQueue`, `MentionList`, `FileItem`, `isPicture` and `isUnsupportedPicture`. `useUiText` is exported for companion entries.
13
+ - `FilePicker` takes pasted files with `paste`: a paste on the picker, or its Paste button where the browser can read the clipboard. A pasted screenshot is named after the time it was pasted.
14
+ - New strings in all nine built-in languages for the editor, the file views and the upload queue.
15
+
3
16
  ## 0.17.9 (2026-10-30)
4
17
 
5
18
  - A `CommentItem` body with many unmatched `*` or `**` no longer slows down: each line is read in one pass, so a 70,000-character comment of unclosed stars takes milliseconds instead of seconds. What the comment shows is unchanged.
package/README.md CHANGED
@@ -621,7 +621,7 @@ touch keyboard is open; set `hidden` while a full-screen drawer covers the page.
621
621
  </AppLayout>
622
622
  ```
623
623
 
624
- ### Collaboration: CompletionToggle, MentionTextarea, CommentItem
624
+ ### Collaboration: CompletionToggle, CommentItem, rich text and files
625
625
 
626
626
  `CompletionToggle` is the round done toggle of a task row, card or detail
627
627
  header: a `role="checkbox"` with a 44px touch target. Show the new state at
@@ -629,24 +629,58 @@ once, set `pending` while it saves, and on failure put the old state back and
629
629
  set `error`. Its click does not reach the row, so it never also opens the item.
630
630
  Name it after the item: `label="Done: Send the quote"`.
631
631
 
632
- `MentionTextarea` opens a list of people on `@`. The app owns the people: it
633
- answers `search` (the text after `@`) by setting `candidates`, and turns
634
- `filter` off when its server already matched them. A chosen person is written
635
- `@Label` in the text and kept in `v-model:mentions`; `mentionTokens(text,
636
- mentions).body` is what to store, with `@[id]` tokens, and `mentionText(body,
637
- people)` turns a stored body back for editing. With `maxlength` the box counts
638
- the stored characters near and over the limit; it never cuts text, so disable
639
- sending while it is over (`data-over`).
640
-
641
- `CommentItem` shows one comment: avatar, author, the time as the app formats
642
- it, and the stored body with its line breaks, mentions as names and http(s)
643
- links. `edited`, `deleted` and `highlighted` (a one-time flash for a comment
644
- opened from a notification) cover the rest; edit and delete controls go in
645
- `#actions`.
632
+ Comments and rich text fields store one restricted Markdown: `**bold**`,
633
+ `*italic*`, `~~strike~~`, `` `code` ``, `[text](https://…)` or `mailto:`,
634
+ `@[id]` mentions, `![alt](file:ID)` images of stored files, `- ` and `1. `
635
+ lists two levels deep, `> ` quotes, fenced code and, in a field, `## ` and
636
+ `### ` headings. A backslash keeps punctuation as typed; HTML, tables and
637
+ `---` always show as typed. `richTextParts(body, { people, headings })` reads it
638
+ into blocks of parts, never HTML; `richTextText` gives plain text for a
639
+ notification or a search; `richTextFileIds` lists the image files a body uses.
640
+
641
+ `RichTextView` shows a body: mentions as names, safe links, images that keep
642
+ their shape while they load and open in an `ImageLightbox`. The app passes
643
+ `files(id)`, which describes a stored file (`FileItem`: name, type, `thumb`,
644
+ `preview`, `url`, `width`, `height`, `dominant` colour and a `state` of
645
+ `processing`, `missing` or `changed`). `CommentItem` shows one comment with a
646
+ `RichTextView` body: avatar, author, the time as the app formats it; `edited`,
647
+ `deleted` and `highlighted` (a one-time flash for a comment opened from a
648
+ notification) cover the rest; edit and delete controls go in `#actions`.
649
+
650
+ `RichTextEditor` comes from its own entry, `@synopackageland/ui/rich-text`, so
651
+ an app that only shows rich text never loads Tiptap; load it when an editor
652
+ first opens. Both looks write the same Markdown:
653
+
654
+ - `variant="full"`, for a rich text field: a toolbar that stays at the top, with
655
+ what does not fit in a More menu; images sit in the text.
656
+ - `variant="compact"`, for a comment box (as in Slack, Linear and GitHub): one
657
+ line that grows, Markdown as you type, Ctrl/⌘+B, I and K, a toolbar over
658
+ selected text with a mouse, and an "Aa" button that opens one row of formats
659
+ (the only one on a touch screen). Enter starts a line; Ctrl/⌘+Enter or the
660
+ send button emits `submit`. Images line up under the text, and the box does
661
+ not send while one uploads.
662
+
663
+ The editor knows nothing of the server. `upload(file, { signal, onProgress })`
664
+ sends one file and resolves with its id; pasted, dropped and chosen JPEG, PNG,
665
+ WebP and GIF images go through it, and HEIC is refused with a note (the picker
666
+ names its types, so iOS hands over JPEG). `pending` reports images still
667
+ uploading or failed: a form should not save while there are any. `@` opens the
668
+ people in `candidates`; answer `search` and turn `filter` off when the server
669
+ already matched them. With `maxlength` the box counts characters near and over
670
+ the limit and never cuts text.
671
+
672
+ `FileThumbs` shows a few thumbnails and "+N", `ImageLightbox` shows files full
673
+ size (arrow keys and swipes), and `UploadQueue` lists uploads with progress,
674
+ retry and cancel. `useUploadQueue(upload, { concurrency, retries })` runs the
675
+ uploads it shows. `FilePicker` takes pasted files with `paste`.
646
676
 
647
677
  ```vue
648
- <CommentItem v-for="c in comments" :key="c.id" :author="c.author.name" :time="c.when" :body="c.body" :mentions="c.mentions" :edited="!!c.editedAt" :deleted="c.deleted" />
649
- <MentionTextarea v-model="text" v-model:mentions="mentions" :candidates="people" :filter="false" :maxlength="4000" @search="findPeople" />
678
+ <CommentItem v-for="c in comments" :key="c.id" :author="c.author.name" :time="c.when" :body="c.body" :mentions="people" :files="fileById" :edited="!!c.editedAt" :deleted="c.deleted" />
679
+ <RichTextEditor v-model="text" variant="compact" :candidates="people" :filter="false" :maxlength="4000" :upload="sendFile" :files="fileById" @search="findPeople" @submit="post" />
680
+ ```
681
+
682
+ ```ts
683
+ const RichTextEditor = defineAsyncComponent(() => import('@synopackageland/ui/rich-text').then(module => module.RichTextEditor))
650
684
  ```
651
685
 
652
686
  ### Status: StatusDot, StepList, ConnectionTopology
@@ -1,4 +1,5 @@
1
1
  import { MentionCandidate } from '../../../lib/mentions';
2
+ import { FileItem } from '../../../lib/files';
2
3
  type __VLS_Props = {
3
4
  author: string;
4
5
  /** Keeps the avatar colour stable across renamed accounts. */
@@ -9,10 +10,12 @@ type __VLS_Props = {
9
10
  time?: string;
10
11
  /** The same time for machines (`<time datetime>`), such as an ISO string. */
11
12
  datetime?: string;
12
- /** Text with `@[id]` mention tokens and basic Markdown: bold, italic, code, links and `- ` lists. */
13
+ /** Restricted Markdown with `@[id]` mention tokens and `![alt](file:ID)` images (see `richTextParts`). */
13
14
  body?: string;
14
15
  /** Names for the mention tokens; an unknown id shows as itself. */
15
16
  mentions?: MentionCandidate[];
17
+ /** The file behind an image's id. */
18
+ files?: (id: string) => FileItem | undefined;
16
19
  edited?: boolean;
17
20
  deleted?: boolean;
18
21
  highlighted?: boolean;
@@ -30,6 +33,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
30
33
  time: string;
31
34
  avatar: string;
32
35
  mentions: MentionCandidate[];
36
+ files: (id: string) => FileItem | undefined;
33
37
  authorId: string;
34
38
  datetime: string;
35
39
  edited: boolean;
@@ -18,11 +18,11 @@ type __VLS_Props = {
18
18
  disabled?: boolean;
19
19
  };
20
20
  declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
21
- copied: (text: string) => any;
22
21
  failed: (text: string) => any;
22
+ copied: (text: string) => any;
23
23
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
24
- onCopied?: ((text: string) => any) | undefined;
25
24
  onFailed?: ((text: string) => any) | undefined;
25
+ onCopied?: ((text: string) => any) | undefined;
26
26
  }>, {
27
27
  label: string;
28
28
  size: "sm" | "md";
@@ -15,17 +15,19 @@ type __VLS_Props = {
15
15
  directory?: boolean;
16
16
  showList?: boolean;
17
17
  variant?: 'dropzone' | 'compact';
18
+ /** Takes files pasted while the picker has focus, and adds a Paste button where the browser can read the clipboard (an iPhone has no keyboard to paste with). */
19
+ paste?: boolean;
18
20
  locale?: UiLocale;
19
21
  text?: Partial<FilePickerText>;
20
22
  title?: string;
21
23
  description?: string;
22
24
  };
23
- declare var __VLS_27: {
25
+ declare var __VLS_53: {
24
26
  file: File;
25
27
  index: number;
26
28
  };
27
29
  type __VLS_Slots = {} & {
28
- 'file-actions'?: (props: typeof __VLS_27) => any;
30
+ 'file-actions'?: (props: typeof __VLS_53) => any;
29
31
  };
30
32
  declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
31
33
  "update:modelValue": (files: File[]) => any;
@@ -38,6 +40,7 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
38
40
  disabled: boolean;
39
41
  modelValue: File[];
40
42
  multiple: boolean;
43
+ paste: boolean;
41
44
  maxFiles: number;
42
45
  maxSize: number;
43
46
  directory: boolean;
@@ -0,0 +1,24 @@
1
+ import { FileItem } from '../../../lib/files';
2
+ type __VLS_Props = {
3
+ items: FileItem[];
4
+ /** Thumbnails shown before "+N"; 0 shows them all. */
5
+ max?: number;
6
+ /** `sm` (28px) fits a table row; `md` (64px) a form or a comment box. */
7
+ size?: 'sm' | 'md';
8
+ removable?: boolean;
9
+ lightbox?: boolean;
10
+ };
11
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
12
+ open: (index: number, item: FileItem) => any;
13
+ remove: (item: FileItem) => any;
14
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
15
+ onOpen?: ((index: number, item: FileItem) => any) | undefined;
16
+ onRemove?: ((item: FileItem) => any) | undefined;
17
+ }>, {
18
+ size: "sm" | "md";
19
+ max: number;
20
+ removable: boolean;
21
+ lightbox: boolean;
22
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
23
+ declare const _default: typeof __VLS_export;
24
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { FileItem } from '../../../lib/files';
2
+ type __VLS_Props = {
3
+ items: FileItem[];
4
+ open?: boolean;
5
+ /** The file shown, from 0. */
6
+ index?: number;
7
+ };
8
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
9
+ "update:open": (open: boolean) => any;
10
+ "update:index": (index: number) => any;
11
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
12
+ "onUpdate:open"?: ((open: boolean) => any) | undefined;
13
+ "onUpdate:index"?: ((index: number) => any) | undefined;
14
+ }>, {
15
+ open: boolean;
16
+ index: number;
17
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,31 @@
1
+ import { MentionCandidate } from '../../../lib/mentions';
2
+ type __VLS_Props = {
3
+ open: boolean;
4
+ /** Where the list opens, such as the caret's box. */
5
+ reference: {
6
+ getBoundingClientRect: () => DOMRect;
7
+ } | null;
8
+ people: readonly MentionCandidate[];
9
+ active?: number;
10
+ /** The listbox id; each option is `${id}-${index}`. */
11
+ id: string;
12
+ /** Shows "Loading…" while the list is empty. */
13
+ loading?: boolean;
14
+ /** An element inside the app, which keeps the list within the app's window. */
15
+ boundary?: HTMLElement;
16
+ };
17
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
18
+ choose: (person: MentionCandidate) => any;
19
+ close: () => any;
20
+ hover: (index: number) => any;
21
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onChoose?: ((person: MentionCandidate) => any) | undefined;
23
+ onClose?: (() => any) | undefined;
24
+ onHover?: ((index: number) => any) | undefined;
25
+ }>, {
26
+ loading: boolean;
27
+ active: number;
28
+ boundary: HTMLElement;
29
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
30
+ declare const _default: typeof __VLS_export;
31
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { RichTextBlock } from '../../../lib/richText';
2
+ import { FileItem } from '../../../lib/files';
3
+ type __VLS_Props = {
4
+ blocks: RichTextBlock[];
5
+ file: (id: string, alt: string) => FileItem;
6
+ imageHeight: number;
7
+ };
8
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
9
+ open: (id: string) => any;
10
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
11
+ onOpen?: ((id: string) => any) | undefined;
12
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { FileItem } from '../../../lib/files';
2
+ type __VLS_Props = {
3
+ item: FileItem;
4
+ height: number;
5
+ };
6
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
7
+ open: () => any;
8
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
9
+ onOpen?: (() => any) | undefined;
10
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
11
+ declare const _default: typeof __VLS_export;
12
+ export default _default;
@@ -0,0 +1,23 @@
1
+ import { RichTextInline } from '../../../lib/richText';
2
+ type __VLS_Props = {
3
+ parts: RichTextInline[];
4
+ };
5
+ declare var __VLS_1: {
6
+ image: {
7
+ type: "image";
8
+ id: string;
9
+ alt: string;
10
+ };
11
+ };
12
+ type __VLS_Slots = {} & {
13
+ image?: (props: typeof __VLS_1) => any;
14
+ };
15
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
16
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
17
+ declare const _default: typeof __VLS_export;
18
+ export default _default;
19
+ type __VLS_WithSlots<T, S> = T & {
20
+ new (): {
21
+ $slots: S;
22
+ };
23
+ };
@@ -0,0 +1,28 @@
1
+ import { MentionCandidate } from '../../../lib/mentions';
2
+ import { FileItem } from '../../../lib/files';
3
+ type __VLS_Props = {
4
+ body?: string;
5
+ /** Names for the mention tokens; an unknown id shows as itself. */
6
+ mentions?: MentionCandidate[];
7
+ /** Reads `## ` and `### ` as headings, as a rich text field does. */
8
+ headings?: boolean;
9
+ /** The file behind an image's id; an unknown id shows as missing. */
10
+ files?: (id: string) => FileItem | undefined;
11
+ /** Tallest an image is drawn, in px, before it opens full size. */
12
+ imageHeight?: number;
13
+ lightbox?: boolean;
14
+ };
15
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
16
+ "open-image": (id: string, index: number) => any;
17
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
18
+ "onOpen-image"?: ((id: string, index: number) => any) | undefined;
19
+ }>, {
20
+ body: string;
21
+ imageHeight: number;
22
+ mentions: MentionCandidate[];
23
+ headings: boolean;
24
+ files: (id: string) => FileItem | undefined;
25
+ lightbox: boolean;
26
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
27
+ declare const _default: typeof __VLS_export;
28
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import { UploadStatus } from '../../../lib/uploadQueue';
2
+ export type UploadQueueItem = {
3
+ key: string;
4
+ name: string;
5
+ size: number;
6
+ loaded: number;
7
+ status: UploadStatus;
8
+ error?: string;
9
+ };
10
+ type __VLS_Props = {
11
+ tasks: UploadQueueItem[];
12
+ /** Files the server is still making thumbnails for, added to the summary. */
13
+ processing?: number;
14
+ collapsible?: boolean;
15
+ /** Shows a close button once nothing waits or sends. */
16
+ closable?: boolean;
17
+ };
18
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
19
+ cancel: (key: string) => any;
20
+ close: () => any;
21
+ retry: (key: string) => any;
22
+ retryAll: () => any;
23
+ cancelAll: () => any;
24
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
25
+ onCancel?: ((key: string) => any) | undefined;
26
+ onClose?: (() => any) | undefined;
27
+ onRetry?: ((key: string) => any) | undefined;
28
+ onRetryAll?: (() => any) | undefined;
29
+ onCancelAll?: (() => any) | undefined;
30
+ }>, {
31
+ collapsible: boolean;
32
+ closable: boolean;
33
+ processing: number;
34
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
35
+ declare const _default: typeof __VLS_export;
36
+ export default _default;
@@ -11,11 +11,11 @@ declare const __VLS_base: import('vue').DefineComponent<WizardStepProps, {
11
11
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<WizardStepProps> & Readonly<{}>, {
12
12
  footer: boolean;
13
13
  type: import('./types').WizardStepType;
14
+ lazy: boolean;
14
15
  headline: string;
15
16
  nextDisabled: boolean;
16
17
  showNext: boolean;
17
18
  showBack: boolean;
18
- lazy: boolean;
19
19
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
20
20
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
21
21
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,83 @@
1
+ import { FileItem, MentionCandidate, UploadFunction } from '../../index';
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
4
+ declare const __VLS_export: import('vue').DefineComponent<{
5
+ modelValue?: string;
6
+ variant?: "compact" | "full";
7
+ /** `## ` and `### ` headings; on by default in `full` only. */
8
+ headings?: boolean;
9
+ placeholder?: string;
10
+ disabled?: boolean;
11
+ invalid?: boolean;
12
+ id?: string;
13
+ ariaLabel?: string;
14
+ /** Characters the stored body may hold; the count shows from 90% and over, and a body over it cannot be sent. */
15
+ maxlength?: number;
16
+ /** Images one body may hold: 20 in `compact`, 50 in `full` by default. */
17
+ maxImages?: number;
18
+ /** Sends one file and resolves with its stored id; without it the box takes no images. */
19
+ upload?: UploadFunction<string>;
20
+ /** The file behind a stored image's id. */
21
+ files?: (id: string) => FileItem | undefined;
22
+ /** People to offer for the text typed after `@`. */
23
+ candidates?: MentionCandidate[];
24
+ /** Narrows `candidates` by the query here; turn off when the server already did. */
25
+ filter?: boolean;
26
+ /** Shows "Loading…" in the list of people while the app fetches. */
27
+ loading?: boolean;
28
+ /** `compact` only: the send button shows a spinner and the box cannot send again. */
29
+ sending?: boolean;
30
+ }, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
31
+ search: (query: string) => any;
32
+ submit: (body: string) => any;
33
+ pending: (count: number) => any;
34
+ "update:modelValue": (body: string) => any;
35
+ }, string, import('vue').PublicProps, Readonly<{
36
+ modelValue?: string;
37
+ variant?: "compact" | "full";
38
+ /** `## ` and `### ` headings; on by default in `full` only. */
39
+ headings?: boolean;
40
+ placeholder?: string;
41
+ disabled?: boolean;
42
+ invalid?: boolean;
43
+ id?: string;
44
+ ariaLabel?: string;
45
+ /** Characters the stored body may hold; the count shows from 90% and over, and a body over it cannot be sent. */
46
+ maxlength?: number;
47
+ /** Images one body may hold: 20 in `compact`, 50 in `full` by default. */
48
+ maxImages?: number;
49
+ /** Sends one file and resolves with its stored id; without it the box takes no images. */
50
+ upload?: UploadFunction<string>;
51
+ /** The file behind a stored image's id. */
52
+ files?: (id: string) => FileItem | undefined;
53
+ /** People to offer for the text typed after `@`. */
54
+ candidates?: MentionCandidate[];
55
+ /** Narrows `candidates` by the query here; turn off when the server already did. */
56
+ filter?: boolean;
57
+ /** Shows "Loading…" in the list of people while the app fetches. */
58
+ loading?: boolean;
59
+ /** `compact` only: the send button shows a spinner and the box cannot send again. */
60
+ sending?: boolean;
61
+ }> & Readonly<{
62
+ onSearch?: ((query: string) => any) | undefined;
63
+ onSubmit?: ((body: string) => any) | undefined;
64
+ onPending?: ((count: number) => any) | undefined;
65
+ "onUpdate:modelValue"?: ((body: string) => any) | undefined;
66
+ }>, {
67
+ filter: boolean;
68
+ variant: "compact" | "full";
69
+ loading: boolean;
70
+ disabled: boolean;
71
+ id: string;
72
+ modelValue: string;
73
+ placeholder: string;
74
+ invalid: boolean;
75
+ ariaLabel: string;
76
+ headings: boolean;
77
+ files: (id: string) => FileItem | undefined;
78
+ upload: UploadFunction<string>;
79
+ maxlength: number;
80
+ maxImages: number;
81
+ candidates: MentionCandidate[];
82
+ sending: boolean;
83
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
@@ -0,0 +1,30 @@
1
+ import { Node, AnyExtension } from '@tiptap/core';
2
+ import { SuggestionOptions } from '@tiptap/suggestion';
3
+ import { FileItem, MentionCandidate } from '../../index';
4
+ export type ImageView = {
5
+ /** The file behind a stored image's id. */
6
+ file: (id: string) => FileItem | undefined;
7
+ labels: {
8
+ uploading: string;
9
+ failed: string;
10
+ missing: string;
11
+ };
12
+ };
13
+ /**
14
+ * An image of a stored file, a block of its own. `id` is the stored file;
15
+ * `upload` names an image still being sent, drawn from the local `src`, with
16
+ * its `progress` from 0 to 1, or -1 once it failed.
17
+ */
18
+ export declare function fileImage(view: ImageView): Node<any, any>;
19
+ export type ExtensionOptions = {
20
+ headings: boolean;
21
+ placeholder: string;
22
+ image: ImageView;
23
+ mention: Omit<SuggestionOptions<MentionCandidate>, 'editor' | 'char'>;
24
+ /** Ctrl+Enter or Cmd+Enter; returns whether it was taken. Never while an input method composes. */
25
+ submit?: () => boolean;
26
+ /** Ctrl+K or Cmd+K. */
27
+ link: () => boolean;
28
+ escape: () => boolean;
29
+ };
30
+ export declare function extensions(options: ExtensionOptions): AnyExtension[];
@@ -0,0 +1,22 @@
1
+ import { JSONContent } from '@tiptap/core';
2
+ import { MentionCandidate } from '../../index';
3
+ export type MarkdownOptions = {
4
+ people?: readonly MentionCandidate[];
5
+ headings?: boolean;
6
+ /** Leaves images out of the document and returns their ids, for a comment box that lists them under the text. */
7
+ imagesApart?: boolean;
8
+ };
9
+ /** A stored body → the editor's document, and the file ids of its images when `imagesApart`. */
10
+ export declare function markdownToDoc(body: string, options?: MarkdownOptions): {
11
+ doc: JSONContent;
12
+ images: {
13
+ id: string;
14
+ alt: string;
15
+ }[];
16
+ };
17
+ export type SerializeOptions = {
18
+ /** Between top-level blocks: a blank line by default; a comment box uses a single line break. */
19
+ blockSeparator?: string;
20
+ };
21
+ /** The editor's document → the stored body. Images still uploading (no `id`) are left out. */
22
+ export declare function docToMarkdown(doc: JSONContent, options?: SerializeOptions): string;
@@ -20,5 +20,8 @@ export type FilePickerText = {
20
20
  sizeRejected: string;
21
21
  countRejected: string;
22
22
  duplicateRejected: string;
23
+ /** With `paste`: the button that pastes from the clipboard, and what it says when the clipboard holds no file. */
24
+ paste: string;
25
+ pasteEmpty: string;
23
26
  };
24
27
  export declare const filePickerText: Record<UiLocale, FilePickerText>;
@@ -1,5 +1,53 @@
1
1
  import { UiLocale } from './locale';
2
2
  export declare const englishUiText: {
3
+ readonly "{count} failed": "{count} failed";
4
+ readonly "{name} is not a JPEG, PNG, WebP or GIF image": "{name} is not a JPEG, PNG, WebP or GIF image";
5
+ readonly "{name}: {kind} images are not supported; on an iPhone, set Settings > Camera > Formats to Most Compatible": "{name}: {kind} images are not supported; on an iPhone, set Settings > Camera > Formats to Most Compatible";
6
+ readonly "You can add up to {count} images": "You can add up to {count} images";
7
+ readonly "Add image": "Add image";
8
+ readonly Bold: "Bold";
9
+ readonly "Bulleted list": "Bulleted list";
10
+ readonly "Can't show this file": "Can't show this file";
11
+ readonly "Cancel {name}": "Cancel {name}";
12
+ readonly "Cancel all": "Cancel all";
13
+ readonly "Code block": "Code block";
14
+ readonly Code: "Code";
15
+ readonly "File has changed": "File has changed";
16
+ readonly "File is missing": "File is missing";
17
+ readonly Formatting: "Formatting";
18
+ readonly Heading: "Heading";
19
+ readonly "Hide uploads": "Hide uploads";
20
+ readonly "Images from other websites are not pasted; save them first and add them as files": "Images from other websites are not pasted; save them first and add them as files";
21
+ readonly Images: "Images";
22
+ readonly Italic: "Italic";
23
+ readonly "Link address": "Link address";
24
+ readonly Link: "Link";
25
+ readonly "Links start with http://, https:// or mailto:": "Links start with http://, https:// or mailto:";
26
+ readonly "Mention someone": "Mention someone";
27
+ readonly "Next file": "Next file";
28
+ readonly "Numbered list": "Numbered list";
29
+ readonly "Open {name}": "Open {name}";
30
+ readonly "Previous file": "Previous file";
31
+ readonly "Processing {count}": "Processing {count}";
32
+ readonly Processing: "Processing";
33
+ readonly Quote: "Quote";
34
+ readonly "Remove link": "Remove link";
35
+ readonly "Retry {name}": "Retry {name}";
36
+ readonly "Retry all": "Retry all";
37
+ readonly Send: "Send";
38
+ readonly Sending: "Sending";
39
+ readonly "Show all {count} files": "Show all {count} files";
40
+ readonly "Show uploads": "Show uploads";
41
+ readonly Strikethrough: "Strikethrough";
42
+ readonly Subheading: "Subheading";
43
+ readonly "Upload failed": "Upload failed";
44
+ readonly "Uploaded {done} of {total}": "Uploaded {done} of {total}";
45
+ readonly Uploading: "Uploading";
46
+ readonly Uploads: "Uploads";
47
+ readonly "Wait for the images to finish uploading": "Wait for the images to finish uploading";
48
+ readonly Waiting: "Waiting";
49
+ readonly Uploaded: "Uploaded";
50
+ readonly Canceled: "Canceled";
3
51
  readonly "Opens in a new tab": "Opens in a new tab";
4
52
  readonly Download: "Download";
5
53
  readonly Close: "Close";
package/dist/index.d.ts CHANGED
@@ -131,12 +131,21 @@ export { default as StatTile } from './components/composite/stat-tile/StatTile.v
131
131
  export type { StatTileTone } from './components/composite/stat-tile/StatTile.vue';
132
132
  export { default as StatTileGroup } from './components/composite/stat-tile/StatTileGroup.vue';
133
133
  export { default as CompletionToggle } from './components/composite/completion-toggle/CompletionToggle.vue';
134
- export { default as MentionTextarea } from './components/composite/mention-textarea/MentionTextarea.vue';
135
134
  export { default as CommentItem } from './components/composite/comment-item/CommentItem.vue';
136
- export { mentionParts, mentionText, mentionTokens } from './lib/mentions';
135
+ export { mentionParts } from './lib/mentions';
136
+ export { default as MentionList } from './components/composite/mention-list/MentionList.vue';
137
137
  export type { MentionCandidate, MentionPart } from './lib/mentions';
138
- export { commentMarkdownParts, commentMarkdownText } from './lib/commentMarkdown';
139
- export type { CommentBlock, CommentInline } from './lib/commentMarkdown';
138
+ export { richTextParts, richTextText, richTextFileIds } from './lib/richText';
139
+ export type { RichTextBlock, RichTextInline, RichTextList, RichTextListItem, RichTextOptions } from './lib/richText';
140
+ export { default as RichTextView } from './components/composite/rich-text-view/RichTextView.vue';
141
+ export { default as FileThumbs } from './components/composite/file-thumbs/FileThumbs.vue';
142
+ export { default as ImageLightbox } from './components/composite/image-lightbox/ImageLightbox.vue';
143
+ export { default as UploadQueue } from './components/composite/upload-queue/UploadQueue.vue';
144
+ export type { UploadQueueItem } from './components/composite/upload-queue/UploadQueue.vue';
145
+ export { useUploadQueue } from './lib/uploadQueue';
146
+ export type { UploadFunction, UploadQueueOptions, UploadStatus, UploadTask } from './lib/uploadQueue';
147
+ export { isPicture, isUnsupportedPicture } from './lib/files';
148
+ export type { FileItem, FileState } from './lib/files';
140
149
  export type { DataTableColumn } from './components/composite/data-table/DataTable.vue';
141
150
  export { default as DataGrid } from './components/composite/data-grid/DataGrid.vue';
142
151
  export type { DataGridColumn, DataGridColumnWidths, DataGridLabels, ResolvedDataGridLabels, DataGridSort, DataGridGroup, DataGridExpandable, DataGridDraggable, DataGridReorder, DataGridCellUpdate, DataGridAdapter, DataGridCellSlotProps } from './components/composite/data-grid/DataGrid.vue';
@@ -246,6 +255,8 @@ export { default as OverflowToolbar } from './components/composite/overflow-tool
246
255
  export { computeOverflowFit, ITEM_GAP } from './components/composite/overflow-toolbar/overflow';
247
256
  export type { OverflowToolbarItem, OverflowToolbarMenuEntry, OverflowToolbarMoreMenu, OverflowToolbarMoreState, OverflowToolbarMoreText, } from './components/composite/overflow-toolbar/types';
248
257
  export type { UiLocale } from './i18n/locale';
258
+ export { useUiText } from './i18n/text';
259
+ export type { UiTextKey } from './i18n/text';
249
260
  export { filePickerText } from './i18n/filePicker';
250
261
  export type { FilePickerText } from './i18n/filePicker';
251
262
  export { default as Link } from './components/primitives/link/Link.vue';