@widgetic/editor 4.1.2 → 4.1.4

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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ### WIDGET PROPERTIES EDITOR
2
2
 
3
+ > **Internal building block.** This package is part of Widgetic's own apps and is not a supported integration surface. Partners: use [`@widgetic/ui-sdk`](https://www.npmjs.com/package/@widgetic/ui-sdk) instead.
4
+
3
5
 
4
6
  A Svelte Component Module that will be loaded and used to edit the widget properties: design and content.
5
7
 
@@ -377,6 +379,8 @@ Note that you need to add the right deploy script that copies the dist folder to
377
379
 
378
380
  ## Deploy the component to the npm package registry
379
381
 
382
+ GitLab CI on `main` publishes this package. Bump `version` in `package.json` and push `main`. The job is an npm Trusted Publisher (`NPM_ID_TOKEN`) and skips a version that is already on npm. `npm publish` below is the laptop fallback when CI minutes are gone. The token stays in `~/.npmrc` and is never committed.
383
+
380
384
  ```bash
381
385
  npm publish
382
386
  ```
@@ -30,6 +30,8 @@
30
30
  import { shareTextDirection, type TextDirection } from '@widgetic/design-system/components/textDirection';
31
31
  import { leftOnThisScreen } from '../../utils/dockPlacement';
32
32
  import { provideEditorLanguage } from '../../i18n/language.js';
33
+ import type { PartnerPickFileFn } from '../../pickFileContext.js';
34
+ import type { StoreFileUrlFn } from '../../fileStorageUrlContext.js';
33
35
  import PropsEditor from './PropsEditor.svelte';
34
36
  import WidgetPreview from '../WidgetPreview.svelte';
35
37
  import { setGoogleFontsApiKey } from '../../config';
@@ -74,6 +76,15 @@
74
76
  * library. Without it the files are the ones of the Widgetic library the token can see.
75
77
  */
76
78
  export let mediaLibrary: MediaLibraryProvider | undefined = undefined;
79
+ /** When given, file fields call this instead of opening the built-in File Browser. */
80
+ export let pickFile: PartnerPickFileFn | undefined = undefined;
81
+ /** When given, resolves the url stored with a picked file (partner `mediaLibrary.urlResolver`). */
82
+ export let storeFileUrl: StoreFileUrlFn | undefined = undefined;
83
+ /**
84
+ * When true, dragging or typing a preview size changes the preview on screen but does not change the embed width and
85
+ * height saved with the composition.
86
+ */
87
+ export let lockPreviewSize = false;
77
88
  /** The key the font pickers list Google Fonts with. Without one only the fonts that need no key are listed. */
78
89
  export let googleFontsApiKey = '';
79
90
  /** Wait this long after the last change before saving by itself; `0` turns saving by itself off. Read once, when the editor starts. */
@@ -130,6 +141,7 @@
130
141
  compositionId,
131
142
  api,
132
143
  autosaveDelayMs,
144
+ lockPreviewSize,
133
145
  // The sentences the controller makes (why there is no preview) are made in the language of the moment.
134
146
  translate: (key, values) => get(language.t)(key, values),
135
147
  // The preview page hears about every change as it happens (and everything, once its page has loaded).
@@ -508,6 +520,8 @@
508
520
  initialDesignValues={state.designValues}
509
521
  initialContentValues={propsEditorContentValues}
510
522
  mediaLibrary={mediaLibrary ?? defaultMediaLibrary}
523
+ {pickFile}
524
+ {storeFileUrl}
511
525
  width="{EDITOR_WIDTH_PX}px"
512
526
  height="{floatingEditorHeight}px"
513
527
  showDragHandle={true}
@@ -1,6 +1,8 @@
1
1
  import { type MediaLibraryProvider } from '@widgetic/file-browser';
2
2
  import type { MessageOverrides } from '@widgetic/design-system/i18n';
3
3
  import { type TextDirection } from '@widgetic/design-system/components/textDirection';
4
+ import type { PartnerPickFileFn } from '../../pickFileContext.js';
5
+ import type { StoreFileUrlFn } from '../../fileStorageUrlContext.js';
4
6
  import { type TokenReason } from '../../composition/compositionApi.js';
5
7
  import { type CompositionEditorState, type EditorFailure } from '../../composition/compositionEditorController.js';
6
8
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
@@ -32,6 +34,12 @@ declare const CompositionEditorApp: $$__sveltets_2_IsomorphicComponent<{
32
34
  * Where the file picker gets its files, when they are not the Widgetic library: the partner's own media
33
35
  * library. Without it the files are the ones of the Widgetic library the token can see.
34
36
  */ mediaLibrary?: MediaLibraryProvider | undefined;
37
+ /** When given, file fields call this instead of opening the built-in File Browser. */ pickFile?: PartnerPickFileFn | undefined;
38
+ /** When given, resolves the url stored with a picked file (partner `mediaLibrary.urlResolver`). */ storeFileUrl?: StoreFileUrlFn | undefined;
39
+ /**
40
+ * When true, dragging or typing a preview size changes the preview on screen but does not change the embed width and
41
+ * height saved with the composition.
42
+ */ lockPreviewSize?: boolean;
35
43
  /** The key the font pickers list Google Fonts with. Without one only the fonts that need no key are listed. */ googleFontsApiKey?: string;
36
44
  /** Wait this long after the last change before saving by itself; `0` turns saving by itself off. Read once, when the editor starts. */ autosaveDelayMs?: number | undefined;
37
45
  /** Writes what the layout does to the console. */ debug?: boolean;
@@ -90,6 +90,8 @@
90
90
  } from '../../utils/shadowDom';
91
91
  import { config as appConfig } from '../../config';
92
92
  import { provideEditorMediaLibrary } from '../../mediaLibraryContext';
93
+ import { provideEditorPickFile, type PartnerPickFileFn } from '../../pickFileContext';
94
+ import { provideEditorStoreFileUrl, type StoreFileUrlFn } from '../../fileStorageUrlContext';
93
95
  import { useEditorLanguage } from '../../i18n/language';
94
96
  import type { MediaLibraryProvider } from '@widgetic/file-browser';
95
97
 
@@ -181,8 +183,14 @@
181
183
  * storage (a CMS, an S3 bucket, a DAM) can be used with no Widgetic storage at all.
182
184
  */
183
185
  export let mediaLibrary: MediaLibraryProvider | undefined = undefined;
186
+ /** When given, file fields call this instead of opening the built-in File Browser. */
187
+ export let pickFile: PartnerPickFileFn | undefined = undefined;
188
+ /** When given, resolves the url stored with a picked file (partner `mediaLibrary.urlResolver`). */
189
+ export let storeFileUrl: StoreFileUrlFn | undefined = undefined;
184
190
  // The file controls are created from the schema several components down: give them the library through context.
185
191
  provideEditorMediaLibrary(() => mediaLibrary);
192
+ provideEditorPickFile(() => pickFile);
193
+ provideEditorStoreFileUrl(() => storeFileUrl);
186
194
 
187
195
  // The words of the editor in the language of the page: the editor above this one chose it (see `provideEditorLanguage`).
188
196
  const { t } = useEditorLanguage();
@@ -1,4 +1,6 @@
1
1
  import type { ContentSchemaView, DesignSchemaView } from '../../schemaViews.js';
2
+ import { type PartnerPickFileFn } from '../../pickFileContext';
3
+ import { type StoreFileUrlFn } from '../../fileStorageUrlContext';
2
4
  import type { MediaLibraryProvider } from '@widgetic/file-browser';
3
5
  /**
4
6
  * STYLES from Widgetic Design System
@@ -52,6 +54,8 @@ declare const PropsEditor: $$__sveltets_2_IsomorphicComponent<{
52
54
  * `MediaLibraryProvider` they list, upload, rename and delete through its functions instead, so a partner's own
53
55
  * storage (a CMS, an S3 bucket, a DAM) can be used with no Widgetic storage at all.
54
56
  */ mediaLibrary?: MediaLibraryProvider | undefined;
57
+ /** When given, file fields call this instead of opening the built-in File Browser. */ pickFile?: PartnerPickFileFn | undefined;
58
+ /** When given, resolves the url stored with a picked file (partner `mediaLibrary.urlResolver`). */ storeFileUrl?: StoreFileUrlFn | undefined;
55
59
  updateDesignValue?: (propertyId: string, value: any) => void;
56
60
  setPosition?: (left: number, top: number) => void;
57
61
  getPosition?: () => {
@@ -32,10 +32,12 @@
32
32
  import { onDestroy, tick } from 'svelte';
33
33
 
34
34
  // Shadcn UI Components
35
- import { Portal, Tooltip } from '@widgetic/design-system/components';
35
+ import { Portal, Tooltip, showToast } from '@widgetic/design-system/components';
36
36
  import { ThumbHashImage } from "@widgetic/design-system/components";
37
37
  import { FileBrowser, setFileBrowserUploadConfig, persistentSelectStore } from '@widgetic/file-browser';
38
38
  import { useEditorMediaLibrary } from '../../mediaLibraryContext';
39
+ import { useEditorPickFile, type PartnerPickFile } from '../../pickFileContext';
40
+ import { useEditorStoreFileUrl } from '../../fileStorageUrlContext';
39
41
  import { useEditorLanguage } from '../../i18n/language';
40
42
  import type { EditorMessageKey } from '../../i18n/messages';
41
43
  import type { Translate } from '@widgetic/design-system/i18n';
@@ -74,6 +76,9 @@
74
76
  // The media library the editor above was given (a partner's own storage, for instance), read through a getter
75
77
  // because it reaches this component through context.
76
78
  const getEditorMediaLibrary = useEditorMediaLibrary();
79
+ const getEditorPickFile = useEditorPickFile();
80
+ const getEditorStoreFileUrl = useEditorStoreFileUrl();
81
+ let pickAbortController: AbortController | null = null;
77
82
 
78
83
  // The words of the editor in the language of the page, and the share of them that belongs to the File Browser inside it.
79
84
  const { t, fileBrowser: fileBrowserLanguage } = useEditorLanguage();
@@ -218,7 +223,56 @@
218
223
  }
219
224
  }
220
225
 
226
+ function acceptPatternsForField(): string[] {
227
+ if (!fileTypes?.length || fileTypes.includes('*')) return ['*/*'];
228
+ return fileTypes;
229
+ }
230
+
231
+ async function storedUrlFor(file: PartnerPickFile): Promise<string> {
232
+ const storeFileUrl = getEditorStoreFileUrl();
233
+ if (!storeFileUrl) return file.url;
234
+ try {
235
+ return await storeFileUrl(file);
236
+ } catch (error) {
237
+ console.warn('[editor] storeFileUrl failed; using the file url.', error);
238
+ return file.url;
239
+ }
240
+ }
241
+
242
+ async function applyPickedFile(selectedFile: PartnerPickFile) {
243
+ const url = await storedUrlFor(selectedFile);
244
+ const nextValue: FileValue = {
245
+ id: selectedFile.id,
246
+ file: selectedFile.name || '',
247
+ url,
248
+ hash: selectedFile.hash
249
+ };
250
+ value = nextValue;
251
+ onChange(nextValue);
252
+ }
253
+
221
254
  async function openFileBrowser() {
255
+ const pickFile = getEditorPickFile();
256
+ if (pickFile) {
257
+ pickAbortController?.abort();
258
+ pickAbortController = new AbortController();
259
+ const signal = pickAbortController.signal;
260
+ try {
261
+ const picked = await pickFile({ accept: acceptPatternsForField(), multiple: false, signal });
262
+ if (signal.aborted) return;
263
+ const selectedFile = picked?.[0];
264
+ if (!selectedFile) return;
265
+ await applyPickedFile(selectedFile);
266
+ } catch (error) {
267
+ if (signal.aborted) return;
268
+ const name = error instanceof Error ? error.name : '';
269
+ if (name === 'AbortError') return;
270
+ console.error('[editor] pickFile failed:', error);
271
+ showToast('error', $t('editor.file.pickFailed'));
272
+ }
273
+ return;
274
+ }
275
+
222
276
  try {
223
277
  syncFileBrowserUploadConfigIfEmbedded();
224
278
  // console.log('BrowserInputController: opening file browser', {
@@ -240,7 +294,7 @@
240
294
  setPortalHostInteractive(false);
241
295
  }
242
296
 
243
- function handleFileBrowserSelect(event: CustomEvent<{ files?: Array<FileValue & { name?: string; thumbnail?: string }> }>) {
297
+ async function handleFileBrowserSelect(event: CustomEvent<{ files?: Array<FileValue & { name?: string; thumbnail?: string }> }>) {
244
298
  try {
245
299
  const selectedFile = event.detail?.files?.[0];
246
300
  if (!selectedFile) {
@@ -248,21 +302,12 @@
248
302
  return;
249
303
  }
250
304
 
251
- const nextValue: FileValue = {
305
+ await applyPickedFile({
252
306
  id: selectedFile.id,
253
- file: selectedFile.file || selectedFile.name || '',
307
+ name: selectedFile.name || selectedFile.file || '',
254
308
  url: selectedFile.url,
255
309
  hash: selectedFile.hash
256
- };
257
-
258
- // console.log('BrowserInputController: selected file', {
259
- // id: nextValue.id,
260
- // file: nextValue.file,
261
- // url: nextValue.url
262
- // });
263
-
264
- value = nextValue;
265
- onChange(nextValue);
310
+ });
266
311
  closeFileBrowser();
267
312
  } catch (error) {
268
313
  console.error('BrowserInputController: failed to select file', error);
@@ -292,6 +337,8 @@
292
337
  }
293
338
 
294
339
  onDestroy(() => {
340
+ pickAbortController?.abort();
341
+ pickAbortController = null;
295
342
  clearTimeout(showHelpTimeout);
296
343
  setPortalHostInteractive(false);
297
344
  if (audioElement) {
@@ -148,6 +148,11 @@ export interface CompositionEditorOptions {
148
148
  * Default: English.
149
149
  */
150
150
  translate?: Translate<EditorMessageKey>;
151
+ /**
152
+ * When true, resizing the preview changes how it is drawn but never updates the embed width and height that are saved
153
+ * with the composition (the partner owns embed size elsewhere).
154
+ */
155
+ lockPreviewSize?: boolean;
151
156
  }
152
157
  /** What went wrong, from whatever was thrown. */
153
158
  export declare function describeFailure(error: unknown): EditorFailure;
@@ -515,6 +515,14 @@ export class CompositionEditorController {
515
515
  // (The preview reports the size it was just given, too: that is not a new size.)
516
516
  if (previewWidth === this.state.previewWidth && previewHeight === this.state.previewHeight)
517
517
  return;
518
+ if (this.options.lockPreviewSize === true) {
519
+ this.patch({
520
+ previewWidth,
521
+ previewHeight,
522
+ deviceView: 'desktop'
523
+ });
524
+ return;
525
+ }
518
526
  const embedChanged = previewWidth !== this.state.embedWidth || previewHeight !== this.state.embedHeight;
519
527
  this.patch({
520
528
  previewWidth,
@@ -0,0 +1,6 @@
1
+ import type { PartnerPickFile } from './pickFileContext';
2
+ /** Resolves the address stored with a composition for a picked file (partner `mediaLibrary.urlResolver`). */
3
+ export type StoreFileUrlFn = (file: PartnerPickFile) => Promise<string>;
4
+ export type StoreFileUrlGetter = () => StoreFileUrlFn | undefined;
5
+ export declare function provideEditorStoreFileUrl(getStoreFileUrl: StoreFileUrlGetter): void;
6
+ export declare function useEditorStoreFileUrl(): StoreFileUrlGetter;
@@ -0,0 +1,13 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const STORE_FILE_URL_CONTEXT_KEY = Symbol.for('widgetic.editor.storeFileUrl');
3
+ export function provideEditorStoreFileUrl(getStoreFileUrl) {
4
+ setContext(STORE_FILE_URL_CONTEXT_KEY, getStoreFileUrl);
5
+ }
6
+ export function useEditorStoreFileUrl() {
7
+ try {
8
+ return getContext(STORE_FILE_URL_CONTEXT_KEY) ?? (() => undefined);
9
+ }
10
+ catch {
11
+ return () => undefined;
12
+ }
13
+ }
@@ -1,4 +1,3 @@
1
- import { SHAD_EDITOR_MESSAGES } from '@widgetic/design-system/shad-editor';
2
1
  export type { EditorCatalogs, EditorLanguage, EditorLanguageHandle } from './languageKit.js';
3
2
  /**
4
3
  * The language of an editor on a page. The editor has two packages inside it (the File Browser that picks a file, and
@@ -145,6 +144,7 @@ export declare const EDITOR_CATALOGS: {
145
144
  readonly 'editor.file.notFound': "File not found";
146
145
  readonly 'editor.file.emptyFolder': "Empty folder";
147
146
  readonly 'editor.file.clear': "Clear file";
147
+ readonly 'editor.file.pickFailed': "Could not choose a file. Try again.";
148
148
  readonly 'editor.file.dialogTitle': "Select file";
149
149
  readonly 'editor.file.closeDialog': "Close file browser";
150
150
  readonly 'editor.color.noRecent': "No recent colors";
@@ -206,7 +206,93 @@ export declare const EDITOR_CATALOGS: {
206
206
  readonly 'editor.schema.subtitleFont': "Subtitle Font";
207
207
  };
208
208
  readonly fileBrowser: any;
209
- readonly shadEditor: any;
209
+ readonly shadEditor: {
210
+ readonly 'shadEditor.toolbar.undo': "Undo (\u2318Z)";
211
+ readonly 'shadEditor.toolbar.redo': "Redo (\u2318\u21E7Z)";
212
+ readonly 'shadEditor.toolbar.bold': "Bold (\u2318B)";
213
+ readonly 'shadEditor.toolbar.italic': "Italic (\u2318I)";
214
+ readonly 'shadEditor.toolbar.underline': "Underline (\u2318U)";
215
+ readonly 'shadEditor.toolbar.strikethrough': "Strike (\u2318\u21E7S)";
216
+ readonly 'shadEditor.toolbar.subscript': "Subscript (\u2318,)";
217
+ readonly 'shadEditor.toolbar.superscript': "Superscript (\u2318.)";
218
+ readonly 'shadEditor.toolbar.code': "Code (\u2318E)";
219
+ readonly 'shadEditor.toolbar.blockQuote': "Block Quote (\u2318\u21E7B)";
220
+ readonly 'shadEditor.toolbar.bulletList': "Bullet List (\u2318\u21E78)";
221
+ readonly 'shadEditor.toolbar.orderedList': "Ordered List";
222
+ readonly 'shadEditor.toolbar.taskList': "Tasks List (\u2318\u21E79)";
223
+ readonly 'shadEditor.text.tooltip': "Text Formatting";
224
+ readonly 'shadEditor.text.heading1': "Heading 1";
225
+ readonly 'shadEditor.text.heading2': "Heading 2";
226
+ readonly 'shadEditor.text.heading3': "Heading 3";
227
+ readonly 'shadEditor.text.paragraph': "Paragraph";
228
+ readonly 'shadEditor.text.codeBlock': "Code Block";
229
+ readonly 'shadEditor.align.tooltip': "Text Alignment";
230
+ readonly 'shadEditor.align.left': "Align Left";
231
+ readonly 'shadEditor.align.center': "Align Center";
232
+ readonly 'shadEditor.align.right': "Align Right";
233
+ readonly 'shadEditor.align.justify': "Align Justify";
234
+ readonly 'shadEditor.link.tooltip': "Add Or Remove Link";
235
+ readonly 'shadEditor.link.title': "Link";
236
+ readonly 'shadEditor.link.hint': "Insert or remove link from selected text.";
237
+ readonly 'shadEditor.link.placeholder': "Enter link to attach..";
238
+ readonly 'shadEditor.link.insert': "Insert";
239
+ readonly 'shadEditor.link.remove': "Remove";
240
+ readonly 'shadEditor.image.tooltip': "Add Image";
241
+ readonly 'shadEditor.image.title': "Image";
242
+ readonly 'shadEditor.image.urlHint': "Insert image url";
243
+ readonly 'shadEditor.image.urlPlaceholder': "Enter image url...";
244
+ readonly 'shadEditor.image.orPick': "OR Pick an Image";
245
+ readonly 'shadEditor.table.tooltip': "Table";
246
+ readonly 'shadEditor.table.insert': "Insert Table";
247
+ readonly 'shadEditor.table.header': "Header";
248
+ readonly 'shadEditor.table.toggleColumn': "Toggle Column";
249
+ readonly 'shadEditor.table.toggleRow': "Toggle Row";
250
+ readonly 'shadEditor.table.toggleCell': "Toggle Cell";
251
+ readonly 'shadEditor.table.cells': "Cells";
252
+ readonly 'shadEditor.table.merge': "Merge";
253
+ readonly 'shadEditor.table.split': "Split";
254
+ readonly 'shadEditor.table.mergeOrSplit': "Merge or Split";
255
+ readonly 'shadEditor.table.row': "Row";
256
+ readonly 'shadEditor.table.insertAbove': "Insert Above";
257
+ readonly 'shadEditor.table.insertBelow': "Insert Below";
258
+ readonly 'shadEditor.table.deleteRow': "Delete Row";
259
+ readonly 'shadEditor.table.column': "Column";
260
+ readonly 'shadEditor.table.insertBefore': "Insert Before";
261
+ readonly 'shadEditor.table.insertAfter': "Insert After";
262
+ readonly 'shadEditor.table.deleteColumn': "Delete";
263
+ readonly 'shadEditor.table.deleteTable': "Delete";
264
+ readonly 'shadEditor.textColor.tooltip': "Text Color";
265
+ readonly 'shadEditor.textColor.pick': "Pick a text color";
266
+ readonly 'shadEditor.textColor.remove': "Remove Color";
267
+ readonly 'shadEditor.highlight.tooltip': "Highlighter (\u2318\u21E7H)";
268
+ readonly 'shadEditor.highlight.pick': "Pick a highlight color";
269
+ readonly 'shadEditor.highlight.remove': "Remove Highlight";
270
+ readonly 'shadEditor.quickColor.tooltip': "Quick Colors";
271
+ readonly 'shadEditor.quickColor.textColor': "Text Color";
272
+ readonly 'shadEditor.quickColor.backgroundColors': "Background Colors";
273
+ readonly 'shadEditor.colorName.default': "Default";
274
+ readonly 'shadEditor.colorName.blue': "Blue";
275
+ readonly 'shadEditor.colorName.brown': "Brown";
276
+ readonly 'shadEditor.colorName.green': "Green";
277
+ readonly 'shadEditor.colorName.grey': "Grey";
278
+ readonly 'shadEditor.colorName.orange': "Orange";
279
+ readonly 'shadEditor.colorName.pink': "Pink";
280
+ readonly 'shadEditor.colorName.purple': "Purple";
281
+ readonly 'shadEditor.colorName.red': "Red";
282
+ readonly 'shadEditor.colorName.yellow': "Yellow";
283
+ readonly 'shadEditor.colorPicker.label': "Pick a color";
284
+ readonly 'shadEditor.search.tooltip': "Search And Replace Text";
285
+ readonly 'shadEditor.search.searchPlaceholder': "Enter Text to search..";
286
+ readonly 'shadEditor.search.replacePlaceholder': "Enter Text to Replace..";
287
+ readonly 'shadEditor.search.caseSensitive': "Case Sensitive";
288
+ readonly 'shadEditor.search.position': "{index} / {count}";
289
+ readonly 'shadEditor.imageBlock.back': "Back";
290
+ readonly 'shadEditor.imageBlock.caption': "Caption";
291
+ readonly 'shadEditor.imageBlock.duplicate': "Duplicate";
292
+ readonly 'shadEditor.imageBlock.fullScreen': "Full Screen";
293
+ readonly 'shadEditor.imageBlock.delete': "Delete";
294
+ readonly 'shadEditor.imageBlock.defaultCaption': "Image Caption";
295
+ };
210
296
  };
211
297
  /** Every message of the editor and of the packages inside it, as one catalog: what a page's `messages` may have a key of. */
212
298
  export declare const ALL_EDITOR_MESSAGES: Readonly<Record<string, string>>;
@@ -215,12 +301,12 @@ export declare const ALL_EDITOR_MESSAGES: Readonly<Record<string, string>>;
215
301
  * the nearest real one), a value that is not text, a template that does not parse, or one that asks for a value the message
216
302
  * is never given. An empty list means every message will be used. For a test or a build step of the page.
217
303
  */
218
- export declare const checkEditorMessages: (messages: SHAD_EDITOR_MESSAGES) => SHAD_EDITOR_MESSAGES[];
304
+ export declare const checkEditorMessages: (messages: import("@widgetic/design-system/i18n").MessageOverrides) => import("@widgetic/design-system/i18n").MessageProblem[];
219
305
  /**
220
306
  * Called by the root component of an editor while it is created, so everything inside it speaks the page's language.
221
307
  * The context belongs to one root component, so two editors on a page can speak two languages.
222
308
  */
223
- export declare const provideEditorLanguage: (initial: SHAD_EDITOR_MESSAGES) => import("./languageKit.js").EditorLanguageHandle;
309
+ export declare const provideEditorLanguage: (initial: import("@widgetic/design-system/i18n").I18nSettings) => import("./languageKit.js").EditorLanguageHandle;
224
310
  /**
225
311
  * Called by a part of the editor while it is created. Gives its words as stores, so a part that is already on the screen
226
312
  * changes when the page sets the language again. Outside an editor (a control used on its own) it speaks English.
@@ -164,6 +164,7 @@ export declare const EDITOR_MESSAGES: {
164
164
  readonly 'editor.file.notFound': "File not found";
165
165
  readonly 'editor.file.emptyFolder': "Empty folder";
166
166
  readonly 'editor.file.clear': "Clear file";
167
+ readonly 'editor.file.pickFailed': "Could not choose a file. Try again.";
167
168
  readonly 'editor.file.dialogTitle': "Select file";
168
169
  readonly 'editor.file.closeDialog': "Close file browser";
169
170
  readonly 'editor.color.noRecent': "No recent colors";
@@ -173,6 +173,7 @@ export const EDITOR_MESSAGES = {
173
173
  'editor.file.notFound': 'File not found',
174
174
  'editor.file.emptyFolder': 'Empty folder',
175
175
  'editor.file.clear': 'Clear file',
176
+ 'editor.file.pickFailed': 'Could not choose a file. Try again.',
176
177
  'editor.file.dialogTitle': 'Select file',
177
178
  'editor.file.closeDialog': 'Close file browser',
178
179
  // ===== Colours =====
@@ -3,4 +3,4 @@
3
3
  * its sentences with it until the page gives another translator). The editor's screens get their words, in the page's
4
4
  * language, from `useEditorLanguage` (see `language.ts`).
5
5
  */
6
- export declare const translateEnglish: any;
6
+ export declare const translateEnglish: import("@widgetic/design-system/i18n").Translate<"editor.app.loading" | "editor.app.tryAgain" | "editor.app.retry" | "editor.failure.widgetNotPublished" | "editor.failure.sessionEndedWhileSaving" | "editor.failure.sessionEnded" | "editor.failure.compositionNotFound" | "editor.failure.noAccess" | "editor.failure.noWidget" | "editor.failure.widgetNotFound" | "editor.failure.saveNeedsConnection" | "editor.failure.loadNeedsConnection" | "editor.failure.saveFailed" | "editor.failure.loadFailed" | "editor.failure.settingsLoadFailed" | "editor.failure.noSettings" | "editor.failure.errorCode" | "editor.props.title" | "editor.props.designTab" | "editor.props.contentTab" | "editor.props.contentCard" | "editor.props.save" | "editor.props.saving" | "editor.props.noConfiguration" | "editor.props.noDesignSchema" | "editor.props.noSchemaData" | "editor.props.noComponents" | "editor.props.noProperties" | "editor.props.addItem" | "editor.props.deleteItem" | "editor.embed.title" | "editor.embed.loading" | "editor.embed.noCode" | "editor.embed.codeHeader" | "editor.embed.headerArea" | "editor.embed.readMore" | "editor.embed.copy" | "editor.embed.copied" | "editor.embed.copiedHint" | "editor.embed.copiedDone" | "editor.embed.copyFailed" | "editor.embed.copyFailedHint" | "editor.preview.title" | "editor.preview.dragHandle" | "editor.preview.desktop" | "editor.preview.mobile" | "editor.preview.widthShort" | "editor.preview.heightShort" | "editor.preview.noUrl" | "editor.preview.copyUrl" | "editor.preview.refresh" | "editor.preview.fitToPanel" | "editor.preview.mobileViewport" | "editor.preview.desktopViewport" | "editor.preview.openInNewTab" | "editor.preview.newTab" | "editor.preview.selectWidget" | "editor.preview.widgetName" | "editor.preview.notPublished" | "editor.preview.publishToPreview" | "editor.preview.frameTitle" | "editor.preview.frameTitleNamed" | "editor.preview.unavailable" | "editor.preview.loadFailedPreview" | "editor.preview.retry" | "editor.preview.retryBuild" | "editor.preview.retryPreview" | "editor.preview.fixErrors" | "editor.preview.viewErrors" | "editor.preview.runtimeError" | "editor.preview.compilationErrors" | "editor.preview.compilationErrorsDetail" | "editor.preview.noCodeYet" | "editor.preview.noPreview" | "editor.preview.building" | "editor.preview.loadingWidget" | "editor.preview.loadingPreview" | "editor.preview.checkingWidget" | "editor.preview.applyingSettings" | "editor.preview.connecting" | "editor.preview.generatingCode" | "editor.list.recentlyUsed" | "editor.list.more" | "editor.list.searchResults" | "editor.list.loadingMore" | "editor.list.deleteItem" | "editor.category.all" | "editor.category.mine" | "editor.category.sansSerif" | "editor.category.serif" | "editor.category.script" | "editor.category.other" | "editor.category.icons" | "editor.category.emojis" | "editor.category.clipart" | "editor.font.select" | "editor.font.title" | "editor.font.search" | "editor.font.list" | "editor.font.loading" | "editor.font.noneFound" | "editor.font.noneAvailable" | "editor.font.remove" | "editor.font.weight" | "editor.font.weightList" | "editor.fontWeight.withItalic" | "editor.fontWeight.italic" | "editor.fontWeight.thin" | "editor.fontWeight.extraLight" | "editor.fontWeight.light" | "editor.fontWeight.regular" | "editor.fontWeight.medium" | "editor.fontWeight.semiBold" | "editor.fontWeight.bold" | "editor.fontWeight.extraBold" | "editor.fontWeight.black" | "editor.vector.select" | "editor.vector.title" | "editor.vector.search" | "editor.vector.list" | "editor.vector.noneFound" | "editor.vector.noneAvailable" | "editor.file.useFile" | "editor.file.useImage" | "editor.file.useVideo" | "editor.file.useAudio" | "editor.file.useDocument" | "editor.file.addFile" | "editor.file.addImage" | "editor.file.addVideo" | "editor.file.addAudio" | "editor.file.addDocument" | "editor.file.play" | "editor.file.pause" | "editor.file.notFound" | "editor.file.emptyFolder" | "editor.file.clear" | "editor.file.pickFailed" | "editor.file.dialogTitle" | "editor.file.closeDialog" | "editor.color.noRecent" | "editor.color.picker" | "editor.date.pick" | "editor.date.pickRange" | "editor.date.invalid" | "editor.date.selectMonth" | "editor.date.selectYear" | "editor.date.calendarLabel" | "editor.date.rangeCalendarLabel" | "editor.date.previousMonth" | "editor.date.nextMonth" | "editor.dateField.dayHint" | "editor.dateField.monthHint" | "editor.dateField.yearHint" | "editor.dateField.dayLabel" | "editor.dateField.monthLabel" | "editor.dateField.yearLabel" | "editor.dropdown.select" | "editor.switch.off" | "editor.switch.on" | "editor.tags.add" | "editor.position.x" | "editor.position.y" | "editor.fixedPosition.set" | "editor.fixedPosition.topLeft" | "editor.fixedPosition.topCenter" | "editor.fixedPosition.topRight" | "editor.fixedPosition.centerLeft" | "editor.fixedPosition.centerCenter" | "editor.fixedPosition.centerRight" | "editor.fixedPosition.bottomLeft" | "editor.fixedPosition.bottomCenter" | "editor.fixedPosition.bottomRight" | "editor.corners.group" | "editor.corners.topLeft" | "editor.corners.topRight" | "editor.corners.bottomLeft" | "editor.corners.bottomRight" | "editor.corners.lock" | "editor.corners.unlock" | "editor.textArea.toolbar" | "editor.textArea.toggleBold" | "editor.textArea.toggleItalic" | "editor.textArea.toggleUnderline" | "editor.textArea.toggleLink" | "editor.textArea.text" | "editor.textArea.paragraph" | "editor.textArea.heading1" | "editor.textArea.heading2" | "editor.textArea.heading3" | "editor.textArea.bulletList" | "editor.textArea.numberedList" | "editor.textArea.checkList" | "editor.textArea.textStyles" | "editor.textArea.listStyles" | "editor.textArea.enterUrl" | "editor.schema.subtitleFont">;
@@ -0,0 +1,19 @@
1
+ /** One file the partner's picker returns (same shape as the media library's `MediaLibraryFile`). */
2
+ export interface PartnerPickFile {
3
+ id: string;
4
+ name: string;
5
+ url: string;
6
+ mimeType?: string;
7
+ size?: number;
8
+ thumbnail?: string;
9
+ hash?: string;
10
+ }
11
+ export interface PartnerPickFileRequest {
12
+ accept: string[];
13
+ multiple: boolean;
14
+ signal: AbortSignal;
15
+ }
16
+ export type PartnerPickFileFn = (request: PartnerPickFileRequest) => Promise<PartnerPickFile[]>;
17
+ export type PickFileGetter = () => PartnerPickFileFn | undefined;
18
+ export declare function provideEditorPickFile(getPickFile: PickFileGetter): void;
19
+ export declare function useEditorPickFile(): PickFileGetter;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Hands the partner's `pickFile` hook from the editor down to file field controls.
3
+ *
4
+ * Same pattern as {@link mediaLibraryContext}: `PropsEditor` receives `pickFile`, and the file controls read it through
5
+ * context instead of threading a prop through the controller renderer.
6
+ */
7
+ import { getContext, setContext } from 'svelte';
8
+ const PICK_FILE_CONTEXT_KEY = Symbol.for('widgetic.editor.pickFile');
9
+ export function provideEditorPickFile(getPickFile) {
10
+ setContext(PICK_FILE_CONTEXT_KEY, getPickFile);
11
+ }
12
+ export function useEditorPickFile() {
13
+ try {
14
+ return getContext(PICK_FILE_CONTEXT_KEY) ?? (() => undefined);
15
+ }
16
+ catch {
17
+ return () => undefined;
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@widgetic/editor",
3
- "version": "4.1.2",
3
+ "version": "4.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -38,6 +38,7 @@
38
38
  "render-build": "bash render-build.sh",
39
39
  "build": "npm run sync-embed-schema && NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 vite build",
40
40
  "package": "svelte-package",
41
+ "publish-to-npm": "npm version patch --no-git-tag-version && npm run package && npm publish --ignore-scripts --access public",
41
42
  "package:watch": "svelte-package --watch",
42
43
  "preview": "vite preview",
43
44
  "deploy": "vite build && vite preview --host --port 80",