diff --git a/package-lock.json b/package-lock.json index 800c76414..4ea58ffa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "packages/*" ], "devDependencies": { - "@blockly/zoom-to-fit": "^13.0.0", + "@blockly/zoom-to-fit": "^13.2.0", "@node-projects/base-custom-webcomponent": ">=0.27.8", "@node-projects/css-parser": "^5.0.0", "@node-projects/lean-he-esm": "^3.3.0", @@ -22,7 +22,7 @@ "@types/jquery.fancytree": "0.0.11", "@types/node": "^22.8.6", "ace-builds": "^1.36.3", - "blockly": "^13.0.0", + "blockly": "^13.2.1", "codemirror": "^6.0.1", "codemirror5": "npm:codemirror@^5.0.0", "css-tree": "^3.0.0", @@ -566,12 +566,12 @@ "license": "MIT" }, "node_modules/@blockly/zoom-to-fit": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@blockly/zoom-to-fit/-/zoom-to-fit-13.1.0.tgz", - "integrity": "sha512-ch5C4Z+53kLujocgbyTAb4FZQptaQPJV2nSHlmpoP0O2C7ABRB14IR9/25FEMx4SZBkKv9N4cN/AFTipZvwPKw==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@blockly/zoom-to-fit/-/zoom-to-fit-13.2.0.tgz", + "integrity": "sha512-iAhmQONUm/MW5SBtzGOaiH3VLp/RjJL6+YxR73sh80K3+6GITv65bqn2XBXLLMW7H/5vmnwNU2EPp2V8CvVd5w==", "license": "Apache-2.0", "peerDependencies": { - "blockly": "^13.1.0" + "blockly": "^13.2.0" } }, "node_modules/@braintree/sanitize-url": { @@ -2733,15 +2733,15 @@ } }, "node_modules/blockly": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/blockly/-/blockly-13.1.1.tgz", - "integrity": "sha512-0h3YDzlGclLKaZi9VHxyPjRJ9OFV6vw2+TMcolLZO1ZlgfY1Q79qDlGpH2nUYnTNiEVuJu9K6u6NHxqiPeRYOA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/blockly/-/blockly-13.2.1.tgz", + "integrity": "sha512-iOa37H0ks5Zup2G1zZyviLX9E5WzWh3h0Zs8Nta2cPKmnAGHjZ6wrp3VqLTaquTJReJKPwaJZ39K7AD24rT4lg==", "license": "Apache-2.0", "engines": { "node": ">=22" }, "peerDependencies": { - "jsdom": "^27.4.0" + "jsdom": ">=27.4.0 <30.0.0" } }, "node_modules/brace-expansion": { @@ -7111,7 +7111,7 @@ }, "packages/web-component-designer": { "name": "@node-projects/web-component-designer", - "version": "0.2.30", + "version": "0.2.31", "license": "MIT", "dependencies": { "@node-projects/base-custom-webcomponent": ">=0.27.8" @@ -7202,16 +7202,6 @@ "@node-projects/web-component-designer": ">=0.1.278" } }, - "packages/web-component-designer-liveblocks": { - "name": "@node-projects/web-component-designer-liveblocks", - "version": "0.1.0", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@liveblocks/client": "^1.9.7", - "@node-projects/web-component-designer": ">=0.1.1" - } - }, "packages/web-component-designer-mermaid": { "name": "@node-projects/web-component-designer-mermaid", "version": "0.1.1", @@ -7222,17 +7212,6 @@ "mermaid": "^11.12.0" } }, - "packages/web-component-designer-miniatureview-html2canvas": { - "name": "@node-projects/web-component-designer-miniatureview-html2canvas", - "version": "0.1.3", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@node-projects/base-custom-webcomponent": ">=0.27.8", - "@node-projects/web-component-designer": ">=0.1.224", - "html2canvas": "*" - } - }, "packages/web-component-designer-stylesheetservice-css-parser": { "name": "@node-projects/web-component-designer-stylesheetservice-css-parser", "version": "0.1.5", @@ -7242,43 +7221,12 @@ "@node-projects/web-component-designer": ">=0.1.224" } }, - "packages/web-component-designer-stylesheetservice-css-tools": { - "name": "@node-projects/web-component-designer-stylesheetservice-css-tools", - "version": "0.1.11", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@node-projects/css-parser": "^5.0.0", - "@node-projects/web-component-designer": ">=0.1.224" - } - }, - "packages/web-component-designer-stylesheetservice-css-tree": { - "name": "@node-projects/web-component-designer-stylesheetservice-css-tree", - "version": "0.1.4", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@node-projects/web-component-designer": ">=0.1.224", - "css-tree": ">=3.0.0" - } - }, - "packages/web-component-designer-texteditextension-stylo": { - "name": "@node-projects/web-component-designer-texteditextension-stylo", - "version": "0.1.4", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@node-projects/base-custom-webcomponent": ">=0.19.0", - "@node-projects/web-component-designer": ">=0.1.1", - "@papyrs/stylo": ">=0.0.45" - } - }, "packages/web-component-designer-visualization-addons": { "name": "@node-projects/web-component-designer-visualization-addons", - "version": "0.1.149", + "version": "0.1.152", "license": "MIT", "dependencies": { - "@blockly/zoom-to-fit": ">=13.0.0", + "@blockly/zoom-to-fit": ">=13.2.0", "@node-projects/base-custom-webcomponent": ">=0.27.8", "@node-projects/css-parser": "^5.2.0", "@node-projects/propertygrid.webcomponent": ">=1.2.3", @@ -7286,25 +7234,13 @@ "@node-projects/web-component-designer": ">=0.1.224", "@node-projects/web-component-designer-codeview-monaco": ">=0.1.32", "@node-projects/web-component-designer-widgets-wunderbaum": ">=0.1.29", - "blockly": ">=13.0.0", + "blockly": ">=13.2.1", "long": ">=5.2.3" }, "devDependencies": { "esbuild": "^0.25.10" } }, - "packages/web-component-designer-widgets-fancytree": { - "name": "@node-projects/web-component-designer-widgets-fancytree", - "version": "0.1.16", - "extraneous": true, - "license": "MIT", - "dependencies": { - "@node-projects/base-custom-webcomponent": ">=0.27.8", - "@node-projects/web-component-designer": ">=0.1.224", - "jquery": "^3.7.1", - "jquery.fancytree": "^2.38.3" - } - }, "packages/web-component-designer-widgets-wunderbaum": { "name": "@node-projects/web-component-designer-widgets-wunderbaum", "version": "0.2.1", @@ -7317,7 +7253,7 @@ }, "packages/web-component-designer-zpl": { "name": "@node-projects/web-component-designer-zpl", - "version": "0.3.0", + "version": "0.3.2", "license": "MIT", "dependencies": { "@bwip-js/browser": "4.11.2", diff --git a/packages/web-component-designer-widgets-wunderbaum/src/Constants.ts b/packages/web-component-designer-widgets-wunderbaum/src/Constants.ts index 9ca19392c..937ef1487 100644 --- a/packages/web-component-designer-widgets-wunderbaum/src/Constants.ts +++ b/packages/web-component-designer-widgets-wunderbaum/src/Constants.ts @@ -1,2 +1,30 @@ -let imporUrl = new URL((import.meta.url)); -export var assetsPath = imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/'; \ No newline at end of file +/** Used when the module URL cannot locate the assets and the host configured + * nothing. Document relative, so it follows a . */ +const fallbackAssetsPath = 'assets/web-component-designer-widgets-wunderbaum/'; + +function resolveAssetsPath(): string { + const configured = (globalThis as { wunderbaumWidgetsAssetsPath?: string }).wunderbaumWidgetsAssetsPath; + if (configured) + return configured.endsWith('/') ? configured : configured + '/'; + + // Bundlers that do not rewrite import.meta.url replace it with the build + // machine's file:// path, whose origin is the string "null". Only a URL the + // browser can actually fetch locates the assets next to this module. + try { + const imporUrl = new URL((import.meta.url)); + if (imporUrl.protocol === 'http:' || imporUrl.protocol === 'https:') + return imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/'; + } catch { + // import.meta.url unavailable or unparsable, fall through + } + + return fallbackAssetsPath; +} + +export var assetsPath = resolveAssetsPath(); + +/** Overrides where these widgets load their icons from. Prefer setting + * globalThis.wunderbaumWidgetsAssetsPath before importing them. */ +export function setAssetsPath(path: string) { + assetsPath = path.endsWith('/') ? path : path + '/'; +} \ No newline at end of file diff --git a/packages/web-component-designer-zpl/src/Constants.ts b/packages/web-component-designer-zpl/src/Constants.ts new file mode 100644 index 000000000..4976a44fa --- /dev/null +++ b/packages/web-component-designer-zpl/src/Constants.ts @@ -0,0 +1,40 @@ +/** Used when the module URL cannot locate the package files and the host + * configured nothing. Document relative, so it follows a . */ +const fallbackBasePath = 'assets/web-component-designer-zpl/'; + +function resolveBasePath(): string { + const configured = (globalThis as { webComponentDesignerZplBasePath?: string }).webComponentDesignerZplBasePath; + if (configured) + return configured.endsWith('/') ? configured : configured + '/'; + + // Bundlers that do not rewrite import.meta.url replace it with the build + // machine's file:// path, which the browser cannot fetch. Only a real URL + // locates widgets/elements.json and assets/fonts next to this module. + try { + const moduleUrl = new URL(import.meta.url); + if (moduleUrl.protocol === 'http:' || moduleUrl.protocol === 'https:') + return moduleUrl.origin + moduleUrl.pathname.split('/').slice(0, -1).join('/') + '/'; + } catch { + // import.meta.url unavailable or unparsable, fall through + } + + return fallbackBasePath; +} + +/** Directory holding this package's runtime files: widgets/elements.json and + * assets/fonts. Both must be published by the host application. */ +export var zplBasePath = resolveBasePath(); + +/** Overrides where the ZPL add-on loads its element definitions and printer + * fonts from. Prefer setting globalThis.webComponentDesignerZplBasePath before + * importing the add-on, so nothing reads the value before it is configured. */ +export function setZplBasePath(path: string) { + zplBasePath = path.endsWith('/') ? path : path + '/'; +} + +/** Resolves a package relative file against the configured base path, so a + * document relative default still produces an absolute URL. */ +export function zplPackageUrl(relativePath: string): URL { + const base = typeof document === 'undefined' ? undefined : document.baseURI; + return new URL(zplBasePath + relativePath, base); +} diff --git a/packages/web-component-designer-zpl/src/fonts/zplFonts.ts b/packages/web-component-designer-zpl/src/fonts/zplFonts.ts index 48ca4b5db..afcd0b57f 100644 --- a/packages/web-component-designer-zpl/src/fonts/zplFonts.ts +++ b/packages/web-component-designer-zpl/src/fonts/zplFonts.ts @@ -1,3 +1,5 @@ +import { zplPackageUrl } from '../Constants.js'; + export type ZplFontName = '0' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H'; export interface ZplDeviceFontMetrics { @@ -78,7 +80,7 @@ export async function loadZplFonts(document: Document): Promise { ['ZplOCRA', 'OCRA.ttf'] ] as const; loading = Promise.all(sources.map(async ([family, file]) => { - const url = new URL(`../assets/fonts/${file}`, import.meta.url); + const url = zplPackageUrl(`assets/fonts/${file}`); const face = new FontFace(family, `url(${url})`); await face.load(); document.fonts.add(face); diff --git a/packages/web-component-designer-zpl/src/index.ts b/packages/web-component-designer-zpl/src/index.ts index 8d96789f1..25157510b 100644 --- a/packages/web-component-designer-zpl/src/index.ts +++ b/packages/web-component-designer-zpl/src/index.ts @@ -23,4 +23,5 @@ export * from "./barcodes/barcodeRegistry.js"; export * from "./barcodes/bwipRenderer.js"; export * from "./fonts/zplFonts.js"; export * from "./setupZplServiceContainer.js"; -export * from "./zplHelper.js"; +export * from "./zplHelper.js"; +export * from "./Constants.js"; diff --git a/packages/web-component-designer-zpl/src/setupZplServiceContainer.ts b/packages/web-component-designer-zpl/src/setupZplServiceContainer.ts index 46e7c76c1..e847a0d4c 100644 --- a/packages/web-component-designer-zpl/src/setupZplServiceContainer.ts +++ b/packages/web-component-designer-zpl/src/setupZplServiceContainer.ts @@ -1,4 +1,5 @@ import { DefaultPropertyEditorTypesService, DefaultEditorTypeService, ExtensionType, DefaultModelCommandService, DefaultHtmlParserService, JsonFileElementsService, ServiceContainer, PositionExtensionProvider, GrayOutExtensionProvider, AltToEnterContainerExtensionProvider, NamedTools, PointerTool, RectangleSelectorTool, ZoomTool, PanTool, MagicWandSelectorTool, ZMoveContextMenu, CopyPasteContextMenu, MultipleItemsSelectedContextMenu, ItemsBelowContextMenu, ElementDragTitleExtensionProvider, PointerToolButtonProvider, SeperatorToolProvider, SelectorToolButtonProvider, ZoomToolButtonProvider, HighlightElementExtensionProvider, IDesignerCanvas, SelectionService, UndoService, GrayOutDragOverContainerExtensionProvider, ElementAtPointService, SnaplinesProviderService, DefaultInstanceService, PropertyGroupsService, DesignItemDocumentPositionService, DragDropService, BaseCustomWebComponentPropertiesService, TransformToolButtonProvider, DesignItemService, DeletionService, ResizeExtensionProvider } from '@node-projects/web-component-designer'; +import { zplPackageUrl } from './Constants.js'; import { ZplLayoutPlacementService } from './services/ZplLayoutPlacementService.js'; import { ZplParserService } from './services/ZplParserService.js'; import { ZplImageDrop } from './services/ZplImageDrop.js'; @@ -100,7 +101,7 @@ export function createZplDesignerServiceContainer() { serviceContainer.config.demoViewWidget = ZplDemoView; - serviceContainer.register('elementsService', new JsonFileElementsService('zpl', new URL("./widgets/elements.json", import.meta.url))); + serviceContainer.register('elementsService', new JsonFileElementsService('zpl', zplPackageUrl('widgets/elements.json'))); serviceContainer.register('elementsService', new ZplBarcodeElementsService()); return serviceContainer; diff --git a/packages/web-component-designer/src/Constants.ts b/packages/web-component-designer/src/Constants.ts index 2bbba417c..034f9eb15 100644 --- a/packages/web-component-designer/src/Constants.ts +++ b/packages/web-component-designer/src/Constants.ts @@ -2,5 +2,36 @@ export const dragDropFormatNameElementDefinition = 'text/json/elementdefintion'; export const dragDropFormatNameBindingObject = 'text/json/bindingobject'; export const dragDropFormatNamePropertyGrid = 'text/json/propertydrop'; -let imporUrl = new URL((import.meta.url)); -export var assetsPath = imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/'; \ No newline at end of file +/** Used when the module URL cannot locate the assets and the host configured + * nothing. Document relative, so it follows a . */ +const fallbackAssetsPath = 'assets/web-component-designer/'; + +function resolveAssetsPath(): string { + const configured = (globalThis as { webComponentDesignerAssetsPath?: string }).webComponentDesignerAssetsPath; + if (configured) + return configured.endsWith('/') ? configured : configured + '/'; + + // Bundlers that do not rewrite import.meta.url replace it with the build + // machine's file:// path, whose origin is the string "null". Only a URL the + // browser can actually fetch locates the assets next to this module. + try { + const imporUrl = new URL((import.meta.url)); + if (imporUrl.protocol === 'http:' || imporUrl.protocol === 'https:') + return imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/'; + } catch { + // import.meta.url unavailable or unparsable, fall through + } + + return fallbackAssetsPath; +} + +export var assetsPath = resolveAssetsPath(); + +/** Overrides where the designer loads its icons from. + * + * Prefer setting globalThis.webComponentDesignerAssetsPath before importing the + * designer: widgets such as SelectionToolPopup bake assetsPath into a static + * template while their module evaluates, and never observe a later change. */ +export function setAssetsPath(path: string) { + assetsPath = path.endsWith('/') ? path : path + '/'; +} \ No newline at end of file diff --git a/packages/web-component-designer/src/elements/documentContainer.ts b/packages/web-component-designer/src/elements/documentContainer.ts index c84b7bd7a..ec2a5aa6e 100644 --- a/packages/web-component-designer/src/elements/documentContainer.ts +++ b/packages/web-component-designer/src/elements/documentContainer.ts @@ -240,8 +240,8 @@ export class DocumentContainer extends BaseCustomWebComponentLazyAppend implemen this.designerView.instanceServiceContainer.collaborationService.disconnect(); this.designerView.instanceServiceContainer.collaborationService.detachTransport(); } - this.codeView.dispose(); - this.demoView.dispose(); + this.codeView?.dispose(); + this.demoView?.dispose(); } executeCommand(command: IUiCommand) { diff --git a/packages/web-component-designer/src/elements/services/ServiceContainer.ts b/packages/web-component-designer/src/elements/services/ServiceContainer.ts index 86f72387b..6c910a527 100644 --- a/packages/web-component-designer/src/elements/services/ServiceContainer.ts +++ b/packages/web-component-designer/src/elements/services/ServiceContainer.ts @@ -107,7 +107,8 @@ export class ServiceContainer extends BaseServiceContainer { readonly config: { codeViewWidget: new (...args: any[]) => ICodeView & HTMLElement; - demoViewWidget: new (...args: any[]) => IDemoView & HTMLElement; + /** Null removes the preview tab, which DocumentContainer already checks for. */ + demoViewWidget: (new (...args: any[]) => IDemoView & HTMLElement) | null; openBindingsEditor?: (property: IProperty, designItems: IDesignItem[], binding: IBinding, bindingTarget: BindingTarget) => Promise } = { codeViewWidget: CodeViewSimple,