@rebasepro/cms 0.22.0 → 0.24.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 (90) hide show
  1. package/dist/{CollectionEditorDialog-CUtfS_49.js → CollectionEditorDialog-p5-I-Apu.js} +181 -97
  2. package/dist/CollectionEditorDialog-p5-I-Apu.js.map +1 -0
  3. package/dist/{PropertyEditView-BpEbC0Q2.js → PropertyEditView-DDB_X-00.js} +55 -23
  4. package/dist/PropertyEditView-DDB_X-00.js.map +1 -0
  5. package/dist/{RouterCollectionsStudioView-EK8Knq5I.js → RouterCollectionsStudioView-SGBV4CMg.js} +34 -10
  6. package/dist/RouterCollectionsStudioView-SGBV4CMg.js.map +1 -0
  7. package/dist/{SchemaChangeDialog-BeP0A4MJ.js → SchemaChangeDialog-CcFSXvkK.js} +33 -12
  8. package/dist/SchemaChangeDialog-CcFSXvkK.js.map +1 -0
  9. package/dist/collection_editor/_cms_internals.d.ts +3 -1
  10. package/dist/collection_editor/liveSchemaClient.d.ts +38 -18
  11. package/dist/collection_editor/relation_kinds.d.ts +11 -0
  12. package/dist/collection_editor/serializable_types.d.ts +2 -0
  13. package/dist/collection_editor/types/config_controller.d.ts +11 -0
  14. package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +8 -1
  15. package/dist/collection_editor/useLiveSchemaEditing.d.ts +5 -5
  16. package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +7 -0
  17. package/dist/collection_editor_ui.js +5 -5
  18. package/dist/components/ClearFilterSortButton.d.ts +7 -1
  19. package/dist/components/CollectionTableBinding/fields/CompactEntityCellField.d.ts +12 -13
  20. package/dist/components/CollectionTableBinding/fields/TableMultipleRelationField.d.ts +6 -3
  21. package/dist/components/CollectionTableBinding/fields/TableReferenceField.d.ts +8 -1
  22. package/dist/components/CollectionTableBinding/fields/TableRelationField.d.ts +8 -1
  23. package/dist/components/CollectionTableBinding/fields/TableRelationSelectorField.d.ts +13 -3
  24. package/dist/components/CollectionTableBinding/fields/VirtualTableSelect.d.ts +10 -0
  25. package/dist/components/CollectionTableBinding/fields/VirtualTableUserSelect.d.ts +10 -0
  26. package/dist/components/CollectionTableBinding/internal/EntityTableCell.d.ts +21 -0
  27. package/dist/components/CollectionTableBinding/internal/TableCellOpener.d.ts +45 -0
  28. package/dist/components/CollectionTableBinding/table_bindings.d.ts +24 -0
  29. package/dist/components/CollectionViewBinding/CollectionViewActions.d.ts +6 -1
  30. package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +12 -3
  31. package/dist/components/CollectionViewBinding/FiltersDialog.d.ts +5 -0
  32. package/dist/components/EditViewBinding.d.ts +7 -1
  33. package/dist/components/EntityIdentityBar.d.ts +29 -4
  34. package/dist/components/EntityPreviewBinding.d.ts +8 -1
  35. package/dist/components/EntityPreviewNesting.d.ts +15 -0
  36. package/dist/components/NavigationLoadingErrorView.d.ts +16 -0
  37. package/dist/components/PickerTrigger.d.ts +17 -0
  38. package/dist/components/ReferenceTable/SelectionTableBinding.d.ts +12 -0
  39. package/dist/components/RelationSelector.d.ts +24 -7
  40. package/dist/components/UserSelector.d.ts +8 -1
  41. package/dist/components/common/ActionMenuLabel.d.ts +15 -0
  42. package/dist/components/common/default_entity_actions.d.ts +37 -0
  43. package/dist/data_export/export/additional_fields.d.ts +13 -0
  44. package/dist/data_export/export/export.d.ts +6 -0
  45. package/dist/data_import/components/ImportConversionProblems.d.ts +11 -0
  46. package/dist/data_import/components/index.d.ts +1 -0
  47. package/dist/data_import/utils/csv.d.ts +3 -2
  48. package/dist/data_import/utils/data.d.ts +81 -4
  49. package/dist/data_import/utils/id_column.d.ts +16 -0
  50. package/dist/data_import/utils/import_columns.d.ts +48 -0
  51. package/dist/editor/editor.d.ts +7 -1
  52. package/dist/editor/extensions/Image.d.ts +22 -1
  53. package/dist/editor/hooks/useProseMirror.d.ts +4 -1
  54. package/dist/editor.js +69 -7
  55. package/dist/editor.js.map +1 -1
  56. package/dist/{export-LvIpE7sE.js → export-DCHuPD1a.js} +65 -211
  57. package/dist/export-DCHuPD1a.js.map +1 -0
  58. package/dist/form/form_utils.d.ts +37 -0
  59. package/dist/form/resolve_form_properties.d.ts +26 -0
  60. package/dist/form/validation.d.ts +13 -0
  61. package/dist/{history-DG6Ln7CV.js → history-CBd6SBRs.js} +3 -2
  62. package/dist/{history-DG6Ln7CV.js.map → history-CBd6SBRs.js.map} +1 -1
  63. package/dist/hooks/useEntityDisplay.d.ts +4 -5
  64. package/dist/hooks/useRecordActions.d.ts +28 -6
  65. package/dist/hooks/useReferencePaths.d.ts +21 -0
  66. package/dist/hooks/useStorageReferenceResolver.d.ts +23 -0
  67. package/dist/{import-Cxnmpyjl.js → import-DjQoRrIU.js} +35 -54
  68. package/dist/import-DjQoRrIU.js.map +1 -0
  69. package/dist/index.js +191 -117
  70. package/dist/index.js.map +1 -1
  71. package/dist/preview/components/StorageMarkdown.d.ts +10 -0
  72. package/dist/preview/util.d.ts +29 -0
  73. package/dist/util/caller_scopes.d.ts +15 -0
  74. package/dist/util/entity_actions.d.ts +37 -1
  75. package/dist/util/fit_inline_actions.d.ts +20 -0
  76. package/dist/util/navigation_utils.d.ts +14 -2
  77. package/dist/util/view_mode.d.ts +12 -18
  78. package/dist/{util-CyIcgnv2.js → util-BRvKFMLa.js} +5775 -3665
  79. package/dist/util-BRvKFMLa.js.map +1 -0
  80. package/dist/{util-DB31IoJE.js → util-D7ebttKT.js} +39 -2
  81. package/dist/util-D7ebttKT.js.map +1 -0
  82. package/package.json +9 -9
  83. package/dist/CollectionEditorDialog-CUtfS_49.js.map +0 -1
  84. package/dist/PropertyEditView-BpEbC0Q2.js.map +0 -1
  85. package/dist/RouterCollectionsStudioView-EK8Knq5I.js.map +0 -1
  86. package/dist/SchemaChangeDialog-BeP0A4MJ.js.map +0 -1
  87. package/dist/export-LvIpE7sE.js.map +0 -1
  88. package/dist/import-Cxnmpyjl.js.map +0 -1
  89. package/dist/util-CyIcgnv2.js.map +0 -1
  90. package/dist/util-DB31IoJE.js.map +0 -1
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { ImportConversionProblem } from "../utils/data.js";
3
+ /**
4
+ * The cells of an import that do not convert to the type of the field they
5
+ * map to — per column, how many, and the first few with their row — shown in
6
+ * the preview, before anything is written. Those cells are left out of the
7
+ * rows; nothing is turned into `null`, `0` or `false` without saying so.
8
+ */
9
+ export declare function ImportConversionProblems({ problems }: {
10
+ problems: ImportConversionProblem[];
11
+ }): React.JSX.Element | null;
@@ -2,3 +2,4 @@ export * from "./DataNewPropertiesMapping.js";
2
2
  export * from "./ImportFileUpload.js";
3
3
  export * from "./ImportNewPropertyFieldPreview.js";
4
4
  export * from "./ImportSaveInProgress.js";
5
+ export * from "./ImportConversionProblems.js";
@@ -2,8 +2,9 @@
2
2
  * Split CSV text into rows of raw cells, RFC 4180 style.
3
3
  *
4
4
  * Quoted fields may contain the delimiter, newlines and doubled quotes; this is
5
- * the exact inverse of what the export's `entryToCSVRow` writes, so a file this
6
- * admin produced reads back into the cells it was built from.
5
+ * the exact inverse of the quoting the export's `entryToCSVRow` writes, and
6
+ * `parseCsvToObjects` undoes its formula escape, so a file this admin produced
7
+ * reads back into the cells it was built from.
7
8
  */
8
9
  export declare function parseCsvRows(text: string, delimiter?: string): string[][];
9
10
  /**
@@ -1,5 +1,82 @@
1
- import { Entity, CollectionRegistryController, Properties, Property } from "@rebasepro/types";
1
+ import { DataType, Entity, CollectionRegistryController, Properties, Property } from "@rebasepro/types";
2
2
  import { AuthController } from "@rebasepro/cms-types";
3
- export declare function convertDataToEntity(authController: AuthController, navigation: CollectionRegistryController, data: Record<any, any>, idColumn: string | undefined, headersMapping: Record<string, string | null>, properties: Properties, path: string, defaultValues: Record<string, any>): Entity<any>;
4
- export declare function flattenEntry(obj: Record<string, unknown>, parent?: string): Record<string, unknown>;
5
- export declare function processValueMapping(authController: AuthController, value: any, navigation: CollectionRegistryController, property?: Property): any;
3
+ import { ColumnTarget } from "./import_columns.js";
4
+ /** What the conversion asks of the collection registry: the collection a reference points into. */
5
+ export type ImportCollectionLookup = Pick<CollectionRegistryController, "getCollection">;
6
+ /**
7
+ * Why a cell could not be converted to its property's type. Each one is a
8
+ * translation key under `import_problem_`.
9
+ */
10
+ export type ImportProblemReason = "not_a_number" | "number_leading_zero" | "number_too_precise" | "not_a_boolean" | "not_a_date" | "ambiguous_date" | "not_a_vector" | "not_a_map" | "not_a_geopoint";
11
+ /**
12
+ * A cell the import could not convert to the type of the property it maps
13
+ * to. It is left out of the row — the property's default applies — and shown
14
+ * to the person importing before anything is written.
15
+ */
16
+ export type ImportConversionProblem = {
17
+ /** The row's index among the file's data rows, from 0. */
18
+ row: number;
19
+ /** The column, as the file names it. */
20
+ column: string;
21
+ /** The property the column maps to. */
22
+ property: string;
23
+ /** The type that property holds. */
24
+ expected: DataType;
25
+ /** The cell, as it was read from the file. */
26
+ value: unknown;
27
+ reason: ImportProblemReason;
28
+ };
29
+ /** Reports one cell that did not convert; the cell is then left out. */
30
+ export type ImportProblemListener = (problem: {
31
+ reason: ImportProblemReason;
32
+ }) => void;
33
+ /**
34
+ * How a column writes the dates whose day and month are both numbers
35
+ * (`05/01/2024`): day first, month first, or — when the column holds both —
36
+ * each date by the only order that can read it.
37
+ */
38
+ export type SlashDateOrder = "dmy" | "mdy" | "each";
39
+ /**
40
+ * Convert every row of an imported file, and list each cell that did not
41
+ * convert. The previews call this, so the list is in front of the person
42
+ * importing before the first row is written.
43
+ */
44
+ export declare function convertImportData(authController: AuthController, navigation: ImportCollectionLookup, data: object[], idColumn: string | undefined, headersMapping: Record<string, string | null>, properties: Properties, path: string, defaultValues: Record<string, any>): {
45
+ entities: Entity<any>[];
46
+ problems: ImportConversionProblem[];
47
+ };
48
+ export declare function convertDataToEntity(authController: AuthController, navigation: ImportCollectionLookup, data: Record<any, any>, idColumn: string | undefined, headersMapping: Record<string, string | null>, properties: Properties, path: string, defaultValues: Record<string, any>, onProblem?: (problem: Omit<ImportConversionProblem, "row">) => void, dateOrders?: Record<string, SlashDateOrder | undefined>): Entity<any>;
49
+ /**
50
+ * Where a column lands under the import's mapping: the property the mapping
51
+ * names for it, `null` for "Do not import this property", and the property
52
+ * with the column's own name when the mapping does not mention the column.
53
+ */
54
+ export declare function mappedColumnTarget(headersMapping: Record<string, string | null>): ColumnTarget;
55
+ /**
56
+ * The cells of one imported row by column name (`address.street`): each
57
+ * value that is not split into columns of its own (see `importColumns`).
58
+ * Without `properties`, every object is split.
59
+ */
60
+ export declare function flattenEntry(obj: Record<string, unknown>, properties?: Properties, targetOf?: ColumnTarget): Record<string, unknown>;
61
+ /**
62
+ * Convert one cell to the type of the property it lands in.
63
+ *
64
+ * A cell is converted only when the property's type can hold what it says:
65
+ * text becomes a number when it spells one exactly (`readNumberExactly`, the
66
+ * same answer the inference uses), a boolean when it says yes or no, a date
67
+ * when it names a day. Anything else is reported to `onProblem`, and the
68
+ * result is `null` — the caller leaves such a cell out rather than writing
69
+ * that `null`, a `0` or a `false` in its place.
70
+ *
71
+ * @param dateOrder how the cell's column writes day/month dates, when it
72
+ * writes them with numbers only (see `slashDateOrdersByColumn`).
73
+ */
74
+ export declare function processValueMapping(authController: AuthController, value: any, navigation: ImportCollectionLookup, property?: Property, onProblem?: ImportProblemListener, dateOrder?: SlashDateOrder): any;
75
+ /**
76
+ * For each column of the file, how it writes day/month dates with numbers
77
+ * only, when its values say: a first number past 12 is a day, so the column
78
+ * is day first; a second number past 12, month first; both, and each date
79
+ * is read by the only order that fits it. A column that never says is read
80
+ * in the order of the browser's locale.
81
+ */
82
+ export declare function slashDateOrdersByColumn(data: object[], properties?: Properties, targetOf?: ColumnTarget): Record<string, SlashDateOrder | undefined>;
@@ -0,0 +1,16 @@
1
+ import { Properties } from "@rebasepro/types";
2
+ /**
3
+ * The column an import uses as the row id before the user touches the mapping.
4
+ *
5
+ * The guess decides something destructive: the chosen column is taken out of
6
+ * the values and sent as the id of an upsert, so every row overwrites the record
7
+ * that already has that id. A header that merely *contained* "id" or "key" used
8
+ * to qualify, so a file starting with `width`, `video` or `provider` wrote its
9
+ * widths over the records whose ids they happened to equal.
10
+ *
11
+ * Only the first column is a candidate — the one the export writes the id to —
12
+ * and only when its name says it is the id: the header is `id` (in any case),
13
+ * or it maps onto a property the collection declares with `isId`. Anything else
14
+ * is left for the user to choose in the mapping step.
15
+ */
16
+ export declare function guessIdColumn(headers: string[], headersMapping: Record<string, string | null>, properties?: Properties): string | undefined;
@@ -0,0 +1,48 @@
1
+ import { Properties, Property } from "@rebasepro/types";
2
+ /**
3
+ * Whether a property's value is an object the import takes whole, from one
4
+ * column: a geopoint, a vector, or a map that declares no fields (a key-value
5
+ * map). These are the values the export writes as objects — in a JSON file
6
+ * as the object, in a CSV cell as its JSON — and splitting one into
7
+ * `location.latitude` columns left it in columns no property holds: it was
8
+ * dropped without a word.
9
+ *
10
+ * Any other object is read one column per key: a map with declared fields
11
+ * field by field, as the export writes it, and an object no property takes
12
+ * so that its keys can be mapped one by one.
13
+ */
14
+ export declare function takesObjectWhole(property: Property): boolean;
15
+ /** One column of an imported row, as the mapping step lists it. */
16
+ export type ImportColumn = {
17
+ /** The column's name: a key of the row, or `parent.key` below one. */
18
+ column: string;
19
+ value: unknown;
20
+ /** The property path it lands in, or `null` for none. */
21
+ target: string | null;
22
+ /** Its value is an object that is read as one column per key, listed after it. */
23
+ split: boolean;
24
+ };
25
+ /**
26
+ * Where a column lands: the property path for it, or `null` for none. A
27
+ * column below another is given that parent; a top-level one is not.
28
+ */
29
+ export type ColumnTarget = (column: string, key: string, parent?: Pick<ImportColumn, "column" | "target">) => string | null;
30
+ /**
31
+ * The columns of one imported row, parents before their children.
32
+ *
33
+ * Each key of the row is a column. A key whose value is an object is also
34
+ * read as one column per key of that object (`address.street`) — unless the
35
+ * property it lands in takes the object whole (see {@link takesObjectWhole}).
36
+ *
37
+ * The mapping step lists these columns and the conversion reads them, so both
38
+ * get them from here: a list built one way and rows read another is a mapping
39
+ * whose keys match nothing.
40
+ */
41
+ export declare function importColumns(row: Record<string, unknown>, properties: Properties | undefined, targetOf: ColumnTarget): ImportColumn[];
42
+ /**
43
+ * The mapping the import starts from: each column of the file onto the
44
+ * property with its name, or with its name slugged (`First Name` →
45
+ * `first_name`); a column below a map onto that map's field. A column that
46
+ * matches nothing keeps its own name, for the person importing to map.
47
+ */
48
+ export declare function buildHeadersMappingFromData(rows: object[], properties?: Properties): Record<string, string>;
@@ -12,6 +12,12 @@ export type RichTextEditorProps = {
12
12
  onJsonContentChange?: (content: JSONContent | null) => void;
13
13
  onHtmlContentChange?: (content: string) => void;
14
14
  handleImageUpload: (file: File) => Promise<string>;
15
+ /**
16
+ * Turns an image's stored `src` into a URL to show. `handleImageUpload`
17
+ * returns a `rebase-storage:` reference to keep in the text — a private
18
+ * object's URL expires — and the editor asks this for a URL to display it.
19
+ */
20
+ resolveImageSrc?: (src: string) => Promise<string | null | undefined>;
15
21
  version?: number;
16
22
  textSize?: RichTextEditorTextSize;
17
23
  highlight?: {
@@ -22,4 +28,4 @@ export type RichTextEditorProps = {
22
28
  customComponents?: CustomEditorComponent[];
23
29
  disabled?: boolean;
24
30
  };
25
- export declare const RichTextEditor: ({ content, onJsonContentChange, onHtmlContentChange, onMarkdownContentChange, version, textSize, highlight, handleImageUpload, aiController, disabled }: RichTextEditorProps) => React.JSX.Element;
31
+ export declare const RichTextEditor: ({ content, onJsonContentChange, onHtmlContentChange, onMarkdownContentChange, version, textSize, highlight, handleImageUpload, resolveImageSrc, aiController, disabled }: RichTextEditorProps) => React.JSX.Element;
@@ -1,6 +1,27 @@
1
1
  import { EditorView } from "prosemirror-view";
2
- import { Plugin, PluginKey } from "prosemirror-state";
2
+ import { EditorState, Plugin, PluginKey } from "prosemirror-state";
3
3
  export type UploadFn = (image: File) => Promise<string>;
4
+ /**
5
+ * Turns an image's stored `src` into one a browser can load.
6
+ *
7
+ * An uploaded image is stored as a `rebase-storage:` reference rather than a
8
+ * URL — a private object's URL carries a token that expires in minutes — so
9
+ * the editor asks for a URL each time it shows one.
10
+ */
11
+ export type ImageSrcResolver = (src: string) => Promise<string | null | undefined>;
12
+ /**
13
+ * Carries the resolver to the image node views. They render in their own React
14
+ * roots (see `ReactNodeView`), out of reach of any context, so the editor state
15
+ * is where they can find it.
16
+ */
17
+ export declare const createImageSrcResolverPlugin: (resolve: ImageSrcResolver) => Plugin;
18
+ /** The resolver this editor was given, if any. */
19
+ export declare function imageSrcResolverOf(state: EditorState): ImageSrcResolver | undefined;
20
+ /**
21
+ * The URL to load for a stored `src`: a reference resolved, anything else as
22
+ * it is, and `undefined` for a reference this editor cannot resolve.
23
+ */
24
+ export declare function loadableImageSrc(state: EditorState, src: string): Promise<string | undefined>;
4
25
  export declare function onFileRead(view: EditorView, readerEvent: ProgressEvent<FileReader>, pos: number, upload: UploadFn, image: File): Promise<void>;
5
26
  export declare const ImagePluginKey: PluginKey<any>;
6
27
  export declare const createDropImagePlugin: (upload: UploadFn) => Plugin;
@@ -1,11 +1,14 @@
1
1
  import { EditorState } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
+ import { type ImageSrcResolver } from "../extensions/Image.js";
3
4
  interface UseProseMirrorProps {
4
5
  initialContent?: string | any;
5
6
  editable?: boolean;
6
7
  handleImageUpload?: (file: File) => Promise<string>;
8
+ /** See {@link RichTextEditorProps.resolveImageSrc}. */
9
+ resolveImageSrc?: ImageSrcResolver;
7
10
  }
8
- export declare function useProseMirror({ initialContent, editable, handleImageUpload }: UseProseMirrorProps): {
11
+ export declare function useProseMirror({ initialContent, editable, handleImageUpload, resolveImageSrc }: UseProseMirrorProps): {
9
12
  state: EditorState;
10
13
  view: EditorView | null;
11
14
  editorRef: import("react").RefObject<HTMLDivElement | null>;
package/dist/editor.js CHANGED
@@ -1,9 +1,10 @@
1
- import { r as sanitizeUrl } from "./util-DB31IoJE.js";
1
+ import { o as sanitizeUrl } from "./util-D7ebttKT.js";
2
2
  import { i as schema, n as parser, r as serializer } from "./markdown-CCqp8ibf.js";
3
3
  import React, { createContext, forwardRef, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import { BoldIcon, Button, CheckIcon, CheckSquareIcon, Checkbox, ChevronDownIcon, CodeIcon, Heading1Icon, Heading2Icon, Heading3Icon, IconButton, ImageIcon, ItalicIcon, Label, ListIcon, ListOrderedIcon, Popover, QuoteIcon, Separator, Slot, StrikethroughIcon, TableIcon, TextField, TextareaAutosize, Tooltip, Trash2Icon, TypeIcon, UnderlineIcon, Wand2Icon, cls, defaultBorderMixin, focusedDisabled, iconSize, useInjectStyles } from "@rebasepro/ui";
5
5
  import { useTranslation } from "@rebasepro/app";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { isStorageReference } from "@rebasepro/types";
7
8
  import { EditorState, NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
8
9
  import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
9
10
  import { DOMParser as DOMParser$1, DOMSerializer, Fragment as Fragment$1 } from "prosemirror-model";
@@ -167,6 +168,35 @@ function slashCommandPlugin() {
167
168
  }
168
169
  //#endregion
169
170
  //#region src/editor/extensions/Image.ts
171
+ var ImageSrcResolverKey = new PluginKey("imageSrcResolver");
172
+ /**
173
+ * Carries the resolver to the image node views. They render in their own React
174
+ * roots (see `ReactNodeView`), out of reach of any context, so the editor state
175
+ * is where they can find it.
176
+ */
177
+ var createImageSrcResolverPlugin = (resolve) => new Plugin({
178
+ key: ImageSrcResolverKey,
179
+ state: {
180
+ init: () => resolve,
181
+ apply: (_tr, value) => value
182
+ }
183
+ });
184
+ /** The resolver this editor was given, if any. */
185
+ function imageSrcResolverOf(state) {
186
+ return ImageSrcResolverKey.getState(state);
187
+ }
188
+ /**
189
+ * The URL to load for a stored `src`: a reference resolved, anything else as
190
+ * it is, and `undefined` for a reference this editor cannot resolve.
191
+ */
192
+ async function loadableImageSrc(state, src) {
193
+ if (!isStorageReference(src)) return src;
194
+ try {
195
+ return await imageSrcResolverOf(state)?.(src) ?? void 0;
196
+ } catch {
197
+ return;
198
+ }
199
+ }
170
200
  async function onFileRead(view, readerEvent, pos, upload, image) {
171
201
  const { schema } = view.state;
172
202
  const plugin = view.state.plugins.find((p) => p.spec.key === ImagePluginKey);
@@ -200,8 +230,13 @@ async function onFileRead(view, readerEvent, pos, upload, image) {
200
230
  tr.setMeta(plugin, { decorationSet: currentDecos });
201
231
  view.dispatch(tr);
202
232
  };
233
+ const preloadUrl = await loadableImageSrc(view.state, src);
234
+ if (!preloadUrl) {
235
+ replacePlaceholder();
236
+ return;
237
+ }
203
238
  const preloader = new Image();
204
- preloader.src = src;
239
+ preloader.src = preloadUrl;
205
240
  preloader.onload = replacePlaceholder;
206
241
  preloader.onerror = replacePlaceholder;
207
242
  }
@@ -1244,6 +1279,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1244
1279
  /* @__PURE__ */ jsx(Tooltip, {
1245
1280
  title: t("add_row_before"),
1246
1281
  children: /* @__PURE__ */ jsx(IconButton, {
1282
+ "aria-label": t("add_row_before"),
1247
1283
  size: "small",
1248
1284
  onClick: () => executeCommand(addRowBefore),
1249
1285
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1289,6 +1325,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1289
1325
  /* @__PURE__ */ jsx(Tooltip, {
1290
1326
  title: t("add_row_after"),
1291
1327
  children: /* @__PURE__ */ jsx(IconButton, {
1328
+ "aria-label": t("add_row_after"),
1292
1329
  size: "small",
1293
1330
  onClick: () => executeCommand(addRowAfter),
1294
1331
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1334,6 +1371,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1334
1371
  /* @__PURE__ */ jsx(Tooltip, {
1335
1372
  title: t("delete_row"),
1336
1373
  children: /* @__PURE__ */ jsx(IconButton, {
1374
+ "aria-label": t("delete_row"),
1337
1375
  size: "small",
1338
1376
  onClick: () => executeCommand(deleteRow),
1339
1377
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1378,6 +1416,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1378
1416
  /* @__PURE__ */ jsx(Tooltip, {
1379
1417
  title: t("add_column_before"),
1380
1418
  children: /* @__PURE__ */ jsx(IconButton, {
1419
+ "aria-label": t("add_column_before"),
1381
1420
  size: "small",
1382
1421
  onClick: () => executeCommand(addColumnBefore),
1383
1422
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1423,6 +1462,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1423
1462
  /* @__PURE__ */ jsx(Tooltip, {
1424
1463
  title: t("add_column_after"),
1425
1464
  children: /* @__PURE__ */ jsx(IconButton, {
1465
+ "aria-label": t("add_column_after"),
1426
1466
  size: "small",
1427
1467
  onClick: () => executeCommand(addColumnAfter),
1428
1468
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1468,6 +1508,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1468
1508
  /* @__PURE__ */ jsx(Tooltip, {
1469
1509
  title: t("delete_column"),
1470
1510
  children: /* @__PURE__ */ jsx(IconButton, {
1511
+ "aria-label": t("delete_column"),
1471
1512
  size: "small",
1472
1513
  onClick: () => executeCommand(deleteColumn),
1473
1514
  children: /* @__PURE__ */ jsxs("svg", {
@@ -1509,6 +1550,7 @@ var TableBubble = forwardRef(({ options, className }, ref) => {
1509
1550
  /* @__PURE__ */ jsx(Tooltip, {
1510
1551
  title: t("delete_table"),
1511
1552
  children: /* @__PURE__ */ jsx(IconButton, {
1553
+ "aria-label": t("delete_table"),
1512
1554
  size: "small",
1513
1555
  onClick: () => executeCommand(deleteTable),
1514
1556
  children: /* @__PURE__ */ jsxs("svg", {
@@ -2573,8 +2615,24 @@ var TaskItemComponent = ({ node, view, getPos }) => {
2573
2615
  //#region src/editor/nodeViews/ImageComponent.tsx
2574
2616
  var ImageComponent = ({ node, view, getPos }) => {
2575
2617
  const selected = view.state.selection.from === getPos();
2618
+ const stored = node.attrs.src;
2619
+ const [shown, setShown] = useState(isStorageReference(stored) ? void 0 : stored);
2620
+ useEffect(() => {
2621
+ let live = true;
2622
+ if (!isStorageReference(stored)) {
2623
+ setShown(stored);
2624
+ return;
2625
+ }
2626
+ setShown(void 0);
2627
+ loadableImageSrc(view.state, stored).then((url) => {
2628
+ if (live) setShown(url);
2629
+ });
2630
+ return () => {
2631
+ live = false;
2632
+ };
2633
+ }, [stored, view]);
2576
2634
  return /* @__PURE__ */ jsx("img", {
2577
- src: node.attrs.src,
2635
+ src: shown,
2578
2636
  alt: node.attrs.alt || "",
2579
2637
  title: node.attrs.title || "",
2580
2638
  className: cls("rounded-lg max-w-full !m-0", selected ? "" : "")
@@ -2602,12 +2660,15 @@ var trailingNodePlugin = new Plugin({ appendTransaction: (_, oldState, newState)
2602
2660
  if (doc.lastChild && doc.lastChild.type.name !== "paragraph") return newState.tr.insert(doc.content.size, newState.schema.nodes.paragraph.create());
2603
2661
  return null;
2604
2662
  } });
2605
- function useProseMirror({ initialContent, editable = true, handleImageUpload }) {
2663
+ function useProseMirror({ initialContent, editable = true, handleImageUpload, resolveImageSrc }) {
2664
+ const resolveImageSrcRef = useRef(resolveImageSrc);
2665
+ resolveImageSrcRef.current = resolveImageSrc;
2606
2666
  const plugins = [
2607
2667
  ...getCorePlugins(),
2608
2668
  columnResizing(),
2609
2669
  tableEditing(),
2610
- trailingNodePlugin
2670
+ trailingNodePlugin,
2671
+ createImageSrcResolverPlugin(async (src) => resolveImageSrcRef.current ? resolveImageSrcRef.current(src) : void 0)
2611
2672
  ];
2612
2673
  if (handleImageUpload) plugins.push(createDropImagePlugin(handleImageUpload));
2613
2674
  const defaultState = EditorState.create({
@@ -2687,7 +2748,7 @@ var proseClasses = {
2687
2748
  "base": "prose-base",
2688
2749
  "lg": "prose-lg"
2689
2750
  };
2690
- var RichTextEditor = ({ content, onJsonContentChange, onHtmlContentChange, onMarkdownContentChange, version, textSize = "base", highlight, handleImageUpload, aiController, disabled }) => {
2751
+ var RichTextEditor = ({ content, onJsonContentChange, onHtmlContentChange, onMarkdownContentChange, version, textSize = "base", highlight, handleImageUpload, resolveImageSrc, aiController, disabled }) => {
2691
2752
  const { t } = useTranslation();
2692
2753
  const [openNode, setOpenNode] = useState(false);
2693
2754
  const [openLink, setOpenLink] = useState(false);
@@ -2751,7 +2812,8 @@ var RichTextEditor = ({ content, onJsonContentChange, onHtmlContentChange, onMar
2751
2812
  const { state, view, editorRef } = useProseMirror({
2752
2813
  initialContent: content,
2753
2814
  editable: !disabled,
2754
- handleImageUpload
2815
+ handleImageUpload,
2816
+ resolveImageSrc
2755
2817
  });
2756
2818
  const versionRef = useRef(version);
2757
2819
  useEffect(() => {