[
  {
    "path": ".editorconfig",
    "content": "root = true\r\n\r\n[*]\r\nend_of_line = crlf\r\ninsert_final_newline = false\r\nindent_style = space\r\nindent_size = 2"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: jogibear9988\npatreon: jogibear9988\n"
  },
  {
    "path": ".github/copilot-instructions.md",
    "content": "## Project Agent Instructions\n\n- Before starting work, read [AGENTS.md](../AGENTS.md) and follow its guidance.\n- Treat [AGENTS.md](../AGENTS.md) as the canonical agent behavior file for this repository.\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\r\ndist/\r\n/.vs\r\ndebug.log\r\n/.vscode\r\n*.tsbuildinfo\r\n"
  },
  {
    "path": "ACKNOWLEDGMENTS",
    "content": "# Acknowledgments\r\n\r\n- Thanks to @notwaldorf who created the original `wizzywid` project (MIT License).\r\n  https://github.com/PolymerLabs/wizzywid\r\n  This was a start for this whole project (even if mostly nothing of the original code is left)\r\n- Thanks to @chdh for plain-scrollbar component\r\n  https://github.com/chdh/plain-scrollbar\r\n- Thanks to @m-thalmann for contextmenujs\r\n  https://github.com/m-thalmann/contextmenujs (also we have heavily modified it)\r\n- Levi Cole for parts of the cssUnits conversion code\r\n  https://stackoverflow.com/a/66569574/579623\r\n- Domi for text-width code\r\n  https://stackoverflow.com/a/21015393\r\n- gwwar for getClosestStackingContext\r\n  https://github.com/gwwar/z-context\r\n- google icons\r\n  https://fonts.google.com/icons"
  },
  {
    "path": "AGENTS.md",
    "content": "## Project Agent Instructions\n\n- AI agents must store memories under the repository [memories/](memories) directory.\n\n## Why These Guidelines Exist\n\n- Agents may make unchecked assumptions, hide confusion, and skip clarifications.\n- Agents may overengineer solutions with unnecessary abstractions and code bloat.\n- Agents may accidentally modify unrelated code or comments as side effects.\n\n## Core Principles\n\n### 1. Think Before Coding\n\n- State assumptions explicitly; if uncertain, ask instead of guessing.\n- Surface ambiguity and tradeoffs rather than silently choosing one interpretation.\n- Push back when a simpler and safer approach exists.\n- Stop and ask for clarification when confusion remains.\n\n### 2. Simplicity First\n\n- Implement the minimum code needed to solve the requested problem.\n- Do not add speculative features, configurability, or abstractions not requested.\n- Avoid handling impossible scenarios just to look complete.\n- If a solution can be significantly simpler, prefer the simpler version.\n\n### 3. Surgical Changes\n\n- Touch only code required by the task.\n- Do not refactor or restyle adjacent code that is unrelated to the request.\n- Match existing project style and patterns.\n- If you see unrelated dead code, mention it; do not remove it unless asked.\n- Remove only unused artifacts created by your own changes.\n\n### 4. Goal-Driven Execution\n\n- Define concrete success criteria before implementation.\n- Prefer verifiable outcomes, usually with tests or explicit checks.\n- For multi-step tasks, use a short plan where each step includes a verification check.\n- Continue iterating until criteria are met or a real blocker is identified.\n\n## Success Signals\n\n- Diffs contain only requested changes.\n- Solutions are simple and avoid unnecessary rewrites.\n- Clarifying questions happen before implementation when needed.\n- Pull requests stay minimal and focused.\n\n## Tradeoff\n\n- These rules prioritize caution and correctness over raw speed on non-trivial tasks.\n- For obvious small changes, use proportional rigor."
  },
  {
    "path": "CLAUDE.md",
    "content": "## Project Agent Instructions\n\n- Before starting work, read [AGENTS.md](AGENTS.md) and follow its guidance.\n- Treat [AGENTS.md](AGENTS.md) as the canonical agent behavior file for this repository.\n"
  },
  {
    "path": "COMPARISON.md",
    "content": "## Similar Frameworks\r\n\r\n| Name                    | Licence  | Edit Source | Split View | Zooming | Resize Transformed | No Iframe | Iframe | Iframe isolation | Modifies Dom | Multiplayer | URL                                    |\r\n|-------------------------|----------|-------------|------------|---------|--------------------| ----------|--------|------------------|--------------|-------------|----------------------------------------|\r\n| web-component-designer  | MIT      | yes         | yes        | yes     | yes                | yes       | yes    | allow-same-origin| no           | yes no          |                                        |\r\n| GrapeJS                 | BSD-3    | yes         | no         | no      | broken             | no        | yes    | no               | yes          | no          | https://grapesjs.com/                  |\r\n| CraftJS                 | MIT      | no          | no         | no      |                    | no        | yes    | no               | yes          | no          | https://craft.js.org/                  |\r\n| raisins                 | MIT      | -           | no         | no      |                    | no        | yes    | yes              | yes          | no          | https://github.com/saasquatch/raisins  |\r\n\r\n\r\n### Description\r\n- Zooming          => can zoom Designer Canvas\r\n- No iframe        => can Design in a Shadow Root (No Iframe, all Components already loaded are usable)\r\n- Iframe           => can Design inside of an Iframe\r\n- Iframe Isolation => iframe can be isolated (against XSS attacks)\r\n- Modifies DOM     => inserts it's overlays directly into edited DOM (this could be Bad, when a Component depends on the DOM or css classes collide)\r\n- Multiplayer      => Multiple Users can design a document at the same time"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 node-projects \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# web-component-designer\r\n\r\nA HTML web component for designing web components and HTML pages based on PolymerLabs wizzywid which can easily be integrated in your own software.\r\nMeanwhile polymer is not used anymore.\r\n\r\n![image](https://user-images.githubusercontent.com/364896/117482820-358e2d80-af65-11eb-97fd-9d15ebf1966f.png)\r\n\r\nThere is also a preview VSCode addon using the designer: https://github.com/node-projects/vs-code-designer-addon\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer\r\n\r\n## Comparison\r\n\r\n[Comparison of Designer Frameworks](COMPARISON.md)\r\n\r\n## Additional NPM Packages\r\n\r\nAll Modules which need an external dependency are now extracted to extra NPM packges.\r\nSo the designer now should work with bundlers.\r\n\r\n| Name                                                                   | Description                                  |\r\n| ---------------------------------------------------------------------- | -------------------------------------------- |\r\n| web-component-designer-codeview-ace                                    |                                              |\r\n| web-component-designer-codeview-codemirror                             |                                              |\r\n| web-component-designer-codeview-codemirror5                            |                                              |\r\n| web-component-designer-codeview-monaco                                 | atm. stuck at monaco 0.50.0 (webcomp supp)   |\r\n| web-component-designer-collaboration-service                           |                                              |\r\n| web-component-designer-htmlparserservice-base-custom-webcomponent      |                                              |\r\n| web-component-designer-htmlparserservice-lit-element                   |                                              |\r\n| web-component-designer-htmlparserservice-nodehtmlparser                |                                              |\r\n| ~~web-component-designer-miniatureview-html2canvas~~                   | never worked correctly                       |\r\n| web-component-designer-stylesheetservice-css-parser                    |                                              |\r\n| ~~web-component-designer-stylesheetservice-css-tools~~                 | deprecated - switched to css-parser          |\r\n| ~~web-component-designer-stylesheetservice-css-tree~~                  | deprecated - did never work very well        |\r\n| web-component-designer-visualization-addons                            |                                              |\r\n| ~~web-component-designer-texteditextension-stylo~~                     | deprecated - stylo is deprecated             |\r\n| ~~web-component-designer-widgets-fancytree~~                           | deprecated - replaced by widgets-wunderbaum  |\r\n| web-component-designer-widgets-wunderbaum                              |                                              |\r\n\r\n## Browser support\r\n\r\n  - Chrome, Firefox and Safari\r\n  \r\n## Projects using it\r\n\r\nA ZPL-Label Designer:\r\n(https://github.com/node-projects/web-component-designer-zpl-demo)\r\n\r\n![image](https://github.com/node-projects/web-component-designer/assets/364896/e1f1e3cc-29a3-4749-a676-389577fab69a)\r\n\r\nA material flow layout editor in a comercial application:\r\n![image](https://github.com/node-projects/web-component-designer/assets/364896/0062562a-4224-4b11-aaa4-03e31494fcfa)\r\n\r\nA flow chart editor\r\n![image](https://github.com/user-attachments/assets/bcefa433-27fd-4be6-b3b7-a8264d66bef5)\r\n\r\n## Demo\r\n\r\nlook at: https://node-projects.github.io/web-component-designer-demo/index.html\r\nrepository: https://github.com/node-projects/web-component-designer-demo\r\n\r\nor a simple one: https://node-projects.github.io/web-component-designer-simple-demo/index.html\r\nrepository: https://github.com/node-projects/web-component-designer-simple-demo\r\n\r\n## What is needed\r\n\r\n- @node-projects/base-custom-webcomponent a very small basic webcomponent library (maybe this will be included directly later, to be dependecy free)\r\n- optional - ace code editor\r\n- optional - monaco code editor (if you use code-view-monaco)\r\n- optional - code mirror code editor (if you use code-view-codemirror) (workin but buggy)\r\n- optional - fancytree (if you use tree-view-extended, palette-tree-view or bindable-objects-browser)\r\n\r\n## Features we are working on\r\n\r\nhttps://github.com/node-projects/web-component-designer/issues\r\n\r\n## Developing\r\n\r\n  * This will install all required packages, link all the npm packages and build everyone once. (in mac or linux you need to run the script with sudo, or the \"npm link\" will not work)\r\n\r\n```\r\n  $ npm run develop\r\n```\r\n\r\n## Using\r\n\r\nAt first you have to setup a service container providing services for history, properties, elements, ...\r\n\r\n## Code Editor\r\n\r\nYou can select to use one of 3 code editors available (ACE, CodeMirrow, Monaco).\r\nIf you use one of the widgets, you need to include the JS lib in your index.html and then use the specific widget.\r\n\r\n## TreeView\r\n\r\nWe have 2 tree components. One independent and one feature rich which uses FancyTree (and cause of this it needs JQuery and JqueryUI).\r\n\r\n## DragDrop\r\n\r\nIf you'd like to use the designer on mobile, you need the mobile-drag-drop npm library.\r\nYour index.html should be extended as follows:\r\n\r\n    <link rel=\"stylesheet\" href=\"/node_modules/mobile-drag-drop/default.css\">\r\n    <script src=\"/node_modules/mobile-drag-drop/index.js\"></script>\r\n\r\n## Copyright notice\r\n\r\nThe Library uses Images from the Chrome Dev Tools, see\r\nhttps://github.com/ChromeDevTools/devtools-frontend/tree/main/front_end/Images/src\r\nand\r\nhttps://github.com/ChromeDevTools/devtools-frontend/blob/main/LICENSE\r\n\r\n"
  },
  {
    "path": "editor.code-workspace",
    "content": "{\n\t\"folders\": [\n\t\t{\n\t\t\t\"path\": \".\"\n\t\t},\n\t\t{\n\t\t\t\"path\": \"../web-component-designer-demo\"\n\t\t}\n\t],\n\t\"settings\": {\n\t\t\"jestrunner.debugOptions\": {\n\t\t\t\"runtimeArgs\": [\n\t\t\t\t\"--experimental-vm-modules\", \"--enable-source-maps\"\n\t\t\t]\n\t\t}\n\t}\n}"
  },
  {
    "path": "jest.config.js",
    "content": "export default {\n  testMatch: ['**/?(*.)+(spec|test).+(mts|ts|tsx|mjs|js)'],\n  preset: \"ts-jest/presets/default-esm\",\n  testEnvironment: \"node\",\n  extensionsToTreatAsEsm: ['.ts', '.mts'],\n  moduleNameMapper: {\n    '^(\\\\.{1,2}/.*)\\\\.js$': '$1'\n  },\n  transform: {\n    '^.+\\\\.(mts|ts|tsx|mjs|js)$': [\n      \"ts-jest\",\n      {\n        \"useESM\": true,\n        \"tsconfig\": {\n          \"allowJs\": true\n        }\n      }\n    ]\n  },\n  transformIgnorePatterns: [\n    '/node_modules/(?!(?:@node-projects/base-custom-webcomponent)(?:/|$))'\n  ]\n}"
  },
  {
    "path": "memories/attribute-source-part-jump-note.md",
    "content": "Attribute property-grid source navigation uses `attribute:${attributeName}` source\nparts generated by `AbstractHtmlWriterService`. Set the design item selection\nfirst if needed, then call `selectionService.setSelectedPart(sourcePart)`; the\ndocument container must listen to selection refresh events as well as full\nselection changes so same-element source-part jumps update the code selection.\n"
  },
  {
    "path": "memories/default-html-parser-source-parts-note.md",
    "content": "DefaultHtmlParserService cannot read original source ranges from DOMParser nodes.\nAfter full parses, run the HTML writer with `updatePositions=true` on the created\ndesign items so attribute/source-part navigation works immediately from the\nnormalized designer HTML, matching what code view receives after switching back.\n"
  },
  {
    "path": "memories/freehand-path-interpolation-note.md",
    "content": "Freehand `DrawPathTool` can interpolate linear path points during drag with `interpolatePoints: true` and an optional `interpolationDistance`; the stock default tool opts in with 5px maximum spacing. Keep point-to-point path mode separate so its shift/angle snapping behavior stays unchanged.\n"
  },
  {
    "path": "memories/getboxquads-svg-fast-path-removal.md",
    "content": "# getBoxQuads SVG visual boxes\n\nSVGGraphicsElement quads should be built from a local SVG visual box, then transformed into the requested coordinate system. A viewport `getBoundingClientRect()` shortcut is not generally correct because it is already post-transform and loses orientation under rotated/3D HTML ancestors. A raw `getBBox()` is also not enough because native Firefox includes stroke bounds for SVG lines and paths.\n\nThe current polyfill computes a local visual box from `getBBox()` plus stroke inflation, uses `getScreenCTM()` when there is no transformed HTML ancestor, and otherwise uses the existing accumulated transform matrix. This covers plain lines, nested SVG-in-SVG lines, CSS-transformed SVGs, and the i14 stroke-bound path sample.\n"
  },
  {
    "path": "memories/mermaid-diagram-support-plan.md",
    "content": "# Mermaid diagram support rollout\n\nGoal: extend the Mermaid package from a flowchart-first designer into a Mermaid designer that can preview every Mermaid diagram and progressively offer structured visual editing for each diagram type.\n\nPhases:\n1. Flowchart: finish directions, labels, full shape syntax, markdown labels, edge movement, selection mapping, and Mermaid-layout import.\n2. Sequence: participants, messages, activations, notes, loops/alt/opt blocks, writer/parser selection mapping.\n3. Class: classes/interfaces, members, methods, relationships, annotations, namespaces.\n4. State: states, transitions, composite states, notes, forks/joins.\n5. ER: entities, attributes, relationship cardinalities.\n6. Remaining text-first diagrams: Gantt, journey, pie, quadrant, requirement, gitgraph, mindmap, timeline.\n7. Newer visual diagrams: sankey, xy chart, block, packet, kanban, architecture, radar, treemap, venn, ishikawa, wardley, treeview.\n\nPrinciple: every diagram type should first render in preview through Mermaid, then gain a typed parser/model/writer only when its visual editing semantics are defined.\n\nCurrent document metadata:\n- Root design item attributes store the current editable family: `data-mermaid-diagram-type` and, for flowcharts, `data-mermaid-flowchart-direction`.\n- The Mermaid package installs a root property group named `mermaid` so these values are editable when the root item is selected.\n- The writer honors root metadata only when doing so will not drop incompatible existing widgets; actual conversion between diagram families is still a separate future task.\n- The Mermaid writer opts into root-item serialization so root-only documents can still write `title`, diagram type, and flowchart direction even when there are no child design items.\n- Palette entries are filtered by root `diagramType`: flowchart shows flowchart nodes only, sequence shows sequence controls. Flowchart edges are intentionally not in the palette because they are created by the connector tool.\n- Mindmap is now a root `diagramType` with a filtered palette entry, root title support, indentation parser/writer, and a visual `mermaid-mindmap-node` widget. Mindmap nodes are real containers: parsed child lines become nested design items, and the writer emits indentation by walking `item.children()`. Import asks Mermaid to render the mindmap SVG first and maps `g.node.mindmap-node` positions back into the designer; the simple hierarchical placement is only a fallback when SVG layout extraction is unavailable. Direct parent nodes automatically draw curved child connection lines with depth-based stroke thickness. Icons/classes/config layout are still future mindmap property work.\n- Requirement diagram is now a root `diagramType` with requirement/element palette entries, direction property, block parser/writer, Mermaid-rendered node positioning, editable `mermaid-requirement-node` widgets, and relationship widgets for `contains`, `copies`, `derives`, `satisfies`, `verifies`, `refines`, and `traces` in both forward and reverse syntax forms. Styling/class syntax is still future requirement property work.\n- Flowchart frontmatter now preserves non-title content such as `config: htmlLabels: false`; flowchart labels with Markdown markers or embedded newlines are written as Mermaid markdown strings.\n- Flowchart support now covers Mermaid v11 expanded `@{ shape: ... }` nodes for the documented shape aliases, subgraph containers with nested node writing, special edge connectors including circle/cross/multidirectional/invisible/min-length forms, edge ids, and raw flowchart directives for `style`, `classDef`, `class`, `click`, and `linkStyle` so styles/classes/animation definitions round-trip. Node rendering is still an editable approximation of Mermaid's SVG, not pixel-identical for every exotic shape.\n- Mermaid node widgets render inline Markdown safely in the designer for bold/italic markers and multiline labels, using DOM nodes rather than raw HTML injection.\n"
  },
  {
    "path": "memories/monaco-selection-coalesce-note.md",
    "content": "# Monaco selection coalescing\n\nWhen designer selection changes are mirrored into split/code view, the same source range can be requested more than once around a designer content update. `DocumentContainer` now coalesces identical source-range selections before calling the active code view.\n\n`CodeViewMonaco` also tracks the delayed native Monaco selection call. Pending identical selections are ignored, and pending delayed selections are cleared when the model is updated, so a selection queued against the old model is not applied after the refreshed model and followed by the same fresh selection.\n"
  },
  {
    "path": "memories/node-html-parser-source-parts-note.md",
    "content": "The node-html-parser parser service must populate designItemDocumentPositionService\nsource parts during parse, not only element positions. Code-to-design transitions\ndo not run the writer first, so attribute jumps need parser-generated\n`attribute:${name}` and `attribute:${name}/value` source parts immediately after\n`parseDesignerHTML`.\n"
  },
  {
    "path": "memories/source-part-selection-coalesce-note.md",
    "content": "When selecting a source part on a design item that may not already be primary,\npass the source part into `selectionService.setSelectedElements(..., sourcePart)`\ninstead of calling `setSelectedElements()` and then `setSelectedPart()`. Splitting\nthose calls emits two code-view selections (element range, then source-part range)\nand makes the text editor jump.\n"
  },
  {
    "path": "memories/svg-affine-overlay-point-conversion-note.md",
    "content": "For the getBoxQuads polyfill SVG graphics fast path, diagonal SVG lines need\nstroke expansion along the normal vector (`strokeWidth / 2 * abs(dy|dx) / length`)\ninstead of the generic `strokeWidth * 2` inflation. Keep the legacy generic SVG\ninflation for non-line graphics unless replacing it with a fully native-compatible\nstroke bbox calculation; the existing path fixture relies on that behavior.\n"
  },
  {
    "path": "memories/svg-geometry-placement-transform-offset-note.md",
    "content": "- SVG geometry drag commit receives the live preview translation in visual/container\n  coordinates. Before writing geometry attributes (`x`, `y`, path points, etc.) for\n  elements that already have a CSS `transform`, map that offset through the inverse\n  2D linear part of the original transform; otherwise rotated/scaled/skewed SVG\n  shapes preview correctly but jump on mouseup.\n- The conversion intentionally uses only `a/b/c/d` and ignores translation, because\n  it is transforming an offset vector, not an absolute point.\n"
  },
  {
    "path": "memories/unified-geometry-click-selection-no-commit.md",
    "content": "# Unified geometry click selection\n\n`UnifiedGeometryExtension` creates drag state on handle pointer-down so it can keep pointer capture and support immediate dragging. A plain click on a handle must not commit geometry on pointer-up. Track `geometryChanged` in the drag state, set it only after a non-zero pointer movement has updated geometry, and commit on pointer-up only when that flag is true.\n"
  },
  {
    "path": "memory/context-menu-copy-paste-pattern.md",
    "content": "- Designer context menus are registered centrally in DefaultServiceBootstrap and grouped with ChildContextMenu plus SeperatorContextMenu.\n- ContextMenu renders an item with title '-' as a divider, so submenu spacers should use that sentinel item.\n- Clipboard-derived features can either write richer clipboard formats in copyItems or parse the current clipboard payload on demand; prefer the on-demand path when the behavior should work across browser instances without service-local state.\n- For paste-format style transfer, prefer calling getPasteItems() and reading styles() from the first parsed design item instead of adding clipboard-specific methods to copy-paste services.\n\n"
  },
  {
    "path": "memory/context-menu-popover-anchor-note.md",
    "content": "- In ContextMenu, popover top-layer rendering becomes non-interactive when combined with CSS anchor positioning in the demo browser check.\n- For the popover path, use explicit fixed left/top placement with viewport fallbacks; reserve anchor positioning for non-popover fallback paths only.\n- Deferred registration of global mousedown/contextmenu close listeners avoids immediately closing menus opened from mousedown-based triggers like undo/redo hold menus.\n"
  },
  {
    "path": "memory/css-numeric-percent-measured-size-note.md",
    "content": "- For CSS numeric size properties, `to %` conversion should prefer the element's measured box size (`getBoundingClientRect`) over the raw numeric text value so the unit switch preserves the current rendered size.\n- Keep `% -> px` on the normal reference-size path unless a concrete regression shows otherwise.\n- Numeric scrub drag is more reliable in the property grid with window-level pointer move/up/cancel listeners than element-local move/up listeners.\n"
  },
  {
    "path": "memory/css-numeric-preview-lock-shadow-host-note.md",
    "content": "- NumericStyleInput should own its preview display lock internally; editor-level refresh suppression alone is not enough because property-grid refreshes can still race during drag/hold preview.\n- Clear the display lock when an explicit new value arrives that differs from the current underlying `_value`; keep it only across repeated stale refreshes.\n- Percent conversion for root elements must look past `parentElement` and fall back across shadow boundaries via `getRootNode().host`, then `ownerDocument.body/documentElement`.\n- Interactive drag/step changes should snap to step increments and format to step precision, not just use a generic 4-decimal formatter.\n"
  },
  {
    "path": "memory/css-zoom-placement-preview-note.md",
    "content": "- Drag preview transforms in DefaultPlacementService and AbsolutePlacementService must divide the visual movement by the dragged element's CSS `zoom` factor before composing `translate(...)`.\n- `getBoxQuads` must wrap the element transform with the zoom matrix (`zoom * transform`, not `transform * zoom`) so overlay geometry sees the same zoom-scaled translation the browser renders.\n- TransformOriginExtension should draw its marker with `getResultingTransformationBetweenElementAndAllAncestors(..., canvas)` so the overlay includes self zoom, but it should keep using `element.convertPointFromNode(..., canvas)` for pointer-up commits so authored `transform-origin` stays in local element units.\n\n- `zoom` scales transform translation during preview, but the final `placeDesignItem(..., 'position')` commit path should stay in layout units.\n"
  },
  {
    "path": "memory/cssom-shorthand-test-note.md",
    "content": "- Jest node/jsdom in this repo is not reliable for CSS shorthand serialization checks.\n- For CssCombiner tests, fake document.createElement/style before importing the module because it creates a helper element at module load.\n- Prefer focused fake CSSOM tests or real-browser validation when changing cssText/shorthand behavior.\n"
  },
  {
    "path": "memory/jest-esm-validation-note.md",
    "content": "- In packages/web-component-designer, Jest currently has unrelated ESM/module-resolution issues for tests that import dist ESM output or dependencies like @node-projects/base-custom-webcomponent from node_modules.\n- For new service wiring in this package, npm run tsc is the reliable validation path unless Jest config is widened on purpose.\n\n- For isolated DOM-focused tests, replacing a single `css` helper import with a local `CSSStyleSheet` creator can avoid the Jest ESM boundary without widening package-level Jest transforms.\n- For custom-element or editor work in this package, extracting pure parsing/configuration helpers into source files without DOM or @node-projects/base-custom-webcomponent imports gives Jest a stable focused validation target without changing package-wide ESM transforms.\n\n"
  },
  {
    "path": "memory/jest-source-import-style-note.md",
    "content": "- In packages/web-component-designer Jest tests, import source modules with the package's existing ts-jest style (for example '../src/.../Module' without a '.js' suffix); using '.js' can fail module resolution in focused test runs.\n"
  },
  {
    "path": "memory/linked-package-type-compat-note.md",
    "content": "- In this repo's linked-package setup, widening exported structural types like ServiceContainer or InstanceServiceContainer can break demo/package compatibility because helper packages resolve their own nested @node-projects/web-component-designer types.\n- Prefer optional public properties or internal `as any` registration for new cross-cutting services when the demo consumes linked packages compiled against older type surfaces.\n- The demo HTML loads dist/appShell.js, so when normal demo tsc is blocked by the linked-package type mismatch, `npx tsc --noCheck` is the pragmatic way to refresh runtime artifacts for UI changes.\n\n- In web-component-designer-demo, running `npm i` can replace the local linked `@node-projects/web-component-designer` package with the published node_modules copy; if browser behavior disagrees with passing source/tests, rebuild the package, run `npm link`, then rerun `npm run linkAll` in the demo before debugging further.\n\n"
  },
  {
    "path": "memory/manual-collab-snapshot-request.md",
    "content": "- For manual copy/paste WebRTC signaling, do not infer initial snapshot ownership from receiving `hello`; the copied direction can be opposite of the joining direction.\n- Carry an explicit `requestInitialSnapshot` flag in `hello` / `hello-ack`, and decide it from the local document state so the empty peer asks for content instead of sending an empty snapshot back.\n"
  },
  {
    "path": "memory/overlay-refresh-pattern.md",
    "content": "- Designer extensions should prefer passing existing SVG nodes back into _drawLine/_drawCircle/_drawPath and gate refresh work with _valuesHaveChanges.\n- Only rebuild overlays when geometry structure changes (segment count/type or control-point presence), otherwise update positions/styles in place to preserve pointer capture and avoid stale handles.\n"
  },
  {
    "path": "memory/pointertool-selected-quad-drag-note.md",
    "content": "- PointerTool drag start must distinguish click target from drag source.\n- After alt-selecting an element underneath another, a plain click should still reselect the topmost hit element.\n- A plain drag should move the selected underlying item when the pointer is inside that item's border quad; recompute initial drag offset from the chosen drag source element.\n"
  },
  {
    "path": "memory/property-grid-designitem-cache-sync-note.md",
    "content": "- PropertyGrid refresh can run correctly while editors still show stale values if external DOM changes bypass DesignItem APIs; properties services read DesignItem `_attributes`/`_styles` caches.\n- Fix: add `IDesignItem.refreshAttributesAndStylesFromElement()` and call it before PropertyGrid/PropertyGridWithHeader refresh or rebuild work so `getValue`, `isSet`, `attributes()`, and `getAllStyles()` see live DOM state.\n"
  },
  {
    "path": "memory/property-grid-preview-recreation-fix.md",
    "content": "# Property Grid Preview Element Recreation Fix\n\n## Root Cause\n`CssCurrentPropertiesService.getRefreshMode()` returns `RefreshMode.fullOnValueChange` (value 2).\nWhen NumericStyleInput's preview sets `element.style.setProperty(...)`, the MutationObserver fires \n`PropertyGrid._mutationOccured()`, which calls `createElements()` for tabs with `fullOnValueChange`.\nThis DESTROYS and RECREATES all editors (including the active NumericStyleInput) mid-preview.\n\n## Fix\n- PropertyGrid keeps a single `MutationObserver` with `attributeOldValue: true`.\n- `IPropertiesService` now has an optional `shouldRecreatePropertyListOnMutation(...)` hook.\n- `AbstractPropertiesService` defaults to recreating only when an attribute is added or removed.\n- `CssCurrentPropertiesService` and `CssCustomPropertiesService` override that hook to compare old/new inline style declaration names, so value-only style changes refresh editors but declaration add/remove still rebuilds.\n- PropertyGrid also needs `designerCanvas.onContentChanged` and relevant `contentService.onContentChanged` subscriptions, because external property changes can arrive through undo/content notifications without a direct selected-element attribute mutation.\n\n- `hasEditorInPreview()` and the schema-signature recreation path were removed.\n\n\n## Key Enum Values (IPropertiesService.ts)\n```\nRefreshMode { none=0, full=1, fullOnValueChange=2, fullOnClassChange=3 }\n```\n- CssCurrentPropertiesService: fullOnValueChange (2) - the \"styles\" tab\n- CssPropertiesService: none (0) - the \"layout\" tab\n- AttributesPropertiesService: fullOnValueChange (2)\n- NativeElementsPropertiesService: full (1)\n"
  },
  {
    "path": "memory/resize-left-top-initial-local-axis-note.md",
    "content": "- Left/top resize handles cannot derive drag deltas by converting the current pointer into the element's current local space, because the element origin moves during the resize and cancels part of the delta.\n- Compute resize deltas from the pointer's movement in the initial local-axis basis (initial element-local-to-canvas matrix, translation removed), then apply opposite-corner correction from the current local border-box anchor converted back to canvas/parent space.\n- Rereading live getBoxQuads() during the same pointermove turn can add jitter; prefer current local anchor geometry plus convertPointFromNode()."
  },
  {
    "path": "memory/straighten-line-screen-y-note.md",
    "content": "- PathDataPolyfill.straightenLine uses calculateAlpha's clockwise screen-space angle; reconstruct snapped points with `y - Math.sin(rad) * length` or vertical directions invert.\n- Added PathDataPolyfill.test.ts with stubbed SVG globals because the helper module patches SVG element prototypes at import time."
  },
  {
    "path": "memory/svg-getctm-double-transform-note.md",
    "content": "- In the getBoxQuads transform walk, do not seed SVGGraphicsElement (non-root SVG) with getElementTransformWithZoom for self transforms when the loop also multiplies getCTM(); getCTM already includes the element's local SVG/CSS transform and double-applies rotate/scale otherwise.\n- A rotated CSS-sized SVG rect can expose this immediately: 90deg rotate produced a 180deg-style quad until the self CSS transform was skipped and getCTM handled it alone.\n- For SVG geometry placement commit, the drag preview translation extracted from element.style.transform is already in local geometry coordinates; do not rotate it again before calling placeDesignItem, or rotated shapes jump on mouseup.\n"
  },
  {
    "path": "memory/svg-rect-style-geometry-write-note.md",
    "content": "- For SVG rect editing in the unified geometry path, read rendered geometry from getBBox() instead of rect.x/rect.width baseVal values when CSS width/height may be the source of truth.\n- Preserve style-backed SVG geometry on write by carrying per-property serialization hints (style vs attribute, unit) through the geometry model, then applying writes with setStyle/style.setProperty instead of always setAttribute.\n- This avoids stacked rect corner handles for CSS-sized rects and prevents drag/resize from injecting width/height attributes when those properties were authored in inline style.\n"
  },
  {
    "path": "memory/transform-preview-sync-pattern.md",
    "content": "- When previewing transform edits via direct writes to element.style.transform, always sync or clear that inline transform after commit.\n- If the persisted transform stays local, restore the local style value; if it is stylesheet-backed, clear the inline preview so it does not override the stylesheet declaration.\n"
  },
  {
    "path": "memory/transformed-resize-grid-local-point-pattern.md",
    "content": "- For resize and grid interactions under transformed elements or transformed ancestors, do not derive local deltas from axis-aligned rects or an element-only inverse matrix.\n- Convert canvas/overlay points back into element-local coordinates with convertPointFromNode (via the getBoxQuads polyfill), then compute size deltas, grid cell hits, and grid track drags from those local points.\n- Grid helpers should expose local cell/gap coordinates separately from overlay coordinates so placement, hover, and resize logic all share the same transform-safe hit-testing path.\n- During live resize, getBoxQuads can still reflect the pre-resize box inside the same pointermove turn; for opposite-corner correction, convert the current local fixed-anchor point back to canvas instead of rereading the current quad.\n\n"
  },
  {
    "path": "memory/undo-group-content-changed-commit-note.md",
    "content": "- In packages/web-component-designer, grouped undo operations should not emit instanceServiceContainer.onContentChanged from UndoService.execute while a ChangeGroup is open.\n- ChangeGroup now accumulates IContentChanged payloads from execute calls and committed subgroups, and UndoService emits them once from commitTransactionItem on the outermost commit.\n- This keeps documentContainer, treeView, and propertyGrid from reacting to intermediate states during grouped edits.\n"
  },
  {
    "path": "memory/webrtc-cross-machine-ice-config-note.md",
    "content": "- Manual WebRTC signaling in collaboration-service worked on the same machine but failed across machines until the transport exposed optional `rtcConfiguration`.\n- Root cause: the transport created `RTCPeerConnection()` with no configurable STUN/TURN servers, so cross-machine connections depended only on local host candidates.\n- Demo support was added via `collabIceServer` query params and optional JSON `collabRtcConfiguration`; when the demo compiles against an older published package, pass the new option object as `any` at the constructor call site to stay compatible.\n- Cloudflare TURN should be integrated through the official server-side `generate-ice-servers` flow from `https://developers.cloudflare.com/realtime/turn/generate-credentials/`; a direct public `https://speed.cloudflare.com/turn-creds` link is not the supported integration path even though the speed test site can fetch credentials internally.\n- Cloudflare's official TURN API endpoint at `https://rtc.live.cloudflare.com/v1/turn/keys/{TURN_KEY_ID}/credentials/generate-ice-servers` is callable from the browser with user-supplied key id and API token; for the demo this is acceptable as a dev/test convenience, but it still exposes the long-lived API token to the browser.\n\n- Older static gist TURN hosts like `numb.viagenie.ca`, `turn.bistri.com`, `turn.anyfirewall.com`, and the raw gist `hubl.in` entries did not verify and should not be exposed as presets.\n- OpenRelay is still live as a free-tier provider, but it needs signup/API credentials or auth-secret integration, so expose it as a manual provider workflow rather than an unauthenticated preset.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@node-projects/monorepo\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"test\": \"node --experimental-vm-modules node_modules/jest/bin/jest.js\",\n    \"develop\": \"npm i && npm run link && npm run build\",\n    \"link\": \"npm run link --workspaces\",\n    \"watch\": \"npm run watch --workspaces\",\n    \"build\": \"npm run build --workspaces\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"devDependencies\": {\n    \"@blockly/zoom-to-fit\": \"^6.0.9\",\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\n    \"@node-projects/css-parser\": \"^5.0.0\",\n    \"@node-projects/lean-he-esm\": \"^3.3.0\",\n    \"@node-projects/node-html-parser-esm\": \"^6.2.0\",\n    \"@node-projects/propertygrid.webcomponent\": \"^1.2.3\",\n    \"@types/codemirror\": \"^5.60.15\",\n    \"@types/css-tree\": \"^2.3.8\",\n    \"@types/jest\": \"^29.5.14\",\n    \"@types/jquery\": \"^3.5.32\",\n    \"@types/jquery.fancytree\": \"0.0.11\",\n    \"@types/node\": \"^22.8.6\",\n    \"ace-builds\": \"^1.36.3\",\n    \"blockly\": \"^11.1.1\",\n    \"codemirror\": \"^6.0.1\",\n    \"codemirror5\": \"npm:codemirror@^5.0.0\",\n    \"css-tree\": \"^3.0.0\",\n    \"esprima-next\": \"^6.0.3\",\n    \"html2canvas\": \"*\",\n    \"jest\": \"^29.7.0\",\n    \"jest-environment-jsdom\": \"^30.3.0\",\n    \"jquery\": \"^3.7.1\",\n    \"jquery.fancytree\": \"^2.38.3\",\n    \"jsbarcode\": \"^3.11.6\",\n    \"long\": \"^5.2.3\",\n    \"mdn-data\": \"^2.12.1\",\n    \"monaco-editor\": \"^0.52.0\",\n    \"ts-jest\": \"^29.2.5\",\n    \"typescript\": \"^5.8.3\",\n    \"typescript-lit-html-plugin\": \"^0.9.0\",\n    \"wunderbaum\": \">=0.13.0\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/.npmignore",
    "content": "src/\r\ntest/\r\ntests/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer/README.md",
    "content": "# web-component-designer\r\n\r\n```It's now considered beta. It works, we use it in production, but there are many more features to come```\r\n\r\nA HTML web component for designing web components and HTML pages based on PolymerLabs wizzywid which can easily be integrated in your own software.\r\nMeanwhile polymer is not used anymore.\r\n\r\n![image](https://user-images.githubusercontent.com/364896/117482820-358e2d80-af65-11eb-97fd-9d15ebf1966f.png)\r\n\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer\r\n\r\n     npm i @node-projects/web-component-designer\r\n\r\n## Browser support\r\n\r\n  - Chrome/Firefox & Safari\r\n\r\n## Developing\r\n\r\n  * Install dependencies\r\n```\r\n  $ npm install\r\n```\r\n\r\n  * Compile typescript after doing changes\r\n```\r\n  $ npm run build (if you use Visual Studio Code, you can also run the build task via Ctrl + Shift + B > tsc:build - tsconfig.json)\r\n```\r\n\r\n  * *Link node module*<br/>\r\n```\r\n  $ npm link \r\n```\r\n\r\n## Using\r\n\r\nAt first you have to setup a service container providing services for history, properties, elements, ...\r\n\r\n## Code Editor\r\n\r\nYou can select to use one of 3 code editors available (ACE, CodeMirrow, Monaco).\r\nIf you use one of the widgets, you need to include the JS lib in your index.html and then use the specific widget.\r\n\r\n## TreeView\r\n\r\nWe have 2 tree components. One independent and one feature rich which uses FancyTree (and cause of this it needs JQuery and JqueryUI).\r\n\r\n## DragDrop\r\n\r\nIf you'd like to use the designer on mobile, you need the mobile-drag-drop npm library.\r\nYour index.html should be extended as follows:\r\n\r\n    <link rel=\"stylesheet\" href=\"/node_modules/mobile-drag-drop/default.css\">\r\n    <script src=\"/node_modules/mobile-drag-drop/index.js\"></script>\r\n\r\n## Keys\r\n\r\nPointer Tool:\r\nalt:   select element behind\r\nshift:  draw selection rect\r\nctrl:  add/remove from selection\r\nctrl+shift: pan\r\n\r\n## Copyright notice\r\n\r\nThe Library uses Images from the Chrome Dev Tools, see\r\nhttps://github.com/ChromeDevTools/devtools-frontend/tree/main/front_end/Images/src\r\nand\r\nhttps://github.com/ChromeDevTools/devtools-frontend/blob/main/LICENSE\r\n"
  },
  {
    "path": "packages/web-component-designer/_esbuild.js",
    "content": "import * as esbuild from 'esbuild';\nimport { minifyHTMLLiteralsPlugin } from 'esbuild-plugin-minify-html-literals';\n\nawait esbuild.build({\n  entryPoints: ['./dist/index-all.js'],\n  outfile: './dist/index-min.js',\n\n  bundle: true,\n  format: 'esm',\n  minify: true,\n  sourcemap: true,\n  platform: 'neutral',\n\n  external: ['@node-projects/base-custom-webcomponent', './NpmPackageHacks.json'  ],\n\n  plugins: [\n    minifyHTMLLiteralsPlugin()\n  ]\n}).catch(() => process.exit(1));\n"
  },
  {
    "path": "packages/web-component-designer/assets/designerCanvasIframe.html",
    "content": "<html>\n\n<head>\n    <script>\n        window.addEventListener(\"message\",\n            (event) => {\n                const data = event.data;\n                const obj = JSON.parse(data);\n                switch (obj.type) {\n                    case 'elementsFromPoint': {\n                        const ret = document.elementsFromPoint(...obj.par);\n                    }\n                }\n            }, false);\n    </script>\n</head>\n\n<body></body>\n\n</html>"
  },
  {
    "path": "packages/web-component-designer/assets/images/chromeDevtools/LICENSE",
    "content": "// Copyright 2014 The Chromium Authors. All rights reserved.\r\n//\r\n// Redistribution and use in source and binary forms, with or without\r\n// modification, are permitted provided that the following conditions are\r\n// met:\r\n//\r\n//    * Redistributions of source code must retain the above copyright\r\n// notice, this list of conditions and the following disclaimer.\r\n//    * Redistributions in binary form must reproduce the above\r\n// copyright notice, this list of conditions and the following disclaimer\r\n// in the documentation and/or other materials provided with the\r\n// distribution.\r\n//    * Neither the name of Google Inc. nor the names of its\r\n// contributors may be used to endorse or promote products derived from\r\n// this software without specific prior written permission.\r\n//\r\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "packages/web-component-designer/assets/images/chromeDevtools/info.txt",
    "content": "Images from\r\nhttps://github.com/ChromeDevTools/devtools-frontend/tree/main/front_end/Images/src"
  },
  {
    "path": "packages/web-component-designer/assets/images/treeview/license.txt",
    "content": "WPF Designer Icons are taken from\nFugue Icons Library:\nLicense: Creative Commons Attribution 3.0 License\nhttp://p.yusukekamiyamane.com/\n\nCopied from the Fugue Icon Library and left unmodified:\n\n    - Icons.16x16.WpfOutline.Eye.png => eyeopen.png\n    - Icons.16x16.WpfOutline.EyeClosed.png => eyeclosed.png\n    - lock.png"
  },
  {
    "path": "packages/web-component-designer/config/elements-native.json",
    "content": "{\r\n  \"elements\":\r\n  [\r\n    \"div\",\r\n    \"label\",\r\n    \"input\",\r\n    \"textarea\",\r\n    \"select\",\r\n    {\"tag\" : \"button\", \"defaultWidth\": \"80px\", \"defaultHeight\": \"30px\", \"defaultContent\": \"Button\" },\r\n    \"img\",\r\n    \"iframe\",\r\n    {\"tag\" : \"a\" },\r\n    \"p\",\r\n    \"span\",\r\n    \"b\",\r\n    \"i\",\r\n    \"u\",\r\n    \"br\",\r\n    \"em\",\r\n    \"q\",\r\n    \"small\",\r\n    \"strong\",\r\n    \"form\",\r\n    \"h1\",\r\n    \"h2\",\r\n    \"h3\",\r\n    \"h4\",\r\n    \"h5\",\r\n    \"h6\",\r\n    \"ol\",\r\n    \"ul\",\r\n    \"li\",\r\n    \"pre\",\r\n    \"table\",\r\n    \"caption\",\r\n    \"colgroup\",\r\n    \"col\",\r\n    \"thead\",\r\n    \"th\",\r\n    \"tbody\",\r\n    \"tr\",\r\n    \"td\",\r\n    \"tfoot\"\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/jest.config.js",
    "content": "export default {\n  roots: ['<rootDir>/tests'],\n  testMatch: ['**/?(*.)+(spec|test).+(mts|ts|tsx|mjs|js)'],\n  preset: \"ts-jest/presets/default-esm\",\n  testEnvironment: \"node\",\n  extensionsToTreatAsEsm: ['.ts', '.mts'],\n  moduleNameMapper: {\n    '^(\\\\.{1,2}/.*)\\\\.js$': '$1'\n  },\n  transform: {\n    '^.+\\\\.(mts|ts|tsx|mjs|js)$': [\n      \"ts-jest\",\n      {\n        \"useESM\": true,\n        \"tsconfig\": {\n          \"allowJs\": true\n        }\n      }\n    ]\n  },\n  transformIgnorePatterns: [\n    '/node_modules/(?!(?:@node-projects/base-custom-webcomponent)(?:/|$))'\n  ]\n}"
  },
  {
    "path": "packages/web-component-designer/jsr.json",
    "content": "{\n    \"name\": \"@node-projects/web-component-designer\",\n    \"version\": \"0.1.180\",\n    \"exports\": \"./src/index.ts\"\n}"
  },
  {
    "path": "packages/web-component-designer/package.json",
    "content": "{\r\n  \"description\": \"A WYSIWYG designer webcomponent for html components\",\r\n  \"name\": \"@node-projects/web-component-designer\",\r\n  \"version\": \"0.2.24\",\r\n  \"type\": \"module\",\r\n  \"main\": \"./dist/index.js\",\r\n  \"author\": \"jochen.kuehner@gmx.de\",\r\n  \"license\": \"MIT\",\r\n  \"scripts\": {\r\n    \"test\": \"node --experimental-vm-modules ../../node_modules/jest/bin/jest.js\",\r\n    \"tsc\": \"tsc\",\r\n    \"build\": \"tsc\",\r\n    \"link\": \"npm link\",\r\n    \"watch\": \"pm2 start tsc --watch\",\r\n    \"prepublishOnly\": \"npm run build && npm run bundle\",\r\n    \"bundle\": \"node _esbuild.js\"\r\n  },\r\n  \"dependencies\": {\r\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"@types/node\": \"^22.8.6\",\r\n    \"esbuild\": \"^0.25.10\",\r\n    \"esbuild-plugin-minify-html-literals\": \"^3.0.0\",\r\n    \"mdn-data\": \"^2.4.2\"\r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/Constants.ts",
    "content": "export const dragDropFormatNameElementDefinition = 'text/json/elementdefintion';\r\nexport const dragDropFormatNameBindingObject = 'text/json/bindingobject';\r\nexport const dragDropFormatNamePropertyGrid = 'text/json/propertydrop';\r\n\r\nlet imporUrl = new URL((import.meta.url));\r\nexport var assetsPath = imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/';"
  },
  {
    "path": "packages/web-component-designer/src/commandHandling/CommandType.ts",
    "content": "export enum CommandType {\r\n  'copy' = 'copy',\r\n  'paste' = 'paste',\r\n  'cut' = 'cut',\r\n  'delete' = 'delete',\r\n  'undo' = 'undo',\r\n  'redo' = 'redo',\r\n\r\n  'holdUndo' = 'holdUndo',\r\n  'holdRedo' = 'holdRedo',\r\n\r\n  'rotateCounterClockwise' = 'rotateCounterClockwise',\r\n  'rotateClockwise' = 'rotateClockwise',\r\n  'mirrorHorizontal' = 'mirrorHorizontal',\r\n  'mirrorVertical' = 'mirrorVertical',\r\n\r\n  'selectAll' = 'selectAll',\r\n\r\n  'moveToFront' = 'moveToFront',\r\n  'moveForward' = 'moveForward',\r\n  'moveBackward' = 'moveBackward',\r\n  'moveToBack' = 'moveToBack',\r\n\r\n  'arrangeLeft' = 'arrangeLeft',\r\n  'arrangeCenter' = 'arrangeCenter',\r\n  'arrangeRight' = 'arrangeRight',\r\n  'arrangeTop' = 'arrangeTop',\r\n  'arrangeMiddle' = 'arrangeMiddle',\r\n  'arrangeBottom' = 'arrangeBottom',\r\n\r\n  'unifyWidth' = 'unifyWidth',\r\n  'unifyHeight' = 'unifyHeight',\r\n\r\n  'distributeHorizontal' = 'distributeHorizontaly',\r\n  'distributeVertical' = 'distributeVertical',\r\n\r\n  'setTool' = 'setTool',\r\n\r\n  'setStrokeColor' = 'setStrokeColor',\r\n  'setFillBrush' = 'setFillBrush',\r\n  'setStrokeThickness' = 'setStrokeThickness',\r\n  \r\n  'screenshot' = 'screenshot',\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/commandHandling/IUiCommand.ts",
    "content": "import { CommandType } from './CommandType.js';\r\n\r\nexport interface IUiCommand {\r\n    type: CommandType;\r\n    event?: Event;\r\n    special?: string;\r\n    parameter?: any;\r\n\r\n    altKey?: boolean;\r\n    ctrlKey?: boolean;\r\n    metaKey?: boolean;\r\n    shiftKey?: boolean;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/commandHandling/IUiCommandHandler.ts",
    "content": "import { IUiCommand } from './IUiCommand.js';\r\n\r\nexport interface IUiCommandHandler {\r\n    executeCommand: (command: IUiCommand) => void;\r\n    canExecuteCommand: (command: IUiCommand) => boolean;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/ColorEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\nimport { w3color } from '../helper/w3color.js';\n\nexport type ColorEditorMode = 'rgb' | 'hsl' | 'cmyk' | 'oklab' | 'oklch';\nexport type ColorEditorValueChangedEventArgs = { newValue?: string, oldValue?: string };\n\ntype RgbaColor = { r: number, g: number, b: number, a: number };\ntype HsvColor = { h: number, s: number, v: number };\n\nconst modes: ColorEditorMode[] = ['rgb', 'hsl', 'cmyk', 'oklab', 'oklch'];\nconst epsilon = 0.000001;\n\nexport class ColorEditor extends BaseCustomWebComponentConstructorAppend {\n\n  public static override readonly style = css`\n    :host {\n      display: block;\n      box-sizing: border-box;\n      width: 280px;\n      color: var(--property-grid-text-color, #e8edf2);\n      font: 12px system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n    }\n\n    #editor {\n      display: grid;\n      gap: 10px;\n      box-sizing: border-box;\n      padding: 12px;\n      border: 1px solid rgba(255, 255, 255, .14);\n      border-radius: 8px;\n      background: var(--color-editor-background, #20252b);\n      box-shadow: 0 14px 38px rgba(0, 0, 0, .36);\n    }\n\n    #plane {\n      position: relative;\n      height: 150px;\n      border-radius: 6px;\n      overflow: hidden;\n      cursor: crosshair;\n      background:\n        linear-gradient(to top, #000, transparent),\n        linear-gradient(to right, #fff, transparent),\n        hsl(var(--hue, 0) 100% 50%);\n      box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);\n      touch-action: none;\n    }\n\n    #plane-handle {\n      position: absolute;\n      width: 12px;\n      height: 12px;\n      box-sizing: border-box;\n      border: 2px solid white;\n      border-radius: 50%;\n      transform: translate(-6px, -6px);\n      box-shadow: 0 0 0 1px rgba(0, 0, 0, .85), 0 1px 4px rgba(0, 0, 0, .55);\n      pointer-events: none;\n    }\n\n    .slider-row {\n      display: grid;\n      grid-template-columns: 18px minmax(0, 1fr) 42px;\n      gap: 8px;\n      align-items: center;\n    }\n\n    .slider-row span {\n      color: rgba(232, 237, 242, .68);\n      font-size: 11px;\n      text-transform: uppercase;\n    }\n\n    input[type=\"range\"] {\n      --slider-background: #79b8ff;\n      -webkit-appearance: none;\n      appearance: none;\n      width: 100%;\n      height: 16px;\n      margin: 0;\n      background: transparent;\n      outline: none;\n    }\n\n    #hue {\n      --slider-background: linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);\n    }\n\n    #alpha {\n      --slider-background:\n        linear-gradient(to right, rgba(var(--rgb-color, 0, 0, 0), 0), var(--opaque-color, #000)),\n        linear-gradient(45deg, rgba(255, 255, 255, .24) 25%, transparent 25% 75%, rgba(255, 255, 255, .24) 75%),\n        linear-gradient(45deg, rgba(255, 255, 255, .24) 25%, transparent 25% 75%, rgba(255, 255, 255, .24) 75%),\n        #2a3037;\n      --slider-background-position: 0 0, 0 0, 5px 5px, 0 0;\n      --slider-background-size: auto, 10px 10px, 10px 10px, auto;\n    }\n\n    input[type=\"range\"]::-webkit-slider-runnable-track {\n      height: 7px;\n      border-radius: 999px;\n      background: var(--slider-background);\n      background-position: var(--slider-background-position, 0 0);\n      background-size: var(--slider-background-size, auto);\n      box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22);\n    }\n\n    input[type=\"range\"]::-moz-range-track {\n      height: 7px;\n      border-radius: 999px;\n      background: var(--slider-background);\n      background-position: var(--slider-background-position, 0 0);\n      background-size: var(--slider-background-size, auto);\n      box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22);\n    }\n\n    input[type=\"range\"]::-webkit-slider-thumb {\n      -webkit-appearance: none;\n      appearance: none;\n      width: 14px;\n      height: 14px;\n      margin-top: -3.5px;\n      border: 2px solid white;\n      border-radius: 50%;\n      background: hsl(var(--hue, 0) 100% 50%);\n      box-shadow: 0 0 0 1px rgba(0, 0, 0, .75), 0 1px 3px rgba(0, 0, 0, .45);\n    }\n\n    input[type=\"range\"]::-moz-range-thumb {\n      width: 10px;\n      height: 10px;\n      border: 2px solid white;\n      border-radius: 50%;\n      background: hsl(var(--hue, 0) 100% 50%);\n      box-shadow: 0 0 0 1px rgba(0, 0, 0, .75), 0 1px 3px rgba(0, 0, 0, .45);\n    }\n\n    #alpha::-webkit-slider-thumb {\n      background: var(--color, #000);\n    }\n\n    #alpha::-moz-range-thumb {\n      background: var(--color, #000);\n    }\n\n    .swatch {\n      width: 42px;\n      height: 22px;\n      border-radius: 5px;\n      background:\n        linear-gradient(var(--color, #000), var(--color, #000)),\n        linear-gradient(45deg, rgba(255, 255, 255, .22) 25%, transparent 25% 75%, rgba(255, 255, 255, .22) 75%),\n        linear-gradient(45deg, rgba(255, 255, 255, .22) 25%, transparent 25% 75%, rgba(255, 255, 255, .22) 75%);\n      background-position: 0 0, 0 0, 5px 5px;\n      background-size: auto, 10px 10px, 10px 10px;\n      box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);\n    }\n\n    #mode {\n      height: 26px;\n      min-width: 0;\n      border: 1px solid rgba(255, 255, 255, .14);\n      border-radius: 5px;\n      background: #161a1f;\n      color: inherit;\n      font: inherit;\n      outline: none;\n    }\n\n    #channels {\n      display: grid;\n      grid-template-columns: repeat(4, minmax(0, 1fr));\n      gap: 6px;\n    }\n\n    label {\n      display: grid;\n      gap: 3px;\n      min-width: 0;\n      color: rgba(232, 237, 242, .68);\n      font-size: 10px;\n      text-transform: uppercase;\n    }\n\n    input[type=\"number\"],\n    #text {\n      box-sizing: border-box;\n      width: 100%;\n      min-width: 0;\n      height: 26px;\n      border: 1px solid rgba(255, 255, 255, .14);\n      border-radius: 5px;\n      background: #161a1f;\n      color: inherit;\n      font: inherit;\n      outline: none;\n    }\n\n    input[type=\"number\"] {\n      padding: 0 4px;\n    }\n\n    #text {\n      padding: 0 8px;\n      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n      text-transform: none;\n    }\n\n    #text.invalid {\n      border-color: #e66b6b;\n      box-shadow: 0 0 0 1px rgba(230, 107, 107, .35);\n    }\n\n    input:focus,\n    select:focus {\n      border-color: #79b8ff;\n      box-shadow: 0 0 0 1px rgba(121, 184, 255, .28);\n    }\n\n    :host([readonly]) input,\n    :host([readonly]) select,\n    :host([readonly]) #plane,\n    :host([disabled]) input,\n    :host([disabled]) select,\n    :host([disabled]) #plane {\n      opacity: .65;\n      pointer-events: none;\n    }\n  `;\n\n  public static override readonly template = html`\n    <div id=\"editor\">\n      <div id=\"plane\"><div id=\"plane-handle\"></div></div>\n      <div class=\"slider-row\">\n        <span>Hue</span>\n        <input id=\"hue\" type=\"range\" min=\"0\" max=\"360\" step=\"1\">\n        <div id=\"preview\" class=\"swatch\"></div>\n      </div>\n      <div class=\"slider-row\">\n        <span>Alpha</span>\n        <input id=\"alpha\" type=\"range\" min=\"0\" max=\"100\" step=\"1\">\n        <span id=\"alpha-label\"></span>\n      </div>\n      <select id=\"mode\" aria-label=\"Color mode\">\n        <option value=\"rgb\">RGB</option>\n        <option value=\"hsl\">HSL</option>\n        <option value=\"cmyk\">CMYK</option>\n        <option value=\"oklab\">OKLab</option>\n        <option value=\"oklch\">OKLCH</option>\n      </select>\n      <div id=\"channels\"></div>\n      <input id=\"text\" type=\"text\" spellcheck=\"false\" aria-label=\"Color text\">\n    </div>\n  `;\n\n  public valueChanged = new TypedEvent<ColorEditorValueChangedEventArgs>();\n  public valuePreviewChanged = new TypedEvent<ColorEditorValueChangedEventArgs>();\n\n  private _value = '#000000';\n  private _color: RgbaColor = { r: 0, g: 0, b: 0, a: 1 };\n  private _mode: ColorEditorMode = 'rgb';\n  private _hue = 0;\n  private _plane: HTMLDivElement;\n  private _planeHandle: HTMLDivElement;\n  private _hueInput: HTMLInputElement;\n  private _alphaInput: HTMLInputElement;\n  private _alphaLabel: HTMLSpanElement;\n  private _modeSelect: HTMLSelectElement;\n  private _channels: HTMLDivElement;\n  private _textInput: HTMLInputElement;\n  private _dragPointerId: number = null;\n\n  public get value() {\n    return this._value;\n  }\n  public set value(value: string) {\n    const parsed = parseColor(value);\n    if (parsed) {\n      this._color = parsed;\n      this._mode = inferColorMode(value) ?? this._mode;\n      this._syncHueFromColor();\n      this._value = this._formatColor();\n      this._render();\n      return;\n    }\n    this._value = value ?? '';\n    this._render();\n  }\n\n  public get mode() {\n    return this._mode;\n  }\n  public set mode(value: ColorEditorMode) {\n    if (modes.includes(value)) {\n      this._mode = value;\n      this._value = this._formatColor();\n      this._render();\n    }\n  }\n\n  public get readOnly() {\n    return this.hasAttribute('readonly');\n  }\n  public set readOnly(value: boolean) {\n    this.toggleAttribute('readonly', value);\n  }\n\n  public get disabled() {\n    return this.hasAttribute('disabled');\n  }\n  public set disabled(value: boolean) {\n    this.toggleAttribute('disabled', value);\n  }\n\n  constructor() {\n    super();\n    this._plane = this._getDomElement<HTMLDivElement>('plane');\n    this._planeHandle = this._getDomElement<HTMLDivElement>('plane-handle');\n    this._hueInput = this._getDomElement<HTMLInputElement>('hue');\n    this._alphaInput = this._getDomElement<HTMLInputElement>('alpha');\n    this._alphaLabel = this._getDomElement<HTMLSpanElement>('alpha-label');\n    this._modeSelect = this._getDomElement<HTMLSelectElement>('mode');\n    this._channels = this._getDomElement<HTMLDivElement>('channels');\n    this._textInput = this._getDomElement<HTMLInputElement>('text');\n  }\n\n  ready() {\n    this._wireEvents();\n    const attributeValue = this.getAttribute('value');\n    if (attributeValue != null)\n      this.value = attributeValue;\n    const attributeMode = this.getAttribute('mode') as ColorEditorMode;\n    if (modes.includes(attributeMode)) {\n      this._mode = attributeMode;\n      this._value = this._formatColor();\n    }\n    this._render();\n  }\n\n  private _wireEvents() {\n    this._plane.addEventListener('pointerdown', e => this._startPlaneDrag(e));\n    this._hueInput.addEventListener('input', () => this._applyHue(true));\n    this._hueInput.addEventListener('change', () => this._commitCurrentValue());\n    this._alphaInput.addEventListener('input', () => this._applyAlpha(true));\n    this._alphaInput.addEventListener('change', () => this._commitCurrentValue());\n    this._modeSelect.addEventListener('change', () => {\n      this.mode = this._modeSelect.value as ColorEditorMode;\n      this._commitCurrentValue();\n    });\n    this._channels.addEventListener('input', () => this._applyChannelInputs(true));\n    this._channels.addEventListener('change', () => this._commitCurrentValue());\n    this._textInput.addEventListener('input', () => this._validateTextInput());\n    this._textInput.addEventListener('change', () => this._applyText());\n    this._textInput.addEventListener('keydown', e => {\n      if (e.key === 'Enter') {\n        this._applyText();\n        this._textInput.blur();\n      }\n    });\n  }\n\n  private _startPlaneDrag(event: PointerEvent) {\n    if (this.readOnly || this.disabled)\n      return;\n    this._dragPointerId = event.pointerId;\n    this._plane.setPointerCapture(event.pointerId);\n    this._applyPlanePointer(event, true);\n    this._plane.addEventListener('pointermove', this._planePointerMove);\n    this._plane.addEventListener('pointerup', this._finishPlaneDrag);\n    this._plane.addEventListener('pointercancel', this._finishPlaneDrag);\n    event.preventDefault();\n  }\n\n  private _planePointerMove = (event: PointerEvent) => {\n    if (event.pointerId === this._dragPointerId)\n      this._applyPlanePointer(event, true);\n  };\n\n  private _finishPlaneDrag = (event: PointerEvent) => {\n    if (event.pointerId !== this._dragPointerId)\n      return;\n    this._dragPointerId = null;\n    this._plane.removeEventListener('pointermove', this._planePointerMove);\n    this._plane.removeEventListener('pointerup', this._finishPlaneDrag);\n    this._plane.removeEventListener('pointercancel', this._finishPlaneDrag);\n    this._commitCurrentValue();\n  };\n\n  private _applyPlanePointer(event: PointerEvent, preview: boolean) {\n    const rect = this._plane.getBoundingClientRect();\n    const s = clamp((event.clientX - rect.left) / rect.width, 0, 1);\n    const v = clamp(1 - ((event.clientY - rect.top) / rect.height), 0, 1);\n    const hue = Number(this._hueInput.value) || 0;\n    this._hue = normalizeHue(hue);\n    this._setColor({ ...hsvToRgb(hue, s, v), a: this._color.a }, preview);\n  }\n\n  private _applyHue(preview: boolean) {\n    const hsv = rgbToHsv(this._color);\n    const hue = Number(this._hueInput.value) || 0;\n    this._hue = normalizeHue(hue);\n    this._setColor({ ...hsvToRgb(hue, hsv.s || 1, hsv.v || 1), a: this._color.a }, preview);\n  }\n\n  private _applyAlpha(preview: boolean) {\n    this._setColor({ ...this._color, a: clamp(Number(this._alphaInput.value) / 100, 0, 1) }, preview);\n  }\n\n  private _applyChannelInputs(preview: boolean) {\n    const values = [...this._channels.querySelectorAll<HTMLInputElement>('input')].reduce((map, input) => {\n      map[input.name] = Number(input.value);\n      return map;\n    }, {} as Record<string, number>);\n\n    let color: RgbaColor;\n    if (this._mode === 'rgb')\n      color = { r: values.r, g: values.g, b: values.b, a: values.a / 100 };\n    else if (this._mode === 'hsl')\n      color = { ...hslToRgb(values.h, values.s / 100, values.l / 100), a: values.a / 100 };\n    else if (this._mode === 'cmyk')\n      color = { ...cmykToRgb(values.c / 100, values.m / 100, values.y / 100, values.k / 100), a: values.a / 100 };\n    else if (this._mode === 'oklab')\n      color = { ...oklabToRgb(values.l / 100, values.a1, values.b1), a: values.alpha / 100 };\n    else\n      color = { ...oklchToRgb(values.l / 100, values.c, values.h), a: values.a / 100 };\n\n    this._setColor(normalizeColor(color), preview, false, true);\n  }\n\n  private _validateTextInput() {\n    const text = this._textInput.value.trim();\n    this._textInput.classList.toggle('invalid', text.length > 0 && !parseColor(text));\n  }\n\n  private _applyText() {\n    const parsed = parseColor(this._textInput.value);\n    this._textInput.classList.toggle('invalid', !parsed);\n    if (!parsed)\n      return;\n    this._mode = inferColorMode(this._textInput.value) ?? this._mode;\n    this._setColor(parsed, false, true, true);\n  }\n\n  private _setColor(color: RgbaColor, preview: boolean, renderChannels = true, syncHue = false) {\n    const oldValue = this._value;\n    this._color = normalizeColor(color);\n    if (syncHue)\n      this._syncHueFromColor();\n    this._value = this._formatColor();\n    this._render(renderChannels);\n    if (preview)\n      this.valuePreviewChanged.emit({ newValue: this._value, oldValue });\n    else\n      this.valueChanged.emit({ newValue: this._value, oldValue });\n  }\n\n  private _commitCurrentValue() {\n    const oldValue = this._value;\n    this._value = this._formatColor();\n    this.valueChanged.emit({ newValue: this._value, oldValue });\n  }\n\n  private _render(renderChannels = true) {\n    if (!this._textInput)\n      return;\n\n    const hsv = rgbToHsv(this._color);\n    this.style.setProperty('--hue', String(Math.round(this._hue)));\n    this.style.setProperty('--color', toCssRgb(this._color));\n    this.style.setProperty('--opaque-color', `rgb(${this._color.r}, ${this._color.g}, ${this._color.b})`);\n    this.style.setProperty('--rgb-color', `${this._color.r}, ${this._color.g}, ${this._color.b}`);\n    this._planeHandle.style.left = `${hsv.s * 100}%`;\n    this._planeHandle.style.top = `${(1 - hsv.v) * 100}%`;\n    this._hueInput.value = String(Math.round(this._hue));\n    this._alphaInput.value = String(Math.round(this._color.a * 100));\n    this._alphaLabel.textContent = `${Math.round(this._color.a * 100)}%`;\n    this._modeSelect.value = this._mode;\n    if (renderChannels)\n      this._renderChannelInputs();\n    if (document.activeElement !== this._textInput)\n      this._textInput.value = this._value;\n    this._textInput.classList.remove('invalid');\n  }\n\n  private _renderChannelInputs() {\n    const channels = getChannelValues(this._color, this._mode);\n    this._channels.innerHTML = channels.map(channel => `\n      <label>${channel.label}\n        <input type=\"number\" name=\"${channel.name}\" min=\"${channel.min}\" max=\"${channel.max}\" step=\"${channel.step}\" value=\"${channel.value}\">\n      </label>`).join('');\n  }\n\n  private _formatColor() {\n    return formatColor(this._color, this._mode);\n  }\n\n  private _syncHueFromColor() {\n    const hsv = rgbToHsv(this._color);\n    if (hsv.s > epsilon && hsv.v > epsilon)\n      this._hue = normalizeHue(hsv.h);\n  }\n}\n\nexport class ColorInput extends BaseCustomWebComponentConstructorAppend {\n\n  public static override readonly style = css`\n    :host {\n      display: inline-block;\n      box-sizing: border-box;\n      width: 100%;\n      height: 100%;\n      padding: 2px;\n    }\n\n    button {\n      display: block;\n      box-sizing: border-box;\n      width: 100%;\n      height: 100%;\n      padding: 3px;\n      border: 1px solid var(--input-border-color, #596c7a);\n      border-radius: 4px;\n      background: var(--input-background-color, #1d2228);\n      cursor: pointer;\n      outline: none;\n    }\n\n    button:focus {\n      border-color: #79b8ff;\n      box-shadow: 0 0 0 1px rgba(121, 184, 255, .28);\n    }\n\n    #swatch {\n      display: block;\n      width: 100%;\n      height: 100%;\n      border-radius: 2px;\n      background:\n        linear-gradient(var(--color, #000), var(--color, #000)),\n        linear-gradient(45deg, rgba(255, 255, 255, .25) 25%, transparent 25% 75%, rgba(255, 255, 255, .25) 75%),\n        linear-gradient(45deg, rgba(255, 255, 255, .25) 25%, transparent 25% 75%, rgba(255, 255, 255, .25) 75%);\n      background-position: 0 0, 0 0, 5px 5px;\n      background-size: auto, 10px 10px, 10px 10px;\n      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .28);\n    }\n\n    :host([readonly]) button,\n    :host([disabled]) button {\n      cursor: default;\n      opacity: .65;\n    }\n  `;\n\n  public static override readonly template = html`\n    <button id=\"button\" type=\"button\" aria-label=\"Edit color\"><span id=\"swatch\"></span></button>\n  `;\n\n  private _value = '#000000';\n  private _button: HTMLButtonElement;\n  private _popup: HTMLDivElement;\n  private _editor: ColorEditor;\n  private _ignoreNextClick = false;\n  private _outsidePointerHandler = (event: PointerEvent) => this._handleOutsidePointer(event);\n  private _windowKeyHandler = (event: KeyboardEvent) => this._handleWindowKey(event);\n\n  public get value() {\n    return this._value;\n  }\n  public set value(value: string) {\n    this._setValue(value, false);\n  }\n\n  public get readOnly() {\n    return this.hasAttribute('readonly');\n  }\n  public set readOnly(value: boolean) {\n    this.toggleAttribute('readonly', value);\n  }\n\n  public get disabled() {\n    return this.hasAttribute('disabled');\n  }\n  public set disabled(value: boolean) {\n    this.toggleAttribute('disabled', value);\n    if (this._button)\n      this._button.disabled = value;\n  }\n\n  constructor() {\n    super();\n    this._button = this._getDomElement<HTMLButtonElement>('button');\n  }\n\n  ready() {\n    const attributeValue = this.getAttribute('value');\n    if (attributeValue != null)\n      this._setValue(attributeValue, false);\n    this._button.disabled = this.disabled;\n    this._button.addEventListener('pointerdown', e => this._handleButtonPointerDown(e));\n    this._button.addEventListener('click', e => this._handleButtonClick(e));\n    this._button.addEventListener('keydown', e => this._handleButtonKeyDown(e));\n    this._renderSwatch();\n  }\n\n  disconnectedCallback() {\n    this._closePopup();\n  }\n\n  private _togglePopup() {\n    if (this.readOnly || this.disabled)\n      return;\n    if (this._popup)\n      this._closePopup();\n    else\n      this._openPopup();\n  }\n\n  private _handleButtonPointerDown(event: PointerEvent) {\n    if (event.button !== 0)\n      return;\n    this._ignoreNextClick = true;\n    this._togglePopup();\n    event.preventDefault();\n    event.stopPropagation();\n  }\n\n  private _handleButtonClick(event: MouseEvent) {\n    if (this._ignoreNextClick) {\n      this._ignoreNextClick = false;\n      event.preventDefault();\n      event.stopPropagation();\n      return;\n    }\n    this._togglePopup();\n  }\n\n  private _handleButtonKeyDown(event: KeyboardEvent) {\n    if (event.key !== 'Enter' && event.key !== ' ')\n      return;\n    this._togglePopup();\n    event.preventDefault();\n    event.stopPropagation();\n  }\n\n  private _openPopup() {\n    this._popup = document.createElement('div');\n    this._popup.style.position = 'fixed';\n    this._popup.style.zIndex = '100000';\n    this._popup.style.width = '280px';\n\n    this._editor = document.createElement('node-projects-color-editor') as ColorEditor;\n    this._editor.value = this._value;\n    this._editor.valuePreviewChanged.on(e => {\n      this._setValue(e.newValue, true);\n      this.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n    });\n    this._editor.valueChanged.on(e => {\n      this._setValue(e.newValue, true);\n      this.dispatchEvent(new Event('change', { bubbles: true, composed: true }));\n    });\n    this._popup.appendChild(this._editor);\n    document.body.appendChild(this._popup);\n    this._positionPopup();\n    window.addEventListener('resize', () => this._positionPopup(), { once: true });\n    window.addEventListener('scroll', () => this._positionPopup(), { once: true, capture: true });\n    window.addEventListener('keydown', this._windowKeyHandler);\n    requestAnimationFrame(() => window.addEventListener('pointerdown', this._outsidePointerHandler, true));\n  }\n\n  private _positionPopup() {\n    if (!this._popup)\n      return;\n    const rect = this.getBoundingClientRect();\n    const width = this._popup.offsetWidth || 280;\n    const height = this._popup.offsetHeight || 360;\n    let left = rect.left;\n    let top = rect.bottom + 4;\n    if (left + width > window.innerWidth - 8)\n      left = window.innerWidth - width - 8;\n    if (top + height > window.innerHeight - 8)\n      top = rect.top - height - 4;\n    this._popup.style.left = `${Math.max(8, left)}px`;\n    this._popup.style.top = `${Math.max(8, top)}px`;\n  }\n\n  private _handleOutsidePointer(event: PointerEvent) {\n    const path = event.composedPath();\n    if (path.includes(this) || (this._popup && path.includes(this._popup)))\n      return;\n    this._closePopup();\n  }\n\n  private _handleWindowKey(event: KeyboardEvent) {\n    if (event.key === 'Escape')\n      this._closePopup();\n  }\n\n  private _closePopup() {\n    window.removeEventListener('pointerdown', this._outsidePointerHandler, true);\n    window.removeEventListener('keydown', this._windowKeyHandler);\n    this._popup?.remove();\n    this._popup = null;\n    this._editor = null;\n  }\n\n  private _setValue(value: string, updateAttribute: boolean) {\n    this._value = value ?? '';\n    if (updateAttribute)\n      this.setAttribute('value', this._value);\n    this._renderSwatch();\n  }\n\n  private _renderSwatch() {\n    const parsed = parseColor(this._value) ?? { r: 0, g: 0, b: 0, a: 1 };\n    this.style.setProperty('--color', toCssRgb(parsed));\n  }\n}\n\nfunction parseColor(value: string): RgbaColor {\n  if (!value)\n    return null;\n  const text = value.trim();\n  if (text.toLowerCase() === 'transparent')\n    return { r: 0, g: 0, b: 0, a: 0 };\n  const hex = parseHexColor(text);\n  if (hex)\n    return hex;\n  const oklab = parseOklabColor(text);\n  if (oklab)\n    return oklab;\n\n  const normalized = normalizeModernColorSyntax(text);\n  const color = w3color.toColorObject(normalized);\n  if (color?.valid)\n    return normalizeColor({ r: color.red, g: color.green, b: color.blue, a: color.opacity });\n  return null;\n}\n\nfunction inferColorMode(value: string): ColorEditorMode {\n  const match = /^\\s*(rgba?|hsla?|cmyk|oklab|oklch)\\s*\\(/i.exec(value ?? '');\n  const colorFunction = match?.[1]?.toLowerCase();\n  if (colorFunction === 'rgb' || colorFunction === 'rgba')\n    return 'rgb';\n  if (colorFunction === 'hsl' || colorFunction === 'hsla')\n    return 'hsl';\n  if (colorFunction === 'cmyk')\n    return 'cmyk';\n  if (colorFunction === 'oklab')\n    return 'oklab';\n  if (colorFunction === 'oklch')\n    return 'oklch';\n  return null;\n}\n\nfunction normalizeModernColorSyntax(value: string) {\n  return value\n    .replace(/,\\s*/g, ',')\n    .replace(/rgba?\\(([^)]*)\\)/i, (_, body) => normalizeFunctionBody('rgb', body))\n    .replace(/hsla?\\(([^)]*)\\)/i, (_, body) => normalizeFunctionBody('hsl', body));\n}\n\nfunction normalizeFunctionBody(name: string, body: string) {\n  if (body.includes(',')) {\n    const parts = body.split(',').map(x => x.trim());\n    if (parts.length === 4 && parts[3].endsWith('%'))\n      parts[3] = String(Number(parts[3].slice(0, -1)) / 100);\n    return `${name}${parts.length === 4 ? 'a' : ''}(${parts.join(',')})`;\n  }\n  const [channels, alpha] = body.split('/').map(x => x.trim());\n  const parts = channels.split(/\\s+/).filter(Boolean);\n  if (alpha)\n    parts.push(alpha.endsWith('%') ? String(Number(alpha.slice(0, -1)) / 100) : alpha);\n  return `${name}${parts.length === 4 ? 'a' : ''}(${parts.join(',')})`;\n}\n\nfunction parseHexColor(value: string): RgbaColor {\n  const match = /^#([0-9a-f]{3,8})$/i.exec(value);\n  if (!match)\n    return null;\n  const hex = match[1];\n  const read = (part: string) => parseInt(part.length === 1 ? part + part : part, 16);\n  if (hex.length === 3 || hex.length === 4)\n    return normalizeColor({ r: read(hex[0]), g: read(hex[1]), b: read(hex[2]), a: hex.length === 4 ? read(hex[3]) / 255 : 1 });\n  if (hex.length === 6 || hex.length === 8)\n    return normalizeColor({ r: read(hex.slice(0, 2)), g: read(hex.slice(2, 4)), b: read(hex.slice(4, 6)), a: hex.length === 8 ? read(hex.slice(6, 8)) / 255 : 1 });\n  return null;\n}\n\nfunction parseOklabColor(value: string): RgbaColor {\n  const match = /^(oklab|oklch)\\((.*)\\)$/i.exec(value);\n  if (!match)\n    return null;\n  const mode = match[1].toLowerCase();\n  const [channels, alphaText] = match[2].split('/').map(x => x.trim());\n  const parts = channels.replace(/,/g, ' ').split(/\\s+/).filter(Boolean);\n  if (parts.length !== 3)\n    return null;\n  const alpha = alphaText ? parseNumberOrPercent(alphaText, 1) : 1;\n  if (mode === 'oklab')\n    return normalizeColor({ ...oklabToRgb(parseNumberOrPercent(parts[0], 1), Number(parts[1]), Number(parts[2])), a: alpha });\n  return normalizeColor({ ...oklchToRgb(parseNumberOrPercent(parts[0], 1), Number(parts[1]), Number(parts[2])), a: alpha });\n}\n\nfunction formatColor(color: RgbaColor, mode: ColorEditorMode) {\n  const alpha = round(color.a, 3);\n  if (mode === 'rgb')\n    return color.a >= 1 ? `rgb(${color.r}, ${color.g}, ${color.b})` : `rgba(${color.r}, ${color.g}, ${color.b}, ${alpha})`;\n  if (mode === 'hsl') {\n    const hsl = rgbToHsl(color);\n    return color.a >= 1\n      ? `hsl(${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%)`\n      : `hsla(${Math.round(hsl.h)}, ${Math.round(hsl.s * 100)}%, ${Math.round(hsl.l * 100)}%, ${alpha})`;\n  }\n  if (mode === 'cmyk') {\n    const cmyk = rgbToCmyk(color);\n    const body = `${Math.round(cmyk.c * 100)}%, ${Math.round(cmyk.m * 100)}%, ${Math.round(cmyk.y * 100)}%, ${Math.round(cmyk.k * 100)}%`;\n    return color.a >= 1 ? `cmyk(${body})` : `cmyk(${body}, ${alpha})`;\n  }\n  if (mode === 'oklab') {\n    const lab = rgbToOklab(color);\n    return `oklab(${round(lab.l * 100, 2)}% ${round(lab.a, 4)} ${round(lab.b, 4)}${color.a >= 1 ? '' : ` / ${alpha}`})`;\n  }\n  const lch = rgbToOklch(color);\n  return `oklch(${round(lch.l * 100, 2)}% ${round(lch.c, 4)} ${round(lch.h, 2)}${color.a >= 1 ? '' : ` / ${alpha}`})`;\n}\n\nfunction getChannelValues(color: RgbaColor, mode: ColorEditorMode) {\n  if (mode === 'rgb')\n    return [\n      { label: 'R', name: 'r', value: color.r, min: 0, max: 255, step: 1 },\n      { label: 'G', name: 'g', value: color.g, min: 0, max: 255, step: 1 },\n      { label: 'B', name: 'b', value: color.b, min: 0, max: 255, step: 1 },\n      { label: 'A', name: 'a', value: Math.round(color.a * 100), min: 0, max: 100, step: 1 }\n    ];\n  if (mode === 'hsl') {\n    const hsl = rgbToHsl(color);\n    return [\n      { label: 'H', name: 'h', value: Math.round(hsl.h), min: 0, max: 360, step: 1 },\n      { label: 'S', name: 's', value: Math.round(hsl.s * 100), min: 0, max: 100, step: 1 },\n      { label: 'L', name: 'l', value: Math.round(hsl.l * 100), min: 0, max: 100, step: 1 },\n      { label: 'A', name: 'a', value: Math.round(color.a * 100), min: 0, max: 100, step: 1 }\n    ];\n  }\n  if (mode === 'cmyk') {\n    const cmyk = rgbToCmyk(color);\n    return [\n      { label: 'C', name: 'c', value: Math.round(cmyk.c * 100), min: 0, max: 100, step: 1 },\n      { label: 'M', name: 'm', value: Math.round(cmyk.m * 100), min: 0, max: 100, step: 1 },\n      { label: 'Y', name: 'y', value: Math.round(cmyk.y * 100), min: 0, max: 100, step: 1 },\n      { label: 'K', name: 'k', value: Math.round(cmyk.k * 100), min: 0, max: 100, step: 1 },\n      { label: 'A', name: 'a', value: Math.round(color.a * 100), min: 0, max: 100, step: 1 }\n    ];\n  }\n  if (mode === 'oklab') {\n    const lab = rgbToOklab(color);\n    return [\n      { label: 'L', name: 'l', value: round(lab.l * 100, 2), min: 0, max: 100, step: .1 },\n      { label: 'A', name: 'a1', value: round(lab.a, 4), min: -1, max: 1, step: .001 },\n      { label: 'B', name: 'b1', value: round(lab.b, 4), min: -1, max: 1, step: .001 },\n      { label: 'Alpha', name: 'alpha', value: Math.round(color.a * 100), min: 0, max: 100, step: 1 }\n    ];\n  }\n  const lch = rgbToOklch(color);\n  return [\n    { label: 'L', name: 'l', value: round(lch.l * 100, 2), min: 0, max: 100, step: .1 },\n    { label: 'C', name: 'c', value: round(lch.c, 4), min: 0, max: 1, step: .001 },\n    { label: 'H', name: 'h', value: round(lch.h, 2), min: 0, max: 360, step: .1 },\n    { label: 'A', name: 'a', value: Math.round(color.a * 100), min: 0, max: 100, step: 1 }\n  ];\n}\n\nfunction normalizeColor(color: RgbaColor): RgbaColor {\n  return {\n    r: Math.round(clamp(color.r, 0, 255)),\n    g: Math.round(clamp(color.g, 0, 255)),\n    b: Math.round(clamp(color.b, 0, 255)),\n    a: clamp(Number.isFinite(color.a) ? color.a : 1, 0, 1)\n  };\n}\n\nfunction toCssRgb(color: RgbaColor) {\n  return `rgba(${color.r}, ${color.g}, ${color.b}, ${round(color.a, 3)})`;\n}\n\nfunction rgbToHsv(color: RgbaColor): HsvColor {\n  const r = color.r / 255;\n  const g = color.g / 255;\n  const b = color.b / 255;\n  const max = Math.max(r, g, b);\n  const min = Math.min(r, g, b);\n  const d = max - min;\n  let h = 0;\n  if (d !== 0) {\n    if (max === r)\n      h = ((g - b) / d) % 6;\n    else if (max === g)\n      h = (b - r) / d + 2;\n    else\n      h = (r - g) / d + 4;\n    h *= 60;\n  }\n  return { h: (h + 360) % 360, s: max === 0 ? 0 : d / max, v: max };\n}\n\nfunction hsvToRgb(h: number, s: number, v: number) {\n  const c = v * s;\n  const x = c * (1 - Math.abs((h / 60) % 2 - 1));\n  const m = v - c;\n  let r = 0;\n  let g = 0;\n  let b = 0;\n  if (h < 60)\n    [r, g, b] = [c, x, 0];\n  else if (h < 120)\n    [r, g, b] = [x, c, 0];\n  else if (h < 180)\n    [r, g, b] = [0, c, x];\n  else if (h < 240)\n    [r, g, b] = [0, x, c];\n  else if (h < 300)\n    [r, g, b] = [x, 0, c];\n  else\n    [r, g, b] = [c, 0, x];\n  return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };\n}\n\nfunction rgbToHsl(color: RgbaColor) {\n  const r = color.r / 255;\n  const g = color.g / 255;\n  const b = color.b / 255;\n  const max = Math.max(r, g, b);\n  const min = Math.min(r, g, b);\n  const l = (max + min) / 2;\n  const d = max - min;\n  let h = 0;\n  let s = 0;\n  if (d !== 0) {\n    s = d / (1 - Math.abs(2 * l - 1));\n    if (max === r)\n      h = ((g - b) / d) % 6;\n    else if (max === g)\n      h = (b - r) / d + 2;\n    else\n      h = (r - g) / d + 4;\n    h *= 60;\n  }\n  return { h: (h + 360) % 360, s, l };\n}\n\nfunction hslToRgb(h: number, s: number, l: number) {\n  const c = (1 - Math.abs(2 * l - 1)) * s;\n  const x = c * (1 - Math.abs((h / 60) % 2 - 1));\n  const m = l - c / 2;\n  let r = 0;\n  let g = 0;\n  let b = 0;\n  if (h < 60)\n    [r, g, b] = [c, x, 0];\n  else if (h < 120)\n    [r, g, b] = [x, c, 0];\n  else if (h < 180)\n    [r, g, b] = [0, c, x];\n  else if (h < 240)\n    [r, g, b] = [0, x, c];\n  else if (h < 300)\n    [r, g, b] = [x, 0, c];\n  else\n    [r, g, b] = [c, 0, x];\n  return { r: (r + m) * 255, g: (g + m) * 255, b: (b + m) * 255 };\n}\n\nfunction rgbToCmyk(color: RgbaColor) {\n  const r = color.r / 255;\n  const g = color.g / 255;\n  const b = color.b / 255;\n  const k = 1 - Math.max(r, g, b);\n  if (k >= 1 - epsilon)\n    return { c: 0, m: 0, y: 0, k: 1 };\n  return {\n    c: (1 - r - k) / (1 - k),\n    m: (1 - g - k) / (1 - k),\n    y: (1 - b - k) / (1 - k),\n    k\n  };\n}\n\nfunction cmykToRgb(c: number, m: number, y: number, k: number) {\n  return {\n    r: 255 * (1 - c) * (1 - k),\n    g: 255 * (1 - m) * (1 - k),\n    b: 255 * (1 - y) * (1 - k)\n  };\n}\n\nfunction rgbToOklab(color: RgbaColor) {\n  const r = srgbToLinear(color.r / 255);\n  const g = srgbToLinear(color.g / 255);\n  const b = srgbToLinear(color.b / 255);\n  const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);\n  const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);\n  const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);\n  return {\n    l: 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,\n    a: 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,\n    b: 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s\n  };\n}\n\nfunction oklabToRgb(lValue: number, aValue: number, bValue: number) {\n  const l = Math.pow(lValue + 0.3963377774 * aValue + 0.2158037573 * bValue, 3);\n  const m = Math.pow(lValue - 0.1055613458 * aValue - 0.0638541728 * bValue, 3);\n  const s = Math.pow(lValue - 0.0894841775 * aValue - 1.2914855480 * bValue, 3);\n  return {\n    r: 255 * linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n    g: 255 * linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n    b: 255 * linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s)\n  };\n}\n\nfunction rgbToOklch(color: RgbaColor) {\n  const lab = rgbToOklab(color);\n  const c = Math.sqrt(lab.a * lab.a + lab.b * lab.b);\n  const h = c < epsilon ? 0 : (Math.atan2(lab.b, lab.a) * 180 / Math.PI + 360) % 360;\n  return { l: lab.l, c, h };\n}\n\nfunction oklchToRgb(l: number, c: number, h: number) {\n  const radians = h * Math.PI / 180;\n  return oklabToRgb(l, c * Math.cos(radians), c * Math.sin(radians));\n}\n\nfunction srgbToLinear(value: number) {\n  return value <= 0.04045 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(value: number) {\n  return value <= 0.0031308 ? 12.92 * value : 1.055 * Math.pow(value, 1 / 2.4) - 0.055;\n}\n\nfunction parseNumberOrPercent(value: string, percentBase: number) {\n  return value.endsWith('%') ? Number(value.slice(0, -1)) / 100 * percentBase : Number(value);\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, Number.isFinite(value) ? value : min));\n}\n\nfunction normalizeHue(value: number) {\n  return clamp(value, 0, 360);\n}\n\nfunction round(value: number, decimals: number) {\n  const factor = Math.pow(10, decimals);\n  return Math.round(value * factor) / factor;\n}\n\ncustomElements.define('node-projects-color-editor', ColorEditor);\ncustomElements.define('node-projects-color-input', ColorInput);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/DesignerTabControl.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, TypedEvent, DomHelper } from '@node-projects/base-custom-webcomponent';\r\nimport { IActivateable } from '../../interfaces/IActivateable.js';\r\n\r\nexport type DesignerTabControlIndexChangedEventArgs = { newIndex: number, oldIndex?: number, changedViaClick?: boolean };\r\n\r\nexport class DesignerTabControl extends BaseCustomWebComponentLazyAppend {\r\n\r\n  private _selectedIndex: number = -1;\r\n\r\n  //private _contentObserver: MutationObserver;\r\n  private _panels: HTMLDivElement;\r\n  private _headerDiv: HTMLDivElement;\r\n  private _moreDiv: HTMLDivElement;\r\n  private _moreContainer: HTMLDivElement;\r\n  private _elementMap = new WeakMap<HTMLElement, HTMLDivElement>();\r\n  private _firstConnect = true;\r\n\r\n  static override readonly style = css`\r\n        :host {\r\n            height: 100%;\r\n        }\r\n        .outer {\r\n            display: flex; \r\n            flex-direction: column; \r\n            height: 100%;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n        .header {\r\n            display: inline-flex; \r\n            user-select: none;\r\n            -webkit-user-select: none; \r\n            flex-direction: row; \r\n            cursor: pointer; \r\n            height: 30px;\r\n            width: calc(100% - 30px);\r\n            background-color: var(--dark-grey, #232733);\r\n            overflow-x: auto;\r\n            scrollbar-width: none;  /* Firefox */\r\n        }\r\n        .header-more {\r\n            right: 0;\r\n            top: 0;\r\n            width: 30px;\r\n            position: absolute;\r\n            color: white;\r\n            display: flex;\r\n            justify-content: center;\r\n            align-items: center;\r\n            font-family: math;\r\n        }\r\n        .header-more:hover {\r\n            background: var(--light-grey, #383f52);\r\n        }\r\n        .more-container {\r\n            z-index: 1;\r\n            user-select: none;\r\n            -webkit-user-select: none;\r\n            background-color: var(--dark-grey, #232733);\r\n            right: 0;\r\n            top: 30px;\r\n            position: absolute;\r\n            color: white;\r\n            display: flex;\r\n            flex-direction: column;\r\n            align-items: flex-start;\r\n            cursor: pointer;\r\n        }\r\n        .more-container .tab-header { \r\n            width: 100%;\r\n        }\r\n        .header::-webkit-scrollbar { \r\n            display: none;  /* Safari and Chrome */\r\n        }\r\n        .tab-header {\r\n            height: 30px;\r\n            font-family: Arial;\r\n            display: flex;\r\n            justify-content: center;\r\n            align-items: center;                \r\n            text-transform: uppercase;                \r\n            box-sizing: content-box;                \r\n            padding-left: 5px;\r\n            padding-right: 5px;\r\n            color: white;\r\n            font-size: 12px;\r\n            font-weight: 500;\r\n            line-height: 1.5;\r\n            letter-spacing: 1px;\r\n            white-space: nowrap;\r\n        }\r\n        .tab-header:hover {\r\n            background: var(--light-grey, #383f52);\r\n        }\r\n        .selected {\r\n            background: var(--medium-grey, #2f3545);\r\n            box-shadow: inset 0 3px 0 var(--highlight-pink, #e91e63);\r\n        }\r\n        .panels {\r\n            z-index: 0;\r\n            background: var(--medium-grey, #2f3545);\r\n            height: calc(100% - 30px);\r\n        }\r\n        `;\r\n\r\n  constructor() {\r\n    super();\r\n\r\n\r\n    /*this._contentObserver = new MutationObserver((mut) => {\r\n      let refresh = false;\r\n      for (let m of mut) {\r\n        if (m.type != 'attributes' || m.attributeName == 'style')\r\n          refresh = true;\r\n      }\r\n      if (refresh)\r\n        this.refreshItems();\r\n    });*/\r\n\r\n    let outerDiv = document.createElement(\"div\")\r\n    outerDiv.className = 'outer';\r\n    this.shadowRoot.appendChild(outerDiv);\r\n    this._headerDiv = document.createElement(\"div\")\r\n    this._headerDiv.className = 'header';\r\n    outerDiv.appendChild(this._headerDiv);\r\n\r\n    this._moreDiv = document.createElement(\"div\");\r\n    this._moreDiv.className = \"header header-more\"\r\n    this._moreDiv.innerText = \"<<\"\r\n    outerDiv.appendChild(this._moreDiv);\r\n    this._moreContainer = document.createElement(\"div\");\r\n    this._moreContainer.className = \"more-container\";\r\n    this._moreContainer.style.visibility = \"hidden\";\r\n    outerDiv.appendChild(this._moreContainer);\r\n    this._moreDiv.onclick = () => {\r\n      if (this._moreContainer.children.length && this._moreContainer.style.visibility == \"hidden\")\r\n        this._moreContainer.style.visibility = '';\r\n      else\r\n        this._moreContainer.style.visibility = \"hidden\";\r\n    }\r\n\r\n    this._panels = document.createElement(\"div\")\r\n    this._panels.className = 'panels';\r\n    outerDiv.appendChild(this._panels);\r\n    let _slot = document.createElement(\"slot\")\r\n    _slot.name = 'panels';\r\n    this._panels.appendChild(_slot);\r\n\r\n    const resizeObserver = new ResizeObserver(entries => {\r\n      this._showHideHeaderItems();\r\n    });\r\n    resizeObserver.observe(this._headerDiv);\r\n  }\r\n\r\n  private _showHideHeaderItems() {\r\n    this._moreContainer.style.visibility = \"hidden\";\r\n    let w = 0;\r\n    DomHelper.removeAllChildnodes(this._moreContainer);\r\n    DomHelper.removeAllChildnodes(this._headerDiv);\r\n    let reloadOnce = true;\r\n    for (let item of this.children) {\r\n      if ((<HTMLElement>item).style.display != 'none') {\r\n        let htmlItem = item as HTMLElement;\r\n        if (!this._elementMap.has(htmlItem) && reloadOnce) {\r\n          this.refreshItems();\r\n          reloadOnce = false;\r\n        }\r\n        const tabHeaderDiv = this._elementMap.get(htmlItem);\r\n        this._moreContainer.appendChild(tabHeaderDiv);\r\n        if (this._headerDiv.children.length == 0 || (w + (tabHeaderDiv.clientWidth / 2)) < this._headerDiv.clientWidth) {\r\n          this._headerDiv.appendChild(tabHeaderDiv);\r\n          w += tabHeaderDiv.clientWidth;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  connectedCallback() {\r\n    if (this._firstConnect) {\r\n      this.refreshItems();\r\n      this._firstConnect = false;\r\n\r\n      //this._contentObserver.observe(this, { childList: true, subtree: true, attributes: true });\r\n\r\n      let selectedIndexAttribute = this.getAttribute(\"selected-index\")\r\n      if (selectedIndexAttribute) {\r\n        this.selectedIndex = parseInt(selectedIndexAttribute);\r\n      }\r\n    }\r\n  }\r\n\r\n  public get selectedIndex() {\r\n    return this._firstConnect ? -1 : this._selectedIndex;\r\n  }\r\n  public set selectedIndex(value: number) {\r\n    let old = this._selectedIndex;\r\n    this._selectedIndex = value;\r\n    if (this.children.length && old != this._selectedIndex)\r\n      this._selectedIndexChanged(old);\r\n  }\r\n\r\n  public refreshItems() {\r\n    this._headerDiv.innerHTML = \"\";\r\n    let i = 0;\r\n    for (let item of this.children) {\r\n      if ((<HTMLElement>item).style.display != 'none') {\r\n        let htmlItem = item as HTMLElement;\r\n        let tabHeaderDiv = document.createElement(\"div\")\r\n        tabHeaderDiv.innerText = htmlItem.dataset.title || htmlItem.title;\r\n        tabHeaderDiv.title = htmlItem.dataset.title || htmlItem.title;\r\n        tabHeaderDiv.className = 'tab-header';\r\n        let j = i;\r\n        tabHeaderDiv.onpointerdown = () => {\r\n          let old = this._selectedIndex;\r\n          this._selectedIndex = j;\r\n          if (this._headerDiv.children.length)\r\n            this._selectedIndexChanged(old, true);\r\n          this._moreContainer.style.visibility = 'hidden';\r\n        }\r\n        this._elementMap.set(htmlItem, tabHeaderDiv);\r\n        this._headerDiv.appendChild(tabHeaderDiv);\r\n        i++;\r\n      }\r\n    }\r\n\r\n    this._showHideHeaderItems();\r\n    this._selectedIndexChanged();\r\n  }\r\n\r\n  private _selectedIndexChanged(oldIndex?: number, viaClick = false) {\r\n    let index = -1;\r\n    for (let element of this.children) {\r\n      if ((<HTMLElement>element).style.display != 'none') {\r\n        index++;\r\n        if (index == this._selectedIndex) {\r\n          if (element.slot != \"panels\")\r\n            element.slot = \"panels\";\r\n          const headerEl = this._elementMap.get(<HTMLElement>element);\r\n          if (headerEl) {\r\n            headerEl.classList.add('selected');\r\n            if ((<IActivateable><unknown>element).activated)\r\n              (<IActivateable><unknown>element).activated();\r\n          }\r\n        } else {\r\n          element.removeAttribute(\"slot\");\r\n          const headerEl = this._elementMap.get(<HTMLElement>element);\r\n          if (headerEl) {\r\n            headerEl.classList.remove('selected');\r\n          }\r\n        }\r\n      }\r\n    }\r\n    this.onSelectedTabChanged.emit({ newIndex: this._selectedIndex, oldIndex: oldIndex, changedViaClick: viaClick });\r\n    this._moreContainer.style.visibility = 'hidden';\r\n  }\r\n\r\n  public readonly onSelectedTabChanged = new TypedEvent<DesignerTabControlIndexChangedEventArgs>();\r\n}\r\n\r\ncustomElements.define('node-projects-designer-tab-control', DesignerTabControl);\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/ImageButtonListSelector.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class ImageButtonListSelector extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  public static override readonly style = css`\r\n    div {\r\n      font-size: 10px;\r\n      color: white;\r\n    }\r\n    #property {\r\n      color: #00aff0;\r\n    }\r\n    #value {\r\n      color: lightgray;\r\n    }\r\n    #value.value-set {\r\n      color: wheat;\r\n    }\r\n    .container {\r\n      display: flex;\r\n      flex-direction: row;\r\n    }\r\n    ::slotted(button) {\r\n      min-width: 24px;\r\n      height: 24px;\r\n      padding: 1px;\r\n      background: white;\r\n      border: 1px solid lightgray;\r\n    }\r\n  `;\r\n\r\n  public static override readonly template = html`\r\n    <div>\r\n      <div id=\"header\" style=\"display: none\"><span id=\"property\"></span><span id=\"vhd\">: <span id=\"value\"></span></span></div>\r\n      <div part=\"container\" class=\"container\"><slot id=\"slot\"></slot></div>\r\n    </div>\r\n  `;\r\n\r\n  public static properties = {\r\n    value: String,\r\n    property: String,\r\n    unsetValue: String,\r\n    noValueInHeader: Boolean\r\n  }\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n  }\r\n\r\n  private _value: string;\r\n  public get value() {\r\n    return this._value;\r\n  }\r\n  public set value(value) {\r\n    this._value = value;\r\n    this._updateValue();\r\n  }\r\n\r\n  public property: string;\r\n  public unsetValue: string;\r\n  public noValueInHeader: boolean;\r\n\r\n  _updateValue() {\r\n    if (this.value) {\r\n      this._getDomElement<HTMLSpanElement>('value').innerText = this.value;\r\n      this._getDomElement<HTMLSpanElement>('value').classList.add('value-set');\r\n    } else {\r\n      this._getDomElement<HTMLSpanElement>('value').classList.remove('value-set');\r\n    }\r\n\r\n    const slot = this._getDomElement<HTMLSlotElement>('slot');\r\n    for (let e of slot.assignedElements()) {\r\n      if ((<HTMLElement>e).dataset.value == this.value)\r\n        (<HTMLElement>e).style.background = \"cornflowerblue\";\r\n      else (<HTMLElement>e).style.background = \"\";\r\n    }\r\n  }\r\n\r\n  ready() {\r\n    this._parseAttributesToProperties();\r\n\r\n    if (this.property)\r\n      this._getDomElement<HTMLSpanElement>('header').style.display = 'block';\r\n\r\n    if (this.noValueInHeader)\r\n      this._getDomElement<HTMLSpanElement>('vhd').style.display = 'none';\r\n\r\n    const slot = this._getDomElement<HTMLSlotElement>('slot');\r\n    slot.onclick = (e) => {\r\n      const path = e.composedPath();\r\n      for (let e of slot.assignedElements()) {\r\n        if (path.indexOf(e) >= 0) {\r\n          const oldValue = this._value;\r\n          this.value = (<HTMLElement>e).dataset.value;\r\n          const valueChangedEvent = new CustomEvent('value-changed', {\r\n            detail: {\r\n              newValue: this._value, oldValue: oldValue\r\n            }\r\n          });\r\n          this.dispatchEvent(valueChangedEvent);\r\n        }\r\n      }\r\n    }\r\n\r\n    this._getDomElement<HTMLSpanElement>('property').innerText = this.property ?? '';\r\n    this._getDomElement<HTMLSpanElement>('value').innerText = this.unsetValue ?? '';\r\n    this._updateValue();\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-image-button-list-selector', ImageButtonListSelector);"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/MetricsEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\n\nexport type MetricsEditorArea = 'position' | 'margin' | 'border' | 'padding' | 'content';\nexport type MetricsEditorSide = 'top' | 'right' | 'bottom' | 'left' | 'width' | 'height';\nexport type MetricsEditorValueChangedEventArgs = {\n  property: string,\n  area: MetricsEditorArea,\n  side: MetricsEditorSide,\n  newValue?: string,\n  oldValue?: string\n};\n\ntype MetricsEditorValueMap = Partial<Record<MetricsEditorArea, Partial<Record<MetricsEditorSide, string>>>>;\nconst metricsEditorNaturalWidth = 436;\n\nconst cssProperties: Record<MetricsEditorArea, Partial<Record<MetricsEditorSide, string>>> = {\n  position: {\n    top: 'top',\n    right: 'right',\n    bottom: 'bottom',\n    left: 'left'\n  },\n  margin: {\n    top: 'margin-top',\n    right: 'margin-right',\n    bottom: 'margin-bottom',\n    left: 'margin-left'\n  },\n  border: {\n    top: 'border-top-width',\n    right: 'border-right-width',\n    bottom: 'border-bottom-width',\n    left: 'border-left-width'\n  },\n  padding: {\n    top: 'padding-top',\n    right: 'padding-right',\n    bottom: 'padding-bottom',\n    left: 'padding-left'\n  },\n  content: {\n    width: 'width',\n    height: 'height'\n  }\n};\n\nconst computedStyleProperties: Record<string, string> = {\n  'border-top-width': 'borderTopWidth',\n  'border-right-width': 'borderRightWidth',\n  'border-bottom-width': 'borderBottomWidth',\n  'border-left-width': 'borderLeftWidth',\n  'margin-top': 'marginTop',\n  'margin-right': 'marginRight',\n  'margin-bottom': 'marginBottom',\n  'margin-left': 'marginLeft',\n  'padding-top': 'paddingTop',\n  'padding-right': 'paddingRight',\n  'padding-bottom': 'paddingBottom',\n  'padding-left': 'paddingLeft'\n};\n\nexport class MetricsEditor extends BaseCustomWebComponentConstructorAppend {\n\n  public static override readonly style = css`\n    :host {\n      display: block;\n      box-sizing: border-box;\n      min-width: 0;\n      color: var(--property-grid-text-color, white);\n      font: 11px monospace;\n      overflow: hidden;\n    }\n\n    #box-model {\n      display: grid;\n      grid-template-columns: 46px minmax(120px, 1fr) 46px;\n      grid-template-rows: 22px minmax(29px, auto) minmax(58px, auto) minmax(29px, auto) 22px;\n      grid-template-areas:\n        \". position-top .\"\n        \"position-left margin position-right\"\n        \"position-left margin position-right\"\n        \"position-left margin position-right\"\n        \". position-bottom .\";\n      align-items: center;\n      justify-items: center;\n      width: 100%;\n      min-width: ${metricsEditorNaturalWidth}px;\n      box-sizing: border-box;\n      padding: 4px;\n      transform-origin: top left;\n    }\n\n    .ring {\n      display: grid;\n      grid-template-columns: 42px minmax(56px, 1fr) 42px;\n      grid-template-rows: 20px minmax(34px, auto) 20px;\n      grid-template-areas:\n        \". top .\"\n        \"left inner right\"\n        \". bottom .\";\n      align-items: center;\n      justify-items: center;\n      position: relative;\n      box-sizing: border-box;\n      width: 100%;\n      min-width: 0;\n      height: 100%;\n      min-height: 116px;\n      border: 1px dashed rgba(0, 0, 0, .55);\n    }\n\n    #margin {\n      grid-area: margin;\n      background: #f6c89f;\n    }\n\n    #border {\n      grid-area: inner;\n      background: #f7dd9c;\n      border-style: solid;\n      min-height: 76px;\n    }\n\n    #padding {\n      grid-area: inner;\n      background: #c8d08f;\n      min-height: 38px;\n    }\n\n    #content {\n      grid-area: inner;\n      display: grid;\n      grid-template-columns: minmax(26px, 1fr) auto minmax(26px, 1fr);\n      gap: 4px;\n      align-items: center;\n      justify-items: center;\n      width: 100%;\n      height: 100%;\n      min-height: 24px;\n      box-sizing: border-box;\n      background: #8fb9c3;\n      border: 1px solid rgba(0, 0, 0, .65);\n    }\n\n    #position-label,\n    #content-label {\n      display: none;\n    }\n\n    #content.box,\n    #border.box {\n      outline: 2px solid rgba(0, 0, 0, .85);\n      outline-offset: -2px;\n    }\n\n    .label {\n      position: absolute;\n      top: 2px;\n      left: 4px;\n      max-width: calc(100% - 8px);\n      overflow: hidden;\n      text-overflow: ellipsis;\n      pointer-events: none;\n      color: rgba(0, 0, 0, .72);\n      font-size: 10px;\n      line-height: 12px;\n    }\n\n    input {\n      width: 38px;\n      max-width: 100%;\n      min-width: 0;\n      height: 17px;\n      box-sizing: border-box;\n      padding: 0 2px;\n      border: 0;\n      border-radius: 0;\n      background: transparent;\n      color: rgba(0, 0, 0, .85);\n      font: inherit;\n      line-height: 17px;\n      text-align: center;\n      outline: none;\n    }\n\n    input:hover,\n    input:focus {\n      background: rgba(255, 255, 255, .72);\n      box-shadow: 0 0 0 1px rgba(0, 0, 0, .35);\n    }\n\n    input:disabled {\n      opacity: .65;\n    }\n\n    [data-side=\"top\"] {\n      grid-area: top;\n    }\n\n    [data-side=\"right\"] {\n      grid-area: right;\n    }\n\n    [data-side=\"bottom\"] {\n      grid-area: bottom;\n    }\n\n    [data-side=\"left\"] {\n      grid-area: left;\n    }\n\n    [data-area=\"position\"][data-side=\"top\"] {\n      grid-area: position-top;\n    }\n\n    [data-area=\"position\"][data-side=\"right\"] {\n      grid-area: position-right;\n    }\n\n    [data-area=\"position\"][data-side=\"bottom\"] {\n      grid-area: position-bottom;\n    }\n\n    [data-area=\"position\"][data-side=\"left\"] {\n      grid-area: position-left;\n    }\n  `;\n\n  public static override readonly template = html`\n    <div id=\"box-model\">\n      <span id=\"position-label\" class=\"label\" title=\"position\">position</span>\n      <input data-area=\"position\" data-side=\"top\" title=\"top\" spellcheck=\"false\">\n      <input data-area=\"position\" data-side=\"right\" title=\"right\" spellcheck=\"false\">\n      <input data-area=\"position\" data-side=\"bottom\" title=\"bottom\" spellcheck=\"false\">\n      <input data-area=\"position\" data-side=\"left\" title=\"left\" spellcheck=\"false\">\n\n      <div id=\"margin\" class=\"ring\">\n        <span class=\"label\" title=\"margin\">margin</span>\n        <input data-area=\"margin\" data-side=\"top\" title=\"margin-top\" spellcheck=\"false\">\n        <input data-area=\"margin\" data-side=\"right\" title=\"margin-right\" spellcheck=\"false\">\n        <input data-area=\"margin\" data-side=\"bottom\" title=\"margin-bottom\" spellcheck=\"false\">\n        <input data-area=\"margin\" data-side=\"left\" title=\"margin-left\" spellcheck=\"false\">\n\n        <div id=\"border\" class=\"ring\">\n          <span class=\"label\" title=\"border\">border</span>\n          <input data-area=\"border\" data-side=\"top\" title=\"border-top-width\" spellcheck=\"false\">\n          <input data-area=\"border\" data-side=\"right\" title=\"border-right-width\" spellcheck=\"false\">\n          <input data-area=\"border\" data-side=\"bottom\" title=\"border-bottom-width\" spellcheck=\"false\">\n          <input data-area=\"border\" data-side=\"left\" title=\"border-left-width\" spellcheck=\"false\">\n\n          <div id=\"padding\" class=\"ring\">\n            <span class=\"label\" title=\"padding\">padding</span>\n            <input data-area=\"padding\" data-side=\"top\" title=\"padding-top\" spellcheck=\"false\">\n            <input data-area=\"padding\" data-side=\"right\" title=\"padding-right\" spellcheck=\"false\">\n            <input data-area=\"padding\" data-side=\"bottom\" title=\"padding-bottom\" spellcheck=\"false\">\n            <input data-area=\"padding\" data-side=\"left\" title=\"padding-left\" spellcheck=\"false\">\n\n            <div id=\"content\">\n              <span id=\"content-label\" class=\"label\" title=\"content\">content</span>\n              <input data-area=\"content\" data-side=\"width\" title=\"width\" spellcheck=\"false\">\n              <span id=\"content-separator\">x</span>\n              <input data-area=\"content\" data-side=\"height\" title=\"height\" spellcheck=\"false\">\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `;\n\n  public property: string;\n  public unsetValue = '-';\n\n  private _borderDiv: HTMLDivElement;\n  private _boxModelDiv: HTMLDivElement;\n  private _contentDiv: HTMLDivElement;\n  private _inputs: HTMLInputElement[] = [];\n  private _values: MetricsEditorValueMap = {};\n  private _isRefreshing = false;\n  private _resizeObserver: ResizeObserver;\n\n  constructor() {\n    super();\n    this._restoreCachedInititalValues();\n\n    this._borderDiv = this._getDomElement<HTMLDivElement>('border');\n    this._boxModelDiv = this._getDomElement<HTMLDivElement>('box-model');\n    this._contentDiv = this._getDomElement<HTMLDivElement>('content');\n    this._inputs = [...this.shadowRoot.querySelectorAll<HTMLInputElement>('input[data-area][data-side]')];\n  }\n\n  ready() {\n    this._parseAttributesToProperties();\n    this._wireEvents();\n    this._updateInputs();\n    requestAnimationFrame(() => this._updateScale());\n  }\n\n  connectedCallback() {\n    this._resizeObserver ??= new ResizeObserver(() => this._updateScale());\n    this._resizeObserver.observe(this);\n    requestAnimationFrame(() => this._updateScale());\n  }\n\n  disconnectedCallback() {\n    this._resizeObserver?.disconnect();\n  }\n\n  public get values(): MetricsEditorValueMap {\n    return this._cloneValues(this._values);\n  }\n\n  public set values(value: MetricsEditorValueMap) {\n    this._values = this._cloneValues(value ?? {});\n    this._updateInputs();\n  }\n\n  public getPropertyName(area: MetricsEditorArea, side: MetricsEditorSide) {\n    return cssProperties[area]?.[side];\n  }\n\n  public refresh(element: Element) {\n    this._contentDiv.classList.remove('box');\n    this._borderDiv.classList.remove('box');\n\n    if (!element) {\n      this.values = {};\n      return;\n    }\n\n    const computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);\n    const nextValues: MetricsEditorValueMap = {};\n\n    for (const area of Object.keys(cssProperties) as MetricsEditorArea[]) {\n      nextValues[area] = {};\n      for (const side of Object.keys(cssProperties[area]) as MetricsEditorSide[]) {\n        const propertyName = this.getPropertyName(area, side);\n        nextValues[area][side] = this._getComputedProperty(computedStyle, propertyName, area);\n      }\n    }\n\n    if (computedStyle.boxSizing == 'content-box')\n      this._contentDiv.classList.add('box');\n    else\n      this._borderDiv.classList.add('box');\n\n    this.values = nextValues;\n    this._updateScale();\n  }\n\n  private _wireEvents() {\n    for (const input of this._inputs) {\n      input.addEventListener('focus', () => input.select());\n      input.addEventListener('change', () => this._commitInput(input));\n      input.addEventListener('keydown', event => {\n        if (event.key === 'Enter') {\n          this._commitInput(input);\n          input.blur();\n        } else if (event.key === 'Escape') {\n          this._updateInput(input);\n          input.blur();\n        }\n      });\n    }\n  }\n\n  private _commitInput(input: HTMLInputElement) {\n    if (this._isRefreshing)\n      return;\n\n    const area = input.dataset['area'] as MetricsEditorArea;\n    const side = input.dataset['side'] as MetricsEditorSide;\n    const oldValue = this._values[area]?.[side] ?? '';\n    const newValue = input.value.trim();\n\n    if (!this._values[area])\n      this._values[area] = {};\n    this._values[area][side] = newValue;\n    this._updateInput(input);\n\n    if (oldValue === newValue)\n      return;\n\n    this.dispatchEvent(new CustomEvent<MetricsEditorValueChangedEventArgs>('value-changed', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        property: this.getPropertyName(area, side),\n        area,\n        side,\n        newValue,\n        oldValue\n      }\n    }));\n  }\n\n  private _updateInputs() {\n    this._isRefreshing = true;\n    try {\n      for (const input of this._inputs)\n        this._updateInput(input);\n    } finally {\n      this._isRefreshing = false;\n    }\n  }\n\n  private _updateInput(input: HTMLInputElement) {\n    const area = input.dataset['area'] as MetricsEditorArea;\n    const side = input.dataset['side'] as MetricsEditorSide;\n    input.value = this._values[area]?.[side] ?? this.unsetValue;\n  }\n\n  private _getComputedProperty(computedStyle: CSSStyleDeclaration, propertyName: string, area: MetricsEditorArea) {\n    const camelName = computedStyleProperties[propertyName] ?? propertyName;\n    const value = computedStyle.getPropertyValue(propertyName) || computedStyle[camelName];\n    if (area === 'position' && value === 'auto')\n      return this.unsetValue;\n    return value || this.unsetValue;\n  }\n\n  private _cloneValues(values: MetricsEditorValueMap): MetricsEditorValueMap {\n    const clone: MetricsEditorValueMap = {};\n    for (const area of Object.keys(values) as MetricsEditorArea[])\n      clone[area] = { ...values[area] };\n    return clone;\n  }\n\n  private _updateScale() {\n    if (!this._boxModelDiv)\n      return;\n\n    const availableWidth = this.clientWidth;\n    if (availableWidth <= 0)\n      return;\n\n    const scale = Math.min(1, availableWidth / metricsEditorNaturalWidth);\n    this._boxModelDiv.style.width = scale < 1 ? metricsEditorNaturalWidth + 'px' : '100%';\n    this._boxModelDiv.style.transform = scale < 1 ? `scale(${scale})` : '';\n    this.style.height = (this._boxModelDiv.offsetHeight * scale) + 'px';\n  }\n}\n\ncustomElements.define('node-projects-metrics-editor', MetricsEditor);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/NumericStyleInput.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { combineNumericStyleInputValue, formatNumericStyleInputNumber, getNumericStyleInputUnitLabel, normalizeNumericStyleInputOptionValues, parseNumericStyleInputValue, resolveNumericStyleInputSelectedUnit, resolveNumericStyleInputStep } from './NumericStyleInputValueHelpers.js';\r\n\r\nexport type { ParsedNumericStyleInputValue } from './NumericStyleInputValueHelpers.js';\r\nexport { parseNumericStyleInputValue, formatNumericStyleInputNumber, combineNumericStyleInputValue } from './NumericStyleInputValueHelpers.js';\r\n\r\nexport type NumericStyleInputValueChangedEventArgs = { newValue?: string, oldValue?: string };\r\nexport type NumericStyleInputPreviewFinishedEventArgs = { newValue?: string, oldValue?: string, wasCancelled?: boolean };\r\nexport type NumericStyleInputUnitValueConversionArgs = {\r\n  value: number,\r\n  numberText: string,\r\n  rawValue: string,\r\n  fromUnit: string,\r\n  toUnit: string\r\n};\r\n\r\ntype NumericStyleInputMode = 'unit' | 'fixed' | 'custom';\r\n\r\ntype NumericStyleInputDisplayState = {\r\n  mode: NumericStyleInputMode,\r\n  inputValue: string,\r\n  inputVisible: boolean,\r\n  inputEnabled: boolean,\r\n  selectValue: string,\r\n  selectedUnit?: string\r\n};\r\n\r\nconst customOptionValue = '__node-projects-custom-value__';\r\nconst dragHandleGlyph = '⋮';\r\nexport class NumericStyleInput extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  public static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      width: 100%;\r\n      min-width: 0;\r\n    }\r\n\r\n    #container {\r\n      display: grid;\r\n      gap: 0;\r\n      grid-template-columns: minmax(0, 1fr) auto 16px;\r\n      width: 100%;\r\n      height: 24px;\r\n      align-items: stretch;\r\n    }\r\n\r\n    #value-wrapper {\r\n      display: grid;\r\n      grid-template-columns: 14px minmax(0, 1fr);\r\n      min-width: 0;\r\n    }\r\n\r\n    #scrubber,\r\n    #input,\r\n    #select,\r\n    #stepper button {\r\n      /* border: 1px solid var(--input-border-color, #596c7a); */\r\n      border: none;\r\n      box-sizing: border-box;\r\n      height: 24px;\r\n      min-height: 24px;\r\n      background: transparent;\r\n      color: inherit;\r\n      font: inherit;\r\n      outline: none;\r\n      box-shadow: none;\r\n    }\r\n\r\n    #scrubber,\r\n    #stepper button {\r\n      padding: 0;\r\n      line-height: 1;\r\n    }\r\n\r\n    #scrubber {\r\n      border-right: 0;\r\n      cursor: ns-resize;\r\n      font-size: 11px;\r\n      letter-spacing: -1px;\r\n    }\r\n\r\n    #input {\r\n      border-left: 0;\r\n      border-right: 0;\r\n      min-width: 0;\r\n      text-align: right;\r\n    }\r\n\r\n    #input:focus,\r\n    #select:focus,\r\n    #scrubber:focus,\r\n    #stepper button:focus {\r\n      outline: none;\r\n      box-shadow: none;\r\n      border-color: var(--input-border-color, #596c7a);\r\n    }\r\n\r\n    #input[disabled],\r\n    #scrubber[disabled] {\r\n      cursor: default;\r\n    }\r\n\r\n    #input[disabled] {\r\n      color: inherit;\r\n      opacity: 1;\r\n      -webkit-text-fill-color: currentColor;\r\n    }\r\n\r\n    #select {\r\n      border-left: 0;\r\n      padding: 0 15px 0 2px;\r\n      line-height: 1;\r\n      -webkit-appearance: none;\r\n      appearance: none;\r\n      background: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4'%3E%3Cpath d='M0 0l4 4 4-4z' fill='%23999'/%3E%3C/svg%3E\") no-repeat right 2px center;\r\n    }\r\n\r\n    #measure {\r\n      position: absolute;\r\n      visibility: hidden;\r\n      white-space: nowrap;\r\n      font: inherit;\r\n      pointer-events: none;\r\n    }\r\n\r\n    #stepper {\r\n      display: grid;\r\n      height: 24px;\r\n      min-height: 24px;\r\n      overflow: hidden;\r\n      grid-template-rows: repeat(2, minmax(0, 1fr));\r\n    }\r\n\r\n    #stepper button {\r\n      height: auto;\r\n      min-height: 0;\r\n      width: 16px;\r\n      min-width: 16px;\r\n      font-size: 10px;\r\n    }\r\n  `;\r\n\r\n  public static override readonly template = html`\r\n    <div id=\"container\">\r\n      <div id=\"value-wrapper\">\r\n        <button id=\"scrubber\" type=\"button\" aria-label=\"Drag to change value\">${dragHandleGlyph}</button>\r\n        <input id=\"input\" type=\"text\" spellcheck=\"false\">\r\n      </div>\r\n      <select id=\"select\"></select>\r\n      <div id=\"stepper\">\r\n        <button id=\"increase\" type=\"button\" aria-label=\"Increase value\">+</button>\r\n        <button id=\"decrease\" type=\"button\" aria-label=\"Decrease value\">-</button>\r\n      </div>\r\n      <span id=\"measure\"></span>\r\n    </div>\r\n  `;\r\n\r\n  private _value = '';\r\n  public get value() {\r\n    return this._value;\r\n  }\r\n  public set value(value) {\r\n    this._setValue(value, false, false);\r\n  }\r\n  public valueChanged = new TypedEvent<NumericStyleInputValueChangedEventArgs>();\r\n  public valuePreviewChanged = new TypedEvent<NumericStyleInputValueChangedEventArgs>();\r\n  public valuePreviewFinished = new TypedEvent<NumericStyleInputPreviewFinishedEventArgs>();\r\n\r\n  private _units: string[] = ['px', '%', 'pt'];\r\n  public get units() {\r\n    return [...this._units];\r\n  }\r\n  public set units(value: string[]) {\r\n    this._units = this._normalizeOptionValues(value);\r\n    this._lastNumericUnit = this._units[0] ?? this._lastNumericUnit;\r\n    this._updateValue();\r\n  }\r\n\r\n  private _fixedValues: string[] = [];\r\n  public get fixedValues() {\r\n    return [...this._fixedValues];\r\n  }\r\n  public set fixedValues(value: string[]) {\r\n    this._fixedValues = this._normalizeOptionValues(value);\r\n    this._updateValue();\r\n  }\r\n\r\n  private _step = 1;\r\n  public get step() {\r\n    return this._step;\r\n  }\r\n  public set step(value: number) {\r\n    this._step = Number.isFinite(value) && value > 0 ? value : 1;\r\n  }\r\n\r\n  private _unitSteps: Record<string, number> = {};\r\n  public get unitSteps() {\r\n    return { ...this._unitSteps };\r\n  }\r\n  public set unitSteps(value: Record<string, number>) {\r\n    this._unitSteps = value ?? {};\r\n  }\r\n\r\n  private _min: number = null;\r\n  public get min() {\r\n    return this._min;\r\n  }\r\n  public set min(value: number) {\r\n    this._min = Number.isFinite(value) ? value : null;\r\n  }\r\n\r\n  private _max: number = null;\r\n  public get max() {\r\n    return this._max;\r\n  }\r\n  public set max(value: number) {\r\n    this._max = Number.isFinite(value) ? value : null;\r\n  }\r\n\r\n  private _readOnly = false;\r\n  public get readOnly() {\r\n    return this._readOnly;\r\n  }\r\n  public set readOnly(value: boolean) {\r\n    this._readOnly = value;\r\n    this._applyReadonlyState();\r\n  }\r\n\r\n  public get isInPreview(): boolean {\r\n    return this._previewStartValue != null;\r\n  }\r\n\r\n  private _allowCustomValue = true;\r\n  public get allowCustomValue() {\r\n    return this._allowCustomValue;\r\n  }\r\n  public set allowCustomValue(value: boolean) {\r\n    this._allowCustomValue = value;\r\n    this._updateValue();\r\n  }\r\n\r\n  private _unitValueConverter: (args: NumericStyleInputUnitValueConversionArgs) => string;\r\n  public get unitValueConverter() {\r\n    return this._unitValueConverter;\r\n  }\r\n  public set unitValueConverter(value: (args: NumericStyleInputUnitValueConversionArgs) => string) {\r\n    this._unitValueConverter = value;\r\n  }\r\n\r\n  private _input: HTMLInputElement;\r\n  private _select: HTMLSelectElement;\r\n  private _measure: HTMLSpanElement;\r\n  private _scrubberButton: HTMLButtonElement;\r\n  private _increaseButton: HTMLButtonElement;\r\n  private _decreaseButton: HTMLButtonElement;\r\n  private _lastNumericValue = 0;\r\n  private _lastNumericUnit = 'px';\r\n  private _dragPointerId: number = null;\r\n  private _dragStartY = 0;\r\n  private _dragStartNumericValue = 0;\r\n  private _appliedDragSteps = 0;\r\n  private _previewStartValue: string = null;\r\n  private _previewChanged = false;\r\n  private _displayValueLock: string = null;\r\n  private _stepperPointerId: number = null;\r\n  private _stepperDirection = 0;\r\n  private _stepperRepeatTimeout: number = null;\r\n  private _stepperRepeatInterval: number = null;\r\n  private _windowDragPointerMoveHandler = (event: PointerEvent) => this._handleWindowDragPointerMove(event);\r\n  private _windowDragPointerUpHandler = (event: PointerEvent) => this._finishDragInteractionForPointerEvent(event, false);\r\n  private _windowDragPointerCancelHandler = (event: PointerEvent) => this._finishDragInteractionForPointerEvent(event, true);\r\n  private _windowDragBlurHandler = () => this._finishDragInteraction(false);\r\n  private _windowPointerUpHandler = (event: PointerEvent) => this._handleWindowStepperPointerEnd(event);\r\n  private _windowPointerCancelHandler = (event: PointerEvent) => this._handleWindowStepperPointerEnd(event);\r\n  private _windowBlurHandler = () => this._finishStepperInteraction(false);\r\n  private _preferCustomMode = false;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    this._input = this._getDomElement<HTMLInputElement>('input');\r\n    this._select = this._getDomElement<HTMLSelectElement>('select');\r\n    this._measure = this._getDomElement<HTMLSpanElement>('measure');\r\n    this._scrubberButton = this._getDomElement<HTMLButtonElement>('scrubber');\r\n    this._increaseButton = this._getDomElement<HTMLButtonElement>('increase');\r\n    this._decreaseButton = this._getDomElement<HTMLButtonElement>('decrease');\r\n  }\r\n\r\n  ready() {\r\n    this._parseAttributesToProperties();\r\n    this._wireEvents();\r\n    this._updateValue();\r\n  }\r\n\r\n  private _wireEvents() {\r\n    this._input.addEventListener('change', () => this._applyTypedValue());\r\n    this._input.addEventListener('keydown', e => {\r\n      if (e.key === 'Enter') {\r\n        this._applyTypedValue();\r\n        this._input.blur();\r\n      }\r\n    });\r\n\r\n    this._scrubberButton.addEventListener('pointerdown', e => this._handlePointerDown(e));\r\n\r\n    this._select.addEventListener('change', () => this._applySelectedMode());\r\n    this._increaseButton.addEventListener('pointerdown', e => this._handleStepperPointerDown(e, 1));\r\n    this._decreaseButton.addEventListener('pointerdown', e => this._handleStepperPointerDown(e, -1));\r\n  }\r\n\r\n  private _handlePointerDown(event: PointerEvent) {\r\n    if (this._readOnly)\r\n      return;\r\n    if (!this._isSelectedUnitMode()) {\r\n      if (!this._switchToUnitModeForInteraction())\r\n        return;\r\n    }\r\n    this._dragPointerId = event.pointerId;\r\n    this._dragStartY = event.clientY;\r\n    this._dragStartNumericValue = this._readEditableNumericValue(this._select.value);\r\n    this._appliedDragSteps = 0;\r\n    this._startPreviewSession();\r\n    this._attachDragWindowListeners();\r\n    try {\r\n      this._scrubberButton.setPointerCapture(event.pointerId);\r\n    } catch {\r\n    }\r\n    event.preventDefault();\r\n  }\r\n\r\n  private _handleWindowDragPointerMove(event: PointerEvent) {\r\n    if (this._dragPointerId !== event.pointerId || !this._isSelectedUnitMode())\r\n      return;\r\n\r\n    const stepCount = this._calculateDraggedStepCount(this._dragStartY - event.clientY);\r\n    if (stepCount === this._appliedDragSteps)\r\n      return;\r\n\r\n    this._appliedDragSteps = stepCount;\r\n    const unit = this._select.value;\r\n    const step = this._getEffectiveStep(unit);\r\n    this._previewNumericValue(this._getInteractiveStepValue(this._dragStartNumericValue, stepCount, step), unit, step);\r\n    event.preventDefault();\r\n  }\r\n\r\n  private _finishDragInteractionForPointerEvent(event: PointerEvent, wasCancelled: boolean) {\r\n    if (this._dragPointerId !== event.pointerId)\r\n      return;\r\n\r\n    this._finishDragInteraction(wasCancelled, event.pointerId);\r\n  }\r\n\r\n  private _finishDragInteraction(wasCancelled: boolean, pointerId?: number) {\r\n    if (this._dragPointerId == null)\r\n      return;\r\n\r\n    this._dragPointerId = null;\r\n    this._appliedDragSteps = 0;\r\n    this._detachDragWindowListeners();\r\n    if (pointerId != null) {\r\n      try {\r\n        if (this._scrubberButton.hasPointerCapture(pointerId))\r\n          this._scrubberButton.releasePointerCapture(pointerId);\r\n      } catch {\r\n      }\r\n    }\r\n    this._finishPreviewSession(wasCancelled);\r\n  }\r\n\r\n  private _handleStepperPointerDown(event: PointerEvent, direction: number) {\r\n    if (this._readOnly)\r\n      return;\r\n    if (!this._isSelectedUnitMode()) {\r\n      if (!this._switchToUnitModeForInteraction())\r\n        return;\r\n    }\r\n\r\n    this._finishStepperInteraction(true);\r\n    this._stepperPointerId = event.pointerId;\r\n    this._stepperDirection = direction;\r\n    this._attachStepperWindowListeners();\r\n    this._startPreviewSession();\r\n    this._applyStepPreview(direction);\r\n    this._stepperRepeatTimeout = window.setTimeout(() => {\r\n      this._stepperRepeatInterval = window.setInterval(() => this._applyStepPreview(this._stepperDirection), 50);\r\n    }, 350);\r\n    event.preventDefault();\r\n  }\r\n\r\n  private _handleWindowStepperPointerEnd(event: PointerEvent) {\r\n    if (this._stepperPointerId !== event.pointerId)\r\n      return;\r\n\r\n    this._finishStepperInteraction(false);\r\n  }\r\n\r\n  private _finishStepperInteraction(wasCancelled: boolean) {\r\n    if (this._stepperPointerId == null)\r\n      return;\r\n\r\n    this._stepperPointerId = null;\r\n    this._stepperDirection = 0;\r\n    this._detachStepperWindowListeners();\r\n    this._stopStepperRepeat();\r\n    this._finishPreviewSession(wasCancelled);\r\n  }\r\n\r\n  private _applySelectedMode() {\r\n    const selectedOption = this._select.selectedOptions.item(0);\r\n    if (!selectedOption)\r\n      return;\r\n\r\n    const selectedKind = selectedOption.dataset['kind'];\r\n    if (selectedKind === 'fixed') {\r\n      this._commitValue(selectedOption.value);\r\n      return;\r\n    }\r\n\r\n    if (selectedKind === 'custom') {\r\n      this._preferCustomMode = true;\r\n      this._updateValue();\r\n      requestAnimationFrame(() => {\r\n        this._input.focus();\r\n        this._input.select();\r\n      });\r\n      return;\r\n    }\r\n\r\n    const selectedUnit = selectedOption.value;\r\n    this._lastNumericUnit = selectedUnit;\r\n    this._preferCustomMode = false;\r\n\r\n    const parsedValue = parseNumericStyleInputValue(this._value);\r\n    if (parsedValue.kind === 'numeric') {\r\n      const convertedValue = this._convertNumericValue(parsedValue, selectedUnit);\r\n      this._commitValue(convertedValue);\r\n      return;\r\n    }\r\n\r\n    const resolvedCurrentValue = this._resolveCurrentValueForUnit(selectedUnit);\r\n    if (resolvedCurrentValue != null) {\r\n      this._commitValue(resolvedCurrentValue);\r\n      return;\r\n    }\r\n\r\n    if (parsedValue.kind === 'empty') {\r\n      this._updateValue();\r\n      this._input.focus();\r\n      return;\r\n    }\r\n\r\n    const typedNumericValue = Number(this._input.value);\r\n    if (!Number.isNaN(typedNumericValue)) {\r\n      this._applyNumericValue(typedNumericValue, selectedUnit);\r\n      return;\r\n    }\r\n\r\n    // Switching from a non-numeric value (e.g. unset/inherit) to a unit:\r\n    // clear the value so the unit mode is shown with an empty input\r\n    this._commitValue('');\r\n    this._input.focus();\r\n  }\r\n\r\n  private _applyTypedValue() {\r\n    const selectedOption = this._select.selectedOptions.item(0);\r\n    const selectedKind = selectedOption?.dataset['kind'];\r\n    if (selectedKind === 'fixed')\r\n      return;\r\n\r\n    if (selectedKind === 'custom') {\r\n      this._commitValue(this._input.value);\r\n      return;\r\n    }\r\n\r\n    const numberText = this._input.value?.trim() ?? '';\r\n    if (!numberText) {\r\n      this._commitValue('');\r\n      return;\r\n    }\r\n\r\n    const numericValue = Number(numberText);\r\n    if (!Number.isNaN(numericValue))\r\n      this._lastNumericValue = this._clampNumericValue(numericValue);\r\n\r\n    this._commitValue(combineNumericStyleInputValue(numberText, selectedOption?.value ?? ''));\r\n  }\r\n\r\n  private _updateValue() {\r\n    if (!this._input || !this._select)\r\n      return;\r\n\r\n    if (this._previewStartValue == null)\r\n      this._renderSelectOptions();\r\n\r\n    const displayState = this._getDisplayState(this._displayValueLock ?? this._value);\r\n    this._input.value = displayState.inputValue;\r\n    this._input.style.display = displayState.inputVisible ? '' : 'none';\r\n    this._input.disabled = this._readOnly || !displayState.inputEnabled;\r\n    this._select.value = displayState.selectValue;\r\n    this._autoSizeSelect();\r\n\r\n    if (displayState.mode === 'unit' && displayState.selectedUnit != null)\r\n      this._lastNumericUnit = displayState.selectedUnit;\r\n    if (displayState.mode === 'unit' && displayState.inputValue !== '') {\r\n      const typedNumericValue = Number(displayState.inputValue);\r\n      if (!Number.isNaN(typedNumericValue))\r\n        this._lastNumericValue = this._clampNumericValue(typedNumericValue);\r\n    }\r\n\r\n    const hasUnits = this._units.length > 0;\r\n    const interactionDisabled = this._readOnly || (!hasUnits || (displayState.mode === 'custom'));\r\n    this._scrubberButton.disabled = interactionDisabled;\r\n    this._increaseButton.disabled = interactionDisabled;\r\n    this._decreaseButton.disabled = interactionDisabled;\r\n    this._applyReadonlyState();\r\n  }\r\n\r\n  private _getDisplayState(value: string): NumericStyleInputDisplayState {\r\n    const parsedValue = parseNumericStyleInputValue(value);\r\n    if (parsedValue.kind === 'numeric') {\r\n      if (this._preferCustomMode)\r\n        return this._createCustomDisplayState(parsedValue.numberText);\r\n\r\n      if (parsedValue.unit && this._units.length && !this._units.includes(parsedValue.unit))\r\n        return this._createCustomDisplayState(value);\r\n\r\n      const selectedUnit = resolveNumericStyleInputSelectedUnit(parsedValue.unit, this._lastNumericUnit, this._units) ?? '';\r\n      return {\r\n        mode: 'unit',\r\n        inputValue: parsedValue.numberText,\r\n        inputVisible: true,\r\n        inputEnabled: true,\r\n        selectValue: this._units.includes(selectedUnit) ? selectedUnit : (this._select.value || customOptionValue),\r\n        selectedUnit\r\n      };\r\n    }\r\n\r\n    if (parsedValue.kind === 'text') {\r\n      if (!this._preferCustomMode && this._fixedValues.includes(parsedValue.text)) {\r\n        return {\r\n          mode: 'fixed',\r\n          inputValue: '',\r\n          inputVisible: true,\r\n          inputEnabled: false,\r\n          selectValue: parsedValue.text\r\n        };\r\n      }\r\n      return this._createCustomDisplayState(parsedValue.text);\r\n    }\r\n\r\n    if (this._preferCustomMode)\r\n      return this._createCustomDisplayState('');\r\n\r\n    const selectedUnit = this._lastNumericUnit ?? this._units[0] ?? '';\r\n    if (this._units.includes(selectedUnit)) {\r\n      return {\r\n        mode: 'unit',\r\n        inputValue: '',\r\n        inputVisible: true,\r\n        inputEnabled: true,\r\n        selectValue: selectedUnit,\r\n        selectedUnit\r\n      };\r\n    }\r\n\r\n    return this._createCustomDisplayState('');\r\n  }\r\n\r\n  private _createCustomDisplayState(text: string): NumericStyleInputDisplayState {\r\n    if (!this._allowCustomValue && this._units.length) {\r\n      return {\r\n        mode: 'unit',\r\n        inputValue: text,\r\n        inputVisible: true,\r\n        inputEnabled: true,\r\n        selectValue: this._units[0],\r\n        selectedUnit: this._units[0]\r\n      };\r\n    }\r\n\r\n    return {\r\n      mode: 'custom',\r\n      inputValue: text,\r\n      inputVisible: true,\r\n      inputEnabled: true,\r\n      selectValue: customOptionValue\r\n    };\r\n  }\r\n\r\n  private _renderSelectOptions() {\r\n    const selectedValue = this._select.value;\r\n    this._select.replaceChildren();\r\n\r\n    if (this._units.length) {\r\n      const group = document.createElement('optgroup');\r\n      group.label = 'Units';\r\n      for (const unit of this._units)\r\n        group.appendChild(this._createOption(getNumericStyleInputUnitLabel(unit), unit, 'unit'));\r\n      this._select.appendChild(group);\r\n    }\r\n\r\n    if (this._fixedValues.length) {\r\n      const group = document.createElement('optgroup');\r\n      group.label = 'Values';\r\n      for (const fixedValue of this._fixedValues)\r\n        group.appendChild(this._createOption(fixedValue, fixedValue, 'fixed'));\r\n      this._select.appendChild(group);\r\n    }\r\n\r\n    if (this._allowCustomValue || this._select.options.length === 0)\r\n      this._select.appendChild(this._createOption('custom', customOptionValue, 'custom'));\r\n\r\n    if (selectedValue !== '')\r\n      this._select.value = selectedValue;\r\n    else if (this._select.querySelector('option[value=\"\"]'))\r\n      this._select.value = '';\r\n  }\r\n\r\n  private _createOption(label: string, value: string, kind: NumericStyleInputMode): HTMLOptionElement {\r\n    const option = document.createElement('option');\r\n    option.text = label;\r\n    option.value = value;\r\n    option.dataset['kind'] = kind;\r\n    return option;\r\n  }\r\n\r\n  private _autoSizeSelect() {\r\n    if (!this._measure || !this._select)\r\n      return;\r\n    const selectedOption = this._select.selectedOptions.item(0);\r\n    this._measure.textContent = selectedOption?.text ?? '';\r\n    const textWidth = this._measure.offsetWidth;\r\n    if (textWidth > 0) {\r\n      // 14px accounts for the custom dropdown arrow + padding\r\n      this._select.style.width = (textWidth + 17) + 'px';\r\n    } else {\r\n      // Element not yet laid out, defer measurement\r\n      requestAnimationFrame(() => this._autoSizeSelect());\r\n    }\r\n  }\r\n\r\n  private _applyReadonlyState() {\r\n    if (!this._input || !this._select)\r\n      return;\r\n    this._input.readOnly = this._readOnly;\r\n    this._select.disabled = this._readOnly;\r\n  }\r\n\r\n  private _convertNumericValue(parsedValue: { numberText: string, value: number, unit: string }, selectedUnit: string) {\r\n    if (selectedUnit == null)\r\n      return this._value;\r\n\r\n    const fromUnit = parsedValue.unit;\r\n    const convertedValue = this._unitValueConverter?.({\r\n      value: parsedValue.value,\r\n      numberText: parsedValue.numberText,\r\n      rawValue: this._value,\r\n      fromUnit,\r\n      toUnit: selectedUnit\r\n    });\r\n\r\n    return convertedValue ?? combineNumericStyleInputValue(parsedValue.numberText, selectedUnit);\r\n  }\r\n\r\n  private _resolveCurrentValueForUnit(selectedUnit: string) {\r\n    if (selectedUnit == null)\r\n      return null;\r\n\r\n    const convertedValue = this._unitValueConverter?.({\r\n      value: Number.NaN,\r\n      numberText: '',\r\n      rawValue: this._value,\r\n      fromUnit: '',\r\n      toUnit: selectedUnit\r\n    })?.trim();\r\n\r\n    return convertedValue ? convertedValue : null;\r\n  }\r\n\r\n  private _readEditableNumericValue(selectedUnit: string) {\r\n    const parsedValue = parseNumericStyleInputValue(this._value);\r\n    if (parsedValue.kind === 'numeric') {\r\n      const convertedValue = this._convertNumericValue(parsedValue, selectedUnit);\r\n      const convertedParsedValue = parseNumericStyleInputValue(convertedValue);\r\n      if (convertedParsedValue.kind === 'numeric')\r\n        return convertedParsedValue.value;\r\n      return parsedValue.value;\r\n    }\r\n\r\n    if (this._input.value?.trim()) {\r\n      const typedNumericValue = Number(this._input.value);\r\n      if (!Number.isNaN(typedNumericValue))\r\n        return typedNumericValue;\r\n    }\r\n\r\n    return this._lastNumericValue;\r\n  }\r\n\r\n  private _applyNumericValue(value: number, unit: string) {\r\n    this._lastNumericValue = this._clampNumericValue(value);\r\n    this._lastNumericUnit = unit;\r\n    this._commitValue(combineNumericStyleInputValue(formatNumericStyleInputNumber(this._lastNumericValue), unit));\r\n  }\r\n\r\n  private _previewNumericValue(value: number, unit: string, step?: number) {\r\n    this._lastNumericValue = this._clampNumericValue(value);\r\n    this._lastNumericUnit = unit;\r\n    const effectiveStep = step ?? this._getEffectiveStep(unit);\r\n    this._setValue(combineNumericStyleInputValue(this._formatInteractiveNumericValue(this._lastNumericValue, effectiveStep), unit), false, true);\r\n  }\r\n\r\n  private _commitValue(value: string) {\r\n    this._setValue(value, true, false);\r\n  }\r\n\r\n  private _setValue(value: string, emitCommit: boolean, emitPreview: boolean) {\r\n    const normalizedValue = value ?? '';\r\n    if (!emitCommit && !emitPreview && this._previewStartValue != null)\r\n      return;\r\n\r\n    const oldValue = this._value;\r\n    this._value = normalizedValue;\r\n    if (emitPreview)\r\n      this._displayValueLock = normalizedValue;\r\n    else if (this._displayValueLock != null) {\r\n      if (normalizedValue === this._displayValueLock || normalizedValue !== oldValue)\r\n        this._displayValueLock = null;\r\n    }\r\n    this._preferCustomMode = false;\r\n    this._updateValue();\r\n    if (oldValue !== normalizedValue) {\r\n      if (emitPreview) {\r\n        this._previewChanged = true;\r\n        this.valuePreviewChanged.emit({ newValue: normalizedValue, oldValue: oldValue });\r\n      }\r\n      if (emitCommit)\r\n        this.valueChanged.emit({ newValue: normalizedValue, oldValue: oldValue });\r\n    }\r\n  }\r\n\r\n  private _startPreviewSession() {\r\n    if (this._previewStartValue == null) {\r\n      this._previewStartValue = this._value;\r\n      this._previewChanged = false;\r\n    }\r\n  }\r\n\r\n  private _finishPreviewSession(wasCancelled: boolean) {\r\n    if (this._previewStartValue == null)\r\n      return;\r\n\r\n    const oldValue = this._previewStartValue;\r\n    const newValue = this._displayValueLock ?? this._value;\r\n    const changed = this._previewChanged && oldValue !== newValue;\r\n    const finalWasCancelled = wasCancelled || !changed;\r\n    this._previewStartValue = null;\r\n    this._previewChanged = false;\r\n    if (finalWasCancelled) {\r\n      this._displayValueLock = null;\r\n      this._value = oldValue;\r\n      this._updateValue();\r\n    } else {\r\n      this._displayValueLock = newValue;\r\n      this._updateValue();\r\n    }\r\n    this.valuePreviewFinished.emit({ newValue, oldValue, wasCancelled: finalWasCancelled });\r\n  }\r\n\r\n  private _applyStepPreview(direction: number) {\r\n    const unit = this._select.value;\r\n    const step = this._getEffectiveStep(unit);\r\n    this._previewNumericValue(this._getInteractiveStepValue(this._readEditableNumericValue(unit), direction, step), unit, step);\r\n  }\r\n\r\n  private _stopStepperRepeat() {\r\n    if (this._stepperRepeatTimeout != null) {\r\n      window.clearTimeout(this._stepperRepeatTimeout);\r\n      this._stepperRepeatTimeout = null;\r\n    }\r\n    if (this._stepperRepeatInterval != null) {\r\n      window.clearInterval(this._stepperRepeatInterval);\r\n      this._stepperRepeatInterval = null;\r\n    }\r\n  }\r\n\r\n  private _attachStepperWindowListeners() {\r\n    window.addEventListener('pointerup', this._windowPointerUpHandler, true);\r\n    window.addEventListener('pointercancel', this._windowPointerCancelHandler, true);\r\n    window.addEventListener('blur', this._windowBlurHandler);\r\n  }\r\n\r\n  private _attachDragWindowListeners() {\r\n    window.addEventListener('pointermove', this._windowDragPointerMoveHandler, true);\r\n    window.addEventListener('pointerup', this._windowDragPointerUpHandler, true);\r\n    window.addEventListener('pointercancel', this._windowDragPointerCancelHandler, true);\r\n    window.addEventListener('blur', this._windowDragBlurHandler);\r\n  }\r\n\r\n  private _detachStepperWindowListeners() {\r\n    window.removeEventListener('pointerup', this._windowPointerUpHandler, true);\r\n    window.removeEventListener('pointercancel', this._windowPointerCancelHandler, true);\r\n    window.removeEventListener('blur', this._windowBlurHandler);\r\n  }\r\n\r\n  private _detachDragWindowListeners() {\r\n    window.removeEventListener('pointermove', this._windowDragPointerMoveHandler, true);\r\n    window.removeEventListener('pointerup', this._windowDragPointerUpHandler, true);\r\n    window.removeEventListener('pointercancel', this._windowDragPointerCancelHandler, true);\r\n    window.removeEventListener('blur', this._windowDragBlurHandler);\r\n  }\r\n\r\n  private _calculateDraggedStepCount(pixelDelta: number) {\r\n    const absolutePixels = Math.abs(pixelDelta);\r\n    const fineSteps = Math.min(absolutePixels, 20) / 5;\r\n    const mediumSteps = Math.max(Math.min(absolutePixels - 20, 40), 0) / 3;\r\n    const coarseSteps = Math.max(absolutePixels - 60, 0) / 2;\r\n    const stepCount = Math.trunc(fineSteps + mediumSteps + coarseSteps);\r\n    return Math.sign(pixelDelta) * stepCount;\r\n  }\r\n\r\n  private _getEffectiveStep(unit?: string): number {\r\n    return resolveNumericStyleInputStep(this._unitSteps, this._step, unit);\r\n  }\r\n\r\n  private _getInteractiveStepValue(value: number, stepCount: number, step?: number) {\r\n    const effectiveStep = step ?? this._step;\r\n    const clampedValue = this._clampNumericValue(value);\r\n    if (!Number.isFinite(clampedValue) || !Number.isFinite(stepCount) || stepCount === 0)\r\n      return clampedValue;\r\n\r\n    const stepBase = this._min ?? 0;\r\n    const quotient = (clampedValue - stepBase) / effectiveStep;\r\n    const roundedQuotient = Math.round(quotient);\r\n    const isAligned = Math.abs(quotient - roundedQuotient) < 1e-9;\r\n\r\n    let targetIndex: number;\r\n    if (stepCount > 0) {\r\n      const firstIndex = isAligned ? roundedQuotient + 1 : Math.ceil(quotient);\r\n      targetIndex = firstIndex + stepCount - 1;\r\n    } else {\r\n      const firstIndex = isAligned ? roundedQuotient - 1 : Math.floor(quotient);\r\n      targetIndex = firstIndex + stepCount + 1;\r\n    }\r\n\r\n    const targetValue = stepBase + (targetIndex * effectiveStep);\r\n    return this._clampNumericValue(this._roundToStepPrecision(targetValue, effectiveStep));\r\n  }\r\n\r\n  private _formatInteractiveNumericValue(value: number, step?: number) {\r\n    const effectiveStep = step ?? this._step;\r\n    return formatNumericStyleInputNumber(this._roundToStepPrecision(value, effectiveStep), this._getStepPrecision(effectiveStep));\r\n  }\r\n\r\n  private _roundToStepPrecision(value: number, step?: number) {\r\n    const precision = this._getStepPrecision(step);\r\n    const factor = 10 ** precision;\r\n    return Math.round(value * factor) / factor;\r\n  }\r\n\r\n  private _getStepPrecision(step?: number) {\r\n    const stepText = `${step ?? this._step}`.toLowerCase();\r\n    if (stepText.includes('e-')) {\r\n      const [coefficientText, exponentText] = stepText.split('e-');\r\n      const exponent = Number(exponentText);\r\n      const decimalPartLength = coefficientText.includes('.') ? coefficientText.length - coefficientText.indexOf('.') - 1 : 0;\r\n      return exponent + decimalPartLength;\r\n    }\r\n\r\n    const decimalIndex = stepText.indexOf('.');\r\n    return decimalIndex >= 0 ? stepText.length - decimalIndex - 1 : 0;\r\n  }\r\n\r\n  private _switchToUnitModeForInteraction(): boolean {\r\n    const targetUnit = this._lastNumericUnit ?? this._units[0];\r\n    if (targetUnit == null)\r\n      return false;\r\n\r\n    this._preferCustomMode = false;\r\n\r\n    const resolved = this._resolveCurrentValueForUnit(targetUnit);\r\n    if (resolved != null) {\r\n      this._commitValue(resolved);\r\n      return this._isSelectedUnitMode();\r\n    }\r\n\r\n    this._applyNumericValue(this._lastNumericValue, targetUnit);\r\n    return this._isSelectedUnitMode();\r\n  }\r\n\r\n  private _isSelectedUnitMode() {\r\n    return this._select.selectedOptions.item(0)?.dataset['kind'] === 'unit';\r\n  }\r\n\r\n  private _normalizeOptionValues(values: string[]) {\r\n    return normalizeNumericStyleInputOptionValues(values);\r\n  }\r\n\r\n  private _clampNumericValue(value: number) {\r\n    let result = value;\r\n    if (this._min != null)\r\n      result = Math.max(this._min, result);\r\n    if (this._max != null)\r\n      result = Math.min(this._max, result);\r\n    return result;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-numeric-style-input', NumericStyleInput);"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/NumericStyleInputValueHelpers.ts",
    "content": "export type ParsedNumericStyleInputValue =\n  | { kind: 'empty' }\n  | { kind: 'numeric', numberText: string, value: number, unit: string }\n  | { kind: 'text', text: string };\n\nexport function parseNumericStyleInputValue(value?: string | null): ParsedNumericStyleInputValue {\n  const text = value?.trim() ?? '';\n  if (!text)\n    return { kind: 'empty' };\n\n  const match = text.match(/^([+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+))(?:([a-z%]+))?$/i);\n  if (!match)\n    return { kind: 'text', text };\n\n  const numericValue = Number(match[1]);\n  if (Number.isNaN(numericValue))\n    return { kind: 'text', text };\n\n  return {\n    kind: 'numeric',\n    numberText: match[1],\n    value: numericValue,\n    unit: match[2]?.toLowerCase() ?? ''\n  };\n}\n\nexport function formatNumericStyleInputNumber(value: number, maxDecimalPlaces: number = 4): string {\n  if (!Number.isFinite(value))\n    return '0';\n  const factor = 10 ** Math.max(0, maxDecimalPlaces);\n  const roundedValue = Math.round(value * factor) / factor;\n  return Object.is(roundedValue, -0) ? '0' : `${roundedValue}`;\n}\n\nexport function combineNumericStyleInputValue(numberText: string, unit: string): string {\n  const trimmedNumberText = numberText?.trim() ?? '';\n  if (!trimmedNumberText)\n    return '';\n  return trimmedNumberText + (unit ?? '');\n}\n\nexport function getNumericStyleInputUnitLabel(unit: string): string {\n  return unit === '' ? ' ' : unit;\n}\n\nexport function normalizeNumericStyleInputOptionValues(values?: string[]): string[] {\n  const normalizedValues = (values ?? [])\n    .map(x => x == null ? null : x.trim())\n    .filter((x): x is string => x != null);\n  return [...new Set(normalizedValues)];\n}\n\nexport function resolveNumericStyleInputSelectedUnit(parsedUnit: string | undefined, lastNumericUnit: string | undefined, units: string[]): string | null {\n  if (parsedUnit != null && units.includes(parsedUnit))\n    return parsedUnit;\n  return lastNumericUnit ?? units[0] ?? null;\n}\n\nexport function resolveNumericStyleInputStep(unitSteps: Record<string, number> | undefined, defaultStep: number, unit?: string): number {\n  if (unit != null) {\n    const unitStep = unitSteps?.[unit];\n    if (Number.isFinite(unitStep) && unitStep > 0)\n      return unitStep;\n  }\n  return defaultStep;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/PlainScrollbar.ts",
    "content": "//included from: https://github.com/chdh/plain-scrollbar\r\n\r\nimport { css, html } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nclass Widget {\r\n\r\n  private host: PlainScrollbar;\r\n  private root: HTMLElement;\r\n  private trough: HTMLElement;\r\n  private button1: HTMLElement;                  // up/left button\r\n  private button2: HTMLElement;                  // down/right button\r\n  private thumb: HTMLElement;\r\n  private isConnected: boolean = false;\r\n\r\n  public thumbSize: number = 0.3;                 // relative thumb size (0..1)\r\n  public value: number = 0;                   // current scrollbar position (0..1)\r\n  public orientation: boolean = false;              // false=horizontal, true=vertical\r\n  private clickRepeatDelay: number = 300;                 // click repetition delay time in ms\r\n  private clickRepeatInterval: number = 100;                 // click repetition interval time in ms\r\n  private defaultThumbMinSize: number = 25;                  // default for minimum thumb size in pixels\r\n\r\n  private dragStartPos: number;                       // dragging start pointer position (clientX/Y)\r\n  private dragStartValue: number;                       // dragging start scrollbar position\r\n  private eventTimeoutId: NodeJS.Timeout | undefined;\r\n\r\n  private pointerCaptureId: number | undefined;           // `undefined` = no capture active\r\n  private pointerCaptureElement: HTMLElement;\r\n\r\n  // User interaction state:\r\n  private thumbDragging: boolean;                      // true while user is dragging the thumb\r\n  private button1Active: boolean;                      // true while user has pointer clicked down on button 1\r\n  private button2Active: boolean;                      // true while user has pointer clicked down on button 2\r\n  private troughActive: boolean;                      // true while user has pointer clicked down on trough\r\n\r\n  public constructor(host: PlainScrollbar) {\r\n    this.host = host;\r\n    host.attachShadow({ mode: \"open\" });\r\n    const shadowRoot = host.shadowRoot!;\r\n    shadowRoot.appendChild(scrollbarHtmlTemplate.content.cloneNode(true));\r\n    shadowRoot.adoptedStyleSheets = [scrollbarStyle];\r\n    this.root = <HTMLElement>shadowRoot.querySelector(\"#root\")!;\r\n    this.trough = <HTMLElement>shadowRoot.querySelector(\"#trough\")!;\r\n    this.button1 = <HTMLElement>shadowRoot.querySelector(\"#button1\")!;\r\n    this.button2 = <HTMLElement>shadowRoot.querySelector(\"#button2\")!;\r\n    this.thumb = <HTMLElement>shadowRoot.querySelector(\"#thumb\")!;\r\n    this.trough.addEventListener(\"pointerdown\", this.onTroughPointerDown);\r\n    this.trough.addEventListener(\"pointerup\", this.onPointerUp);\r\n    this.trough.addEventListener(\"pointercancel\", this.onPointerUp);\r\n    this.button1.addEventListener(\"pointerdown\", (event: PointerEvent) => this.onButtonPointerDown(event, 1));\r\n    this.button1.addEventListener(\"pointerup\", this.onPointerUp);\r\n    this.button1.addEventListener(\"pointercancel\", this.onPointerUp);\r\n    this.button1.addEventListener(\"contextmenu\", (e: Event) => e.preventDefault()); // to prevent popup on long touch\r\n    this.button2.addEventListener(\"pointerdown\", (event: PointerEvent) => this.onButtonPointerDown(event, 2));\r\n    this.button2.addEventListener(\"pointerup\", this.onPointerUp);\r\n    this.button2.addEventListener(\"pointercancel\", this.onPointerUp);\r\n    this.button2.addEventListener(\"contextmenu\", (e: Event) => e.preventDefault()); // to prevent popup on long touch\r\n    this.thumb.addEventListener(\"pointerdown\", this.onThumbPointerDown);\r\n    this.thumb.addEventListener(\"pointerup\", this.onPointerUp);\r\n    this.thumb.addEventListener(\"pointercancel\", this.onPointerUp);\r\n    this.thumb.addEventListener(\"pointermove\", this.onThumbPointerMove);\r\n    this.resetInteractionState();\r\n  }\r\n\r\n\r\n  private resetInteractionState() {\r\n    this.thumbDragging = false;\r\n    this.button1Active = false;\r\n    this.button2Active = false;\r\n    this.troughActive = false;\r\n  }\r\n\r\n  public connectedCallback() {\r\n    this.isConnected = true;\r\n    this.resetInteractionState();\r\n    this.updateLayout();\r\n    this.updateStyle();\r\n  }\r\n\r\n  public disconnectedCallback() {\r\n    this.isConnected = false;\r\n    this.resetInteractionState();\r\n    this.stopEventRepetition();\r\n    this.stopPointerCapture();\r\n  }\r\n\r\n  public updateLayout() {\r\n    if (!this.isConnected) {\r\n      return;\r\n    }\r\n    this.root.classList.toggle(\"horizontal\", !this.orientation);\r\n    this.root.classList.toggle(\"vertical\", this.orientation);\r\n    this.thumb.style.display = (this.thumbSize == 0) ? \"none\" : \"\";\r\n    this.thumb.style.height = this.orientation ? percent(this.getEffectiveThumbSize()) : \"\";\r\n    this.thumb.style.width = this.orientation ? \"\" : percent(this.getEffectiveThumbSize());\r\n    this.thumb.style.top = \"\";\r\n    this.thumb.style.left = \"\";\r\n    this.updateThumbPosition();\r\n  }\r\n\r\n  private updateStyle() {\r\n    if (!this.isConnected) {\r\n      return;\r\n    }\r\n    this.thumb.classList.toggle(\"active\", this.thumbDragging);\r\n    this.button1.classList.toggle(\"active\", this.button1Active);\r\n    this.button2.classList.toggle(\"active\", this.button2Active);\r\n    void this.troughActive;\r\n  }                                      // tslint:disable-line\r\n\r\n  public updateThumbPosition() {\r\n    const v = (1 - this.getEffectiveThumbSize()) * this.value;\r\n    if (this.orientation) {\r\n      this.thumb.style.top = percent(v);\r\n    }\r\n    else {\r\n      this.thumb.style.left = percent(v);\r\n    }\r\n  }\r\n\r\n  private getThroughSize(): number {\r\n    return this.orientation ? this.trough.clientHeight : this.trough.clientWidth;\r\n  }\r\n\r\n  private computeThumbMoveValue(distancePixels: number): number {\r\n    const troughSlidePixels = this.getThroughSize() * (1 - this.getEffectiveThumbSize());\r\n    if (troughSlidePixels < EPS) {\r\n      return 0;\r\n    }\r\n    return distancePixels / troughSlidePixels;\r\n  }\r\n\r\n  public setThumbSize(newThumbSize: number) {\r\n    const clippedNewThumbSize = Math.max(0, Math.min(1, newThumbSize));\r\n    if (clippedNewThumbSize == this.thumbSize) {\r\n      return;\r\n    }\r\n    this.thumbSize = clippedNewThumbSize;\r\n    this.updateLayout();\r\n  }\r\n\r\n  private getThumbMinSize(): number {\r\n    const s = this.getCssVar(\"--plain-scrollbar-thumb-min-size\");\r\n    if (!s) {\r\n      return this.defaultThumbMinSize;\r\n    }\r\n    const px = decodePxValue(s);\r\n    if (!px) {\r\n      return this.defaultThumbMinSize;\r\n    }\r\n    return px;\r\n  }\r\n\r\n  private getEffectiveThumbSize(): number {\r\n    const thumbMinSize = this.getThumbMinSize();\r\n    const throughSize = this.getThroughSize();\r\n    if (!throughSize) {\r\n      return this.thumbSize;\r\n    }\r\n    const min = Math.min(1, thumbMinSize / throughSize);\r\n    return Math.max(min, this.thumbSize);\r\n  }\r\n\r\n  public setValue(newValue: number): boolean {\r\n    const clippedNewValue = Math.max(0, Math.min(1, newValue));\r\n    if (clippedNewValue == this.value) {\r\n      return false;\r\n    }\r\n    this.value = isNaN(clippedNewValue) ? 0 : clippedNewValue;\r\n    this.updateThumbPosition();\r\n    return true;\r\n  }\r\n\r\n  public setOrientation(newOrientation: boolean): boolean {\r\n    if (newOrientation == this.orientation) {\r\n      return false;\r\n    }\r\n    this.orientation = newOrientation;\r\n    this.updateLayout();\r\n    return true;\r\n  }\r\n\r\n  private getCssVar(varName: string): string | undefined {\r\n    const s = getComputedStyle(this.root).getPropertyValue(varName);\r\n    if (!s) {\r\n      return null;\r\n    }\r\n    return s.trim();\r\n  }\r\n\r\n  //--- Outgoing events -------------------------------------------------------\r\n\r\n  private fireEvent(eventSubType: string) {\r\n    const event = new CustomEvent(\"scrollbar-input\", { detail: eventSubType });\r\n    this.host.dispatchEvent(event);\r\n  }\r\n\r\n  private fireEventRepeatedly(eventSubType: string, repeatDelay: number, repeatInterval: number, repeatCounter = 0) {\r\n    this.stopEventRepetition();\r\n    this.fireEvent(eventSubType);\r\n    const delay = (repeatCounter == 0) ? repeatDelay : repeatInterval;\r\n    const f = () => this.fireEventRepeatedly(eventSubType, repeatDelay, repeatInterval, repeatCounter + 1);\r\n    this.eventTimeoutId = setTimeout(f, delay);\r\n  }\r\n\r\n  private stopEventRepetition() {\r\n    if (this.eventTimeoutId) {\r\n      clearTimeout(this.eventTimeoutId);\r\n      this.eventTimeoutId = undefined;\r\n    }\r\n  }\r\n\r\n  //--- Pointer input ----------------------------------------------------------\r\n\r\n  private startPointerCapture(element: HTMLElement, pointerId: number) {\r\n    this.stopPointerCapture();\r\n    element.setPointerCapture(pointerId);\r\n    this.pointerCaptureElement = element;\r\n    this.pointerCaptureId = pointerId;\r\n  }\r\n\r\n  private stopPointerCapture() {\r\n    if (!this.pointerCaptureId) {\r\n      return;\r\n    }\r\n    this.pointerCaptureElement.releasePointerCapture(this.pointerCaptureId);\r\n    this.pointerCaptureId = undefined;\r\n  }\r\n\r\n  private onTroughPointerDown = (event: PointerEvent) => {\r\n    if (!this.isConnected || this.pointerCaptureId) {\r\n      return;\r\n    }\r\n    if (!event.isPrimary || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.button != 0) {\r\n      return;\r\n    }\r\n    const r = this.trough.getBoundingClientRect();\r\n    const pos = this.orientation ? event.clientY - r.top : event.clientX - r.left;\r\n    const threshold = (this.orientation ? r.height : r.width) * (1 - this.getEffectiveThumbSize()) * this.value;\r\n    const direction = pos > threshold;\r\n    const eventSubType = direction ? \"incrementLarge\" : \"decrementLarge\";\r\n    this.troughActive = true;\r\n    event.preventDefault();\r\n    this.startPointerCapture(this.trough, event.pointerId);\r\n    this.fireEventRepeatedly(eventSubType, this.clickRepeatDelay, this.clickRepeatInterval);\r\n  };\r\n\r\n  private onButtonPointerDown = (event: PointerEvent, buttonNo: number) => {\r\n    if (!this.isConnected || this.pointerCaptureId) {\r\n      return;\r\n    }\r\n    if (!event.isPrimary || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.button != 0) {\r\n      return;\r\n    }\r\n    switch (buttonNo) {\r\n      case 1: this.button1Active = true; break;\r\n      case 2: this.button2Active = true; break;\r\n    }\r\n    const eventSubType = (buttonNo == 1) ? \"decrementSmall\" : \"incrementSmall\";\r\n    this.updateStyle();\r\n    event.preventDefault();\r\n    const buttonElement = (buttonNo == 1) ? this.button1 : this.button2;\r\n    this.startPointerCapture(buttonElement, event.pointerId);\r\n    this.fireEventRepeatedly(eventSubType, this.clickRepeatDelay, this.clickRepeatInterval);\r\n  };\r\n\r\n  private onThumbPointerDown = (event: PointerEvent) => {\r\n    if (!this.isConnected || this.pointerCaptureId) {\r\n      return;\r\n    }\r\n    if (!event.isPrimary || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.button != 0) {\r\n      return;\r\n    }\r\n    this.dragStartPos = this.orientation ? event.clientY : event.clientX;\r\n    this.dragStartValue = this.value;\r\n    this.thumbDragging = true;\r\n    this.updateStyle();\r\n    event.preventDefault();\r\n    this.startPointerCapture(this.thumb, event.pointerId);\r\n  };\r\n\r\n  private onThumbPointerMove = (event: PointerEvent) => {\r\n    if (!this.isConnected) {\r\n      return;\r\n    }\r\n    if (!event.isPrimary || !this.thumbDragging) {\r\n      return;\r\n    }\r\n    const pos = this.orientation ? event.clientY : event.clientX;\r\n    const deltaPixels = pos - this.dragStartPos;\r\n    const deltaValue = this.computeThumbMoveValue(deltaPixels);\r\n    const newValue = this.dragStartValue + deltaValue;\r\n    event.preventDefault();\r\n    if (this.setValue(newValue)) {\r\n      this.fireEvent(\"value\");\r\n    }\r\n  };\r\n\r\n  private onPointerUp = (event: PointerEvent) => {\r\n    if (!this.isConnected) {\r\n      return;\r\n    }\r\n    if (!event.isPrimary) {\r\n      return;\r\n    }\r\n    this.resetInteractionState();\r\n    this.updateStyle();\r\n    this.stopEventRepetition();\r\n    this.stopPointerCapture();\r\n    event.preventDefault();\r\n  };\r\n\r\n} // end class\r\n\r\n//--- Custom Element -----------------------------------------------------------\r\n\r\nexport class PlainScrollbar extends HTMLElement {\r\n\r\n  private widget: Widget;\r\n\r\n  public constructor() {\r\n    super();\r\n\r\n    this.widget = new Widget(this);\r\n\r\n    const value = parseFloat(this.getAttribute(\"value\"));\r\n    if (!isNaN(value))\r\n      this.widget.value = value\r\n\r\n    if (this.hasOwnProperty('value')) {\r\n      let value = this.value;\r\n      delete this.value;\r\n      if (!isNaN(value))\r\n        this.widget.value = value;\r\n    }\r\n  }\r\n\r\n    /* @Override */ public connectedCallback() {\r\n    this.widget.connectedCallback();\r\n  }\r\n\r\n    /* @Override */ public disconnectedCallback() {\r\n    this.widget.disconnectedCallback();\r\n  }\r\n\r\n  //--- Element properties ----------------------------------------------------\r\n\r\n  // Size of the thumb, relative to the trough.\r\n  // A value between 0 and 1.\r\n  // 0 is used to hide the thumb. Small values greater than 0 are overridden by `plain-scrollbar-thumb-min-size`.\r\n  public get thumbSize(): number {\r\n    return this.widget.thumbSize;\r\n  }\r\n  public set thumbSize(v: number) {\r\n    this.widget.setThumbSize(v);\r\n  }\r\n\r\n  // The current position of the scrollbar.\r\n  // A value between 0 and 1.\r\n  public get value(): number {\r\n    return this.widget.value;\r\n  }\r\n  public set value(v: number) {\r\n    this.widget.setValue(v);\r\n  }\r\n\r\n  // Orientation of the scrollbar.\r\n  // \"horizontal\" or \"vertical\".\r\n  public get orientation(): string {\r\n    return formatOrientation(this.widget.orientation);\r\n  }\r\n  public set orientation(s: string) {\r\n    if (this.widget.setOrientation(decodeOrientation(s))) {\r\n      this.setAttribute(\"orientation\", this.orientation);\r\n    }\r\n  }\r\n\r\n  // Returns false=horizontal, true=vertical.\r\n  public get orientationBoolean(): boolean {\r\n    return this.widget.orientation;\r\n  }\r\n\r\n    //--- Element attributes ----------------------------------------------------\r\n\r\n    /* @Override */ public static get observedAttributes() {\r\n    return [\"orientation\"];\r\n  }\r\n\r\n    /* @Override */ public attributeChangedCallback(attrName: string, _oldValue: string | null, newValue: string | null) {\r\n    switch (attrName) {\r\n      case \"orientation\": {\r\n        if (newValue) {\r\n          this.widget.setOrientation(decodeOrientation(newValue));\r\n        }\r\n        break;\r\n      }\r\n    }\r\n  }\r\n\r\n} // end class\r\n\r\n//------------------------------------------------------------------------------\r\n\r\nconst EPS = 1E-9;\r\nconst buttonSize = \"var(--plain-scrollbar-button-size, 13px)\";\r\nconst buttonPath = '<path d=\"M -60 30 h 120 L 0 -30 z\" stroke-width=\"0\"/>';\r\n\r\nconst scrollbarStyle = css`\r\n    :host {\r\n       display: block;\r\n       contain: content;\r\n       background-color: #f8f8f8;\r\n       border-style: solid;\r\n       border-width: 1px;\r\n       border-color: #dddddd;\r\n    }\r\n    #root {\r\n       touch-action: none;\r\n       user-select: none;\r\n       box-sizing: border-box;\r\n       position: relative;\r\n       width: 100%;\r\n       height: 100%;\r\n    }\r\n    #trough {\r\n       position: absolute;\r\n    }\r\n    #root.vertical #trough {\r\n       width: 100%;\r\n       top: ${buttonSize};\r\n       bottom: ${buttonSize};\r\n    }\r\n    #root.horizontal #trough {\r\n       height: 100%;\r\n       left: ${buttonSize};\r\n       right: ${buttonSize};\r\n    }\r\n    #thumb {\r\n       box-sizing: border-box;\r\n       position: absolute;\r\n       width: 100%;\r\n       height: 100%;\r\n       background-color: var(--plain-scrollbar-thumb-background-color, #f0f0f0);\r\n       border-style: solid;\r\n       border-width: var(--plain-scrollbar-thumb-border-width, 1px);\r\n       border-color: var(--plain-scrollbar-thumb-border-color, #b8b8b8);\r\n       border-radius: var(--plain-scrollbar-thumb-border-radius, 4px);\r\n       transition: background-color 50ms linear;\r\n    }\r\n    #thumb:hover {\r\n       background-color: var(--plain-scrollbar-thumb-background-color-hover, #e0e0e0);\r\n    }\r\n    #thumb.active {\r\n       background-color: var(--plain-scrollbar-thumb-background-color-active, #c0c0c0);\r\n    }\r\n    #button1,\r\n    #button2 {\r\n       box-sizing: border-box;\r\n       position: absolute;\r\n       display: block;\r\n       fill: var(--plain-scrollbar-button-color, #606060);\r\n    }\r\n    #root.vertical #button1 {\r\n       top: 0;\r\n       width: 100%;\r\n       height: ${buttonSize};\r\n    }\r\n    #root.vertical #button2 {\r\n       bottom: 0;\r\n       width: 100%;\r\n       height: ${buttonSize};\r\n    }\r\n    #root.horizontal #button1 {\r\n       left: 0;\r\n       height: 100%;\r\n       width: ${buttonSize};\r\n    }\r\n    #root.horizontal #button2 {\r\n       right: 0;\r\n       height: 100%;\r\n       width: ${buttonSize};\r\n    }\r\n    #upArrow,\r\n    #downArrow,\r\n    #leftArrow,\r\n    #rightArrow {\r\n       display: none;\r\n       width: 100%;\r\n       height: 100%;\r\n    }\r\n    #root.vertical #upArrow,\r\n    #root.vertical #downArrow {\r\n       display: block;\r\n    }\r\n    #root.horizontal #leftArrow,\r\n    #root.horizontal #rightArrow {\r\n       display: block;\r\n    }\r\n    #button1:hover,\r\n    #button2:hover {\r\n       background-color: var(--plain-scrollbar-button-color-hover, #e0e0e0);\r\n    }\r\n    #button1.active,\r\n    #button2.active {\r\n       background-color: var(--plain-scrollbar-button-color-active, #c0c0c0);\r\n    }\r\n    `;\r\n\r\nconst scrollbarHtmlTemplate = html`\r\n    <div id=\"root\" part=\"root\">\r\n     <div id=\"button1\" part=\"button button1\">\r\n      <svg id=\"upArrow\" part=\"arrow upArrow\" viewBox=\"-100 -100 200 200\">${buttonPath}</svg>\r\n      <svg id=\"leftArrow\" part=\"arrow leftArrow\" viewBox=\"-100 -100 200 200\"><g transform=\"rotate(-90)\">${buttonPath}</g></svg>\r\n     </div>\r\n     <div id=\"trough\" part=\"trough\">\r\n      <div id=\"thumb\" part=\"thumb\"></div>\r\n     </div>\r\n     <div id=\"button2\" part=\"button button2\">\r\n      <svg id=\"downArrow\" part=\"arrow downArrow\" viewBox=\"-100 -100 200 200\"><g transform=\"rotate(180)\">${buttonPath}</g></svg>\r\n      <svg id=\"rightArrow\" part=\"arrow rightArrow\" viewBox=\"-100 -100 200 200\"><g transform=\"rotate(90)\">${buttonPath}</g></svg>\r\n     </div>\r\n    </div>\r\n    `;\r\n\r\n//------------------------------------------------------------------------------\r\n\r\nfunction formatOrientation(b: boolean): string {\r\n  return b ? \"vertical\" : \"horizontal\";\r\n}\r\n\r\nfunction decodeOrientation(s: string): boolean {\r\n  switch (s) {\r\n    case \"vertical\": return true;\r\n    case \"horizontal\": return false;\r\n    default: throw new Error(\"Invalid orientation value \\\"\" + s + \"\\\".\");\r\n  }\r\n}\r\n\r\nfunction percent(v: number): string {\r\n  return (v * 100).toFixed(3) + \"%\";\r\n}\r\n\r\nfunction decodePxValue(s: string): number | undefined {\r\n  if (!s || !s.endsWith(\"px\")) {\r\n    return undefined;\r\n  }\r\n  return Number(s.substring(0, s.length - 2));\r\n}\r\n\r\ncustomElements.define(\"node-projects-plain-scrollbar\", PlainScrollbar);"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/SimpleSplitView.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class SimpleSplitView extends BaseCustomWebComponentConstructorAppend {\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n    }  \r\n    #split {\r\n      position: relative;\r\n      height: 100%;\r\n      width: 100%;\r\n      grid-template-rows: calc(var(--split) * 1%) 5px calc(((100 - var(--split)) * 1%) - 5px);\r\n      grid-template-columns: 100%;\r\n      display: grid;\r\n      align-items: center;\r\n    }\r\n    :host([orientation=\"horizontal\"]) #split {\r\n      grid-template-rows: 100%;\r\n      grid-template-columns: calc(var(--split) * 1%) 5px calc(((100 - var(--split)) * 1%) - 5px);\r\n    }\r\n    #splitter {\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n    }\r\n    :host([orientation=\"horizontal\"]) > div > #splitter {\r\n      cursor: ew-resize;\r\n      width: 5px;\r\n      height: 100%;\r\n    }\r\n    :host([orientation=\"vertical\"]) > div > #splitter {\r\n      cursor: ns-resize;\r\n      height: 5px;\r\n      width: 100%;\r\n    }`;\r\n\r\n  static override readonly template = html`\r\n    <div id=\"split\" style=\"--split: 50;\">\r\n      <slot name=\"top\"></slot>  \r\n      <div id=\"splitter\"></div>\r\n      <slot name=\"bottom\"></slot>\r\n    </div>`;\r\n\r\n  public static properties = {\r\n    orientation: String\r\n  }\r\n\r\n  private _orientation: 'vertical' | 'horizontal' = 'vertical';\r\n  public get orientation() {\r\n    return this._orientation;\r\n  }\r\n  public set orientation(value) {\r\n    this._orientation = value;\r\n    this.setAttribute('orientation', value);\r\n  }\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n  }\r\n\r\n  ready() {\r\n    this._parseAttributesToProperties();\r\n    this.setAttribute('orientation', this.orientation);\r\n\r\n    const split = this._getDomElement<HTMLDivElement>(\"split\");\r\n    const splitter = this._getDomElement<HTMLDivElement>(\"splitter\");\r\n\r\n    let start: boolean = null;\r\n    splitter.addEventListener('pointerdown', (e) => {\r\n      splitter.setPointerCapture(e.pointerId);\r\n      start = true;\r\n    });\r\n    splitter.addEventListener('pointerup', (e) => {\r\n      splitter.releasePointerCapture(e.pointerId);\r\n      start = null;\r\n    });\r\n    splitter.addEventListener('pointermove', (e) => {\r\n      if (start !== null) {\r\n        let splitValue = parseFloat(split.style.getPropertyValue('--split'));\r\n        if (this.orientation === 'horizontal')\r\n          splitValue += e.movementX * 100 / split.clientWidth;\r\n        else\r\n          splitValue += e.movementY * 100 / split.clientHeight;\r\n        if (!isNaN(splitValue))\r\n          split.style.setProperty(\"--split\", <any>splitValue);\r\n      }\r\n    });\r\n  }\r\n}\r\ncustomElements.define('node-projects-simple-split-view', SimpleSplitView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/controls/ThicknessEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport type ThicknessEditorValueChangedEventArgs = { newValue?: string, oldValue?: string };\r\n\r\nexport class ThicknessEditor extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  public static override readonly style = css`\r\n  :host {\r\n    margin: 4px;\r\n    margin-left: auto;\r\n    margin-right: auto;\r\n  }\r\n  #container {\r\n    display: grid;\r\n    grid-template-columns: minmax(30px, 40px) minmax(30px, 60px) minmax(30px, 40px);\r\n    grid-template-rows: auto;\r\n    grid-template-areas: \r\n          \"  .   top     .\"\r\n          \"left middle right\"\r\n          \"  .  bottom   .\";\r\n    column-gap: 2px;\r\n    row-gap: 2px;\r\n  }\r\n  input {\r\n    width: 20px;\r\n    text-align: center;\r\n    font-size: 10px;\r\n    height: 20px;\r\n    padding: 0;\r\n  }\r\n  #left {\r\n    grid-area: left;\r\n    justify-self: end;\r\n  }\r\n  #top {\r\n    grid-area: top;\r\n    align-self: end;\r\n    justify-self: center;\r\n  }\r\n  #right {\r\n    grid-area: right;\r\n    justify-self: start;\r\n  }\r\n  #bottom {\r\n    grid-area: bottom;\r\n    align-self: start;\r\n    justify-self: center;\r\n  }\r\n  #rect {\r\n    grid-area: middle;\r\n    border: 1px solid black;\r\n    background: lightgray;\r\n  }\r\n  `;\r\n\r\n  public static override readonly template = html`\r\n    <div id=\"container\">\r\n      <input id=\"left\">\r\n      <input id=\"top\">\r\n      <input id=\"right\">\r\n      <input id=\"bottom\">\r\n      <div id=\"rect\"></div>\r\n    </div>\r\n  `;\r\n\r\n  private _leftInput: HTMLInputElement;\r\n  private _topInput: HTMLInputElement;\r\n  private _rightInput: HTMLInputElement;\r\n  private _bottomInput: HTMLInputElement;\r\n\r\n  private _valueLeft: string;\r\n  public get valueLeft() {\r\n    return this._valueLeft;\r\n  }\r\n  public set valueLeft(value) {\r\n    const oldValue = this._valueLeft;\r\n    this._valueLeft = value;\r\n    if (oldValue !== value) {\r\n      this._updateValue();\r\n      this.valueLeftChanged.emit({ newValue: value, oldValue: oldValue });\r\n    }\r\n  }\r\n  public valueLeftChanged = new TypedEvent<ThicknessEditorValueChangedEventArgs>();\r\n\r\n  private _valueTop: string;\r\n  public get valueTop() {\r\n    return this._valueTop;\r\n  }\r\n  public set valueTop(value) {\r\n    const oldValue = this._valueTop;\r\n    this._valueTop = value;\r\n    if (oldValue !== value) {\r\n      this._updateValue();\r\n      this.valueTopChanged.emit({ newValue: value, oldValue: oldValue });\r\n    }\r\n  }\r\n  public valueTopChanged = new TypedEvent<ThicknessEditorValueChangedEventArgs>();\r\n\r\n  private _valueRight: string;\r\n  public get valueRight() {\r\n    return this._valueRight;\r\n  }\r\n  public set valueRight(value) {\r\n    const oldValue = this._valueRight;\r\n    this._valueRight = value;\r\n    if (oldValue !== value) {\r\n      this._updateValue();\r\n      this.valueRightChanged.emit({ newValue: value, oldValue: oldValue });\r\n    }\r\n  }\r\n  public valueRightChanged = new TypedEvent<ThicknessEditorValueChangedEventArgs>();\r\n\r\n  private _valueBottom: string;\r\n  public get valueBottom() {\r\n    return this._valueBottom;\r\n  }\r\n  public set valueBottom(value) {\r\n    const oldValue = this._valueBottom;\r\n    this._valueBottom = value;\r\n    if (oldValue !== value) {\r\n      this._updateValue();\r\n      this.valueBottomChanged.emit({ newValue: value, oldValue: oldValue });\r\n    }\r\n  }\r\n  public valueBottomChanged = new TypedEvent<ThicknessEditorValueChangedEventArgs>();\r\n\r\n  public property: string;\r\n  public unsetValue: string;\r\n\r\n  _updateValue() {\r\n    this._leftInput.value = this.valueLeft;\r\n    this._topInput.value = this.valueTop;\r\n    this._rightInput.value = this.valueRight;\r\n    this._bottomInput.value = this._valueBottom;\r\n  }\r\n\r\n  ready() {\r\n    this._parseAttributesToProperties();\r\n\r\n    this._leftInput = this._getDomElement<HTMLInputElement>('left');\r\n    this._topInput = this._getDomElement<HTMLInputElement>('top');\r\n    this._rightInput = this._getDomElement<HTMLInputElement>('right');\r\n    this._bottomInput = this._getDomElement<HTMLInputElement>('bottom');\r\n\r\n    this._leftInput.onkeyup = (e) => { if (e.key === 'Enter') this._valueLeft = this._leftInput.value };\r\n    this._topInput.onkeyup = (e) => { if (e.key === 'Enter') this._valueTop = this._topInput.value };\r\n    this._rightInput.onkeyup = (e) => { if (e.key === 'Enter') this._valueRight = this._rightInput.value };\r\n    this._bottomInput.onkeyup = (e) => { if (e.key === 'Enter') this._valueBottom = this._bottomInput.value };\r\n\r\n    this._leftInput.onblur = (e) => this._valueLeft = this._leftInput.value;\r\n    this._topInput.onblur = (e) => this._valueTop = this._topInput.value;\r\n    this._rightInput.onblur = (e) => this._valueRight = this._rightInput.value;\r\n    this._bottomInput.onblur = (e) => this._valueBottom = this._bottomInput.value;\r\n\r\n    this._updateValue();\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-thickness-editor', ThicknessEditor);"
  },
  {
    "path": "packages/web-component-designer/src/elements/documentContainer.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, cssFromString, debounce, TypedEvent } from \"@node-projects/base-custom-webcomponent\"\r\nimport { DesignerTabControl } from './controls/DesignerTabControl.js';\r\nimport { DesignerView } from './widgets/designerView/designerView.js';\r\nimport { ServiceContainer } from './services/ServiceContainer.js';\r\nimport { InstanceServiceContainer } from './services/InstanceServiceContainer.js';\r\nimport { ICodeView } from './widgets/codeView/ICodeView.js';\r\nimport { IStringPosition } from './services/htmlWriterService/IStringPosition.js';\r\nimport { IDemoView } from './widgets/demoView/IDemoView.js';\r\nimport { IUiCommandHandler } from '../commandHandling/IUiCommandHandler.js';\r\nimport { IUiCommand } from '../commandHandling/IUiCommand.js';\r\nimport { IDisposable } from '../interfaces/IDisposable.js';\r\nimport { ISelectionChangedEvent } from \"./services/selectionService/ISelectionChangedEvent.js\";\r\nimport { ISelectionRefreshEvent } from './services/selectionService/ISelectionRefreshEvent.js';\r\nimport { SimpleSplitView } from './controls/SimpleSplitView.js';\r\nimport { IStylesheet } from \"./services/stylesheetService/IStylesheetService.js\";\r\nimport { sleep } from \"./helper/Helper.js\";\r\nimport { ExtensionType } from \"./widgets/designerView/extensions/ExtensionType.js\";\r\n\r\nenum tabIndex {\r\n  designer = 0,\r\n  code = 1,\r\n  split = 2,\r\n  preview = 3\r\n}\r\n\r\nexport class DocumentContainer extends BaseCustomWebComponentLazyAppend implements IUiCommandHandler, IDisposable {\r\n  public designerView: DesignerView;\r\n  public codeView: ICodeView & HTMLElement;\r\n  public demoView: IDemoView & HTMLElement;\r\n\r\n  public additionalData: any;\r\n\r\n  private _firstLoad = true;\r\n  private _stylesheetChangedEventRegistered: boolean;\r\n\r\n  private _additionalStyle: string;\r\n  public set additionalStyleString(style: string) {\r\n    this._additionalStyle = style;\r\n    this.designerView.additionalStyles = [cssFromString(style)];\r\n  };\r\n  public get additionalStyleString() {\r\n    return this._additionalStyle;\r\n  };\r\n\r\n  private _additionalStyles: CSSStyleSheet[];\r\n  public set additionalStyles(value: CSSStyleSheet[]) {\r\n    this._additionalStyles = value;\r\n    this.designerView.additionalStyles = this._additionalStyles;\r\n  };\r\n  public get additionalStyles() {\r\n    return this._additionalStyles;\r\n  };\r\n\r\n  private _additionalStylesheets: IStylesheet[];\r\n  public set additionalStylesheets(stylesheets: IStylesheet[]) {\r\n    this._additionalStylesheets = stylesheets;\r\n    if (this.designerView.instanceServiceContainer.stylesheetService) {\r\n      this.designerView.instanceServiceContainer.stylesheetService.setStylesheets(stylesheets);\r\n      if (!this._stylesheetChangedEventRegistered) {\r\n        this._stylesheetChangedEventRegistered = true;\r\n        this.designerView.instanceServiceContainer.stylesheetService.stylesheetChanged.on(e => this.additionalStylesheetChanged.emit({ name: e.name, newStyle: e.newStyle, oldStyle: e.oldStyle, changeSource: e.changeSource }));\r\n      }\r\n    }\r\n  };\r\n  public get additionalStylesheets() {\r\n    return this._additionalStylesheets;\r\n  };\r\n  public additionalStylesheetChanged = new TypedEvent<{ name: string, newStyle: string, oldStyle: string, changeSource: 'extern' | 'styleupdate' | 'undo' }>;\r\n\r\n  get readOnly() {\r\n    return this.designerView?.readOnly;\r\n  }\r\n  set readOnly(v) {\r\n    if (this.designerView)\r\n      this.designerView.readOnly = v;\r\n    if (this.codeView)\r\n      this.codeView.readOnly = v;\r\n  }\r\n\r\n  public onContentChanged = new TypedEvent<{ source: 'designer' | 'code' }>();\r\n  public onTabChanged = new TypedEvent<{ oldTab: 'designer' | 'code' | 'split' | 'preview', newTab: 'designer' | 'code' | 'split' | 'preview' }>();\r\n\r\n  private _contentChangeSource: 'designer' | 'code' = 'designer';\r\n  private _serviceContainer: ServiceContainer;\r\n  private _content: string = '';\r\n  private _tabControl: DesignerTabControl;\r\n  private _selectionPosition: IStringPosition;\r\n  private _lastCodeSelectionKey: string;\r\n  private _splitDiv: SimpleSplitView;\r\n  private _designerDiv: HTMLDivElement;\r\n  private _codeDiv: HTMLDivElement;\r\n  private refreshInSplitViewDebounced: (...args: any) => any;\r\n  private _disableChangeNotificationDesigner: boolean;\r\n  private _disableChangeNotificationEditor: boolean;\r\n\r\n  static override get style() {\r\n    return css`\r\n      div {\r\n        height: 100%;\r\n        display: flex;\r\n        flex-direction: column;\r\n      }                            \r\n      node-projects-designer-view {\r\n        height: 100%;\r\n        overflow: hidden;\r\n      }\r\n      `;\r\n  }\r\n\r\n  constructor(serviceContainer: ServiceContainer, content?: string, useIframe: boolean = false) {\r\n    super();\r\n\r\n    this.refreshInSplitViewDebounced = debounce(this.refreshInSplitView, 200)\r\n    this._serviceContainer = serviceContainer;\r\n    if (content != null)\r\n      this._content = content;\r\n\r\n    let div = document.createElement(\"div\");\r\n    this._tabControl = new DesignerTabControl();\r\n    div.appendChild(this._tabControl);\r\n    this.designerView = new DesignerView(useIframe);\r\n    this.designerView.setAttribute('exportparts', 'canvas');\r\n    this.designerView.slot = 'top';\r\n    this._designerDiv = document.createElement(\"div\");\r\n    this._tabControl.appendChild(this._designerDiv);\r\n    this._designerDiv.appendChild(this.designerView);\r\n    this._designerDiv.dataset.title = 'Designer';\r\n    this.designerView.initialize(this._serviceContainer);\r\n    this.designerView.instanceServiceContainer.documentContainer = this;\r\n    this.designerView.instanceServiceContainer.selectionService.onSelectionChanged.on(e => this.designerSelectionChanged(e))\r\n    this.designerView.instanceServiceContainer.selectionService.onSelectionRefresh.on(e => this.designerSelectionChanged(e))\r\n    this.designerView.instanceServiceContainer.onContentChanged.on(() => this.designerContentChanged())\r\n\r\n    this.codeView = new serviceContainer.config.codeViewWidget();\r\n    this.codeView.slot = 'bottom';\r\n    this.codeView.style.position = 'relative';\r\n    this._codeDiv = document.createElement(\"div\");\r\n    this._tabControl.appendChild(this._codeDiv);\r\n    this._codeDiv.style.position = 'relative';\r\n    this._codeDiv.appendChild(this.codeView);\r\n    this._codeDiv.dataset.title = 'Code';\r\n    this.codeView.onTextChanged.on(text => {\r\n      if (!this._disableChangeNotificationDesigner) {\r\n        if (this._tabControl.selectedIndex === tabIndex.code || this._tabControl.selectedIndex === tabIndex.split) {\r\n          this._disableChangeNotificationEditor = true;\r\n          this._content = text;\r\n          this.refreshInSplitViewDebounced();\r\n        }\r\n      }\r\n    })\r\n\r\n    this._splitDiv = new SimpleSplitView();\r\n    this._splitDiv.style.height = '100%';\r\n    this._splitDiv.dataset.title = 'Split';\r\n    this._tabControl.appendChild(this._splitDiv);\r\n    if (serviceContainer.config.demoViewWidget) {\r\n      this.demoView = new serviceContainer.config.demoViewWidget();\r\n      this.demoView.dataset.title = 'Preview';\r\n      this._tabControl.appendChild(this.demoView);\r\n    }\r\n    queueMicrotask(() => {\r\n      this.shadowRoot.appendChild(div);\r\n      this._tabControl.selectedIndex = tabIndex.designer;\r\n    });\r\n  }\r\n\r\n  async refreshInSplitView() {\r\n    try {\r\n      await this.updateDesignerHtml();\r\n    } catch (err) {\r\n      console.error(err);\r\n    }\r\n    this._disableChangeNotificationEditor = false;\r\n  }\r\n\r\n  get currentView(): 'designer' | 'split' | 'code' | 'preview' {\r\n    if (this._tabControl.selectedIndex == tabIndex.designer)\r\n      return 'designer'\r\n    if (this._tabControl.selectedIndex == tabIndex.split)\r\n      return 'split'\r\n    if (this._tabControl.selectedIndex == tabIndex.code)\r\n      return 'code'\r\n    if (this._tabControl.selectedIndex == tabIndex.preview)\r\n      return 'preview'\r\n    return null;\r\n  }\r\n  set currentView(view: 'designer' | 'split' | 'code' | 'preview') {\r\n    if (view == 'designer')\r\n      this._tabControl.selectedIndex = tabIndex.designer;\r\n    if (view == 'split')\r\n      this._tabControl.selectedIndex = tabIndex.split;\r\n    if (view == 'code')\r\n      this._tabControl.selectedIndex = tabIndex.code;\r\n    if (view == 'preview')\r\n      this._tabControl.selectedIndex = tabIndex.preview;\r\n  }\r\n\r\n  designerSelectionChanged(e: ISelectionChangedEvent | ISelectionRefreshEvent) {\r\n    if (this._tabControl.selectedIndex === tabIndex.split) {\r\n      let primarySelection = this.instanceServiceContainer.selectionService.primarySelection;\r\n      if (primarySelection) {\r\n        if (this.designerView.instanceServiceContainer.designItemDocumentPositionService) {\r\n          this._selectionPosition = this.instanceServiceContainer.selectionService.selectedPart?.textRange\r\n            ?? this.designerView.instanceServiceContainer.designItemDocumentPositionService.getPosition(primarySelection);\r\n          if (this._selectionPosition)\r\n            this.setCodeViewSelection(this._selectionPosition);\r\n          this._selectionPosition = null;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  designerContentChanged() {\r\n    //event wenn text geändert......\r\n    this.onContentChanged.emit({ source: this._contentChangeSource });\r\n\r\n    if (!this._disableChangeNotificationEditor) {\r\n      this._disableChangeNotificationDesigner = true;\r\n      if (this._tabControl.selectedIndex === tabIndex.code || this._tabControl.selectedIndex === tabIndex.split) {\r\n        let primarySelection = this.instanceServiceContainer.selectionService.primarySelection;\r\n        this._content = this.designerView.getDesignerHTML();\r\n        this.codeView.update(this._content, this.designerView.instanceServiceContainer);\r\n        this._lastCodeSelectionKey = null;\r\n        if (primarySelection) {\r\n          if (this.designerView.instanceServiceContainer.designItemDocumentPositionService) {\r\n            this._selectionPosition = this.instanceServiceContainer.selectionService.selectedPart?.textRange\r\n              ?? this.designerView.instanceServiceContainer.designItemDocumentPositionService.getPosition(primarySelection);\r\n            if (this._selectionPosition)\r\n              this.setCodeViewSelection(this._selectionPosition);\r\n            this._selectionPosition = null;\r\n          }\r\n        }\r\n      }\r\n      this._disableChangeNotificationDesigner = false;\r\n    }\r\n  }\r\n\r\n  dispose(): void {\r\n    if (this.designerView?.instanceServiceContainer?.collaborationService) {\r\n      this.designerView.instanceServiceContainer.collaborationService.disconnect();\r\n      this.designerView.instanceServiceContainer.collaborationService.detachTransport();\r\n    }\r\n    this.codeView.dispose();\r\n    this.demoView.dispose();\r\n  }\r\n\r\n  executeCommand(command: IUiCommand) {\r\n    if (this._tabControl.selectedIndex === tabIndex.designer || this._tabControl.selectedIndex === tabIndex.split)\r\n      this.designerView.executeCommand(command);\r\n    else if (this._tabControl.selectedIndex === tabIndex.code)\r\n      this.codeView.executeCommand(command);\r\n    else if (this._tabControl.selectedIndex === tabIndex.preview)\r\n      this.demoView.executeCommand(command);\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    if (this._tabControl.selectedIndex === tabIndex.designer || this._tabControl.selectedIndex === tabIndex.split) {\r\n      if (this.designerView?.canExecuteCommand)\r\n        return this.designerView.canExecuteCommand(command);\r\n    } else if (this._tabControl.selectedIndex === tabIndex.code) {\r\n      if (this.codeView?.canExecuteCommand)\r\n        return this.codeView.canExecuteCommand(command);\r\n    } else if (this._tabControl.selectedIndex === tabIndex.preview) {\r\n      if (this.demoView?.canExecuteCommand)\r\n        return this.demoView.canExecuteCommand(command);\r\n    }\r\n    return false;\r\n  }\r\n\r\n  async setContentAsync(value: string) {\r\n    this._content = value;\r\n\r\n    if (this._tabControl) {\r\n      if (this._tabControl.selectedIndex === tabIndex.designer)\r\n        await this.updateDesignerHtml();\r\n      else if (this._tabControl.selectedIndex === tabIndex.code)\r\n        this.codeView.update(this._content, this.designerView.instanceServiceContainer);\r\n      else if (this._tabControl.selectedIndex === tabIndex.split) {\r\n\r\n      }\r\n      else if (this._tabControl.selectedIndex === tabIndex.preview)\r\n        this.demoView.display(this._serviceContainer, this.designerView.instanceServiceContainer, this._content, this.additionalStyleString);\r\n    }\r\n  }\r\n\r\n  set content(value: string) {\r\n    this.setContentAsync(value);\r\n  }\r\n  get content() {\r\n    if (this._tabControl) {\r\n      if (this._tabControl.selectedIndex === tabIndex.designer)\r\n        this._content = this.designerView.getDesignerHTML();\r\n      else if (this._tabControl.selectedIndex === tabIndex.code)\r\n        this._content = this.codeView.getText();\r\n      return this._content;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  ready() {\r\n    this._tabControl.onSelectedTabChanged.on(i => {\r\n      if (i.oldIndex === tabIndex.designer) {\r\n        let primarySelection = this.instanceServiceContainer.selectionService.primarySelection;\r\n        this._content = this.designerView.getDesignerHTML();\r\n        if (this.designerView.instanceServiceContainer.designItemDocumentPositionService) {\r\n          this._selectionPosition = this.instanceServiceContainer.selectionService.selectedPart?.textRange\r\n            ?? this.designerView.instanceServiceContainer.designItemDocumentPositionService.getPosition(primarySelection);\r\n        }\r\n      } else if (i.oldIndex === tabIndex.code) {\r\n        this._content = this.codeView.getText();\r\n      } else if (i.oldIndex === tabIndex.split) {\r\n        this._designerDiv.appendChild(this.designerView);\r\n        this._codeDiv.appendChild(this.codeView);\r\n      } else if (i.oldIndex === tabIndex.preview) {\r\n        if (this.demoView?.stopDisplay)\r\n          this.demoView.stopDisplay();\r\n      }\r\n\r\n      if (i.newIndex === tabIndex.designer || i.newIndex === tabIndex.split)\r\n        this.updateDesignerHtml();\r\n      if (i.newIndex === tabIndex.code || i.newIndex === tabIndex.split) {\r\n        this.codeView.update(this._content, this.designerView.instanceServiceContainer);\r\n        this._lastCodeSelectionKey = null;\r\n        if (this._selectionPosition) {\r\n          this.setCodeViewSelection(this._selectionPosition);\r\n          sleep(20).then(x => {\r\n            if (this._selectionPosition)\r\n              this.setCodeViewSelection(this._selectionPosition);\r\n            this._selectionPosition = null;\r\n          });\r\n        }\r\n        if (i.changedViaClick) {\r\n          this.codeView.focusEditor();\r\n        }\r\n      }\r\n      if (i.newIndex === tabIndex.split) {\r\n        this._splitDiv.appendChild(this.designerView);\r\n        this._splitDiv.appendChild(this.codeView);\r\n      }\r\n      if (i.newIndex === tabIndex.preview) {\r\n        this.demoView.display(this._serviceContainer, this.designerView.instanceServiceContainer, this._content, this.additionalStyleString);\r\n      }\r\n\r\n      if (this._content) {\r\n        this._firstLoad = false;\r\n      }\r\n\r\n      this.onTabChanged.emit({ oldTab: <any>tabIndex[i.oldIndex], newTab: <any>tabIndex[i.newIndex] });\r\n    });\r\n    if (this._content) {\r\n      this.content = this._content;\r\n      this._firstLoad = false;\r\n    }\r\n  }\r\n\r\n  private async updateDesignerHtml() {\r\n    if (this._firstLoad)\r\n      return this.designerView.parseDesignerHTML(this._content, this._firstLoad);\r\n    else {\r\n      const html = this.designerView.getDesignerHTML();\r\n      if (html != this._content) {\r\n        this._contentChangeSource = 'code';\r\n        await this.designerView.parseDesignerHTML(this._content, this._firstLoad);\r\n        this._contentChangeSource = 'designer';\r\n        return;\r\n      } else {\r\n        this.instanceServiceContainer.undoService.clearTransactionstackIfNotEmpty();\r\n        this.designerView.designerCanvas.overlayLayer.removeAllOverlays();\r\n        this.designerView.designerCanvas.extensionManager.reapplyAllAppliedExtentions(null, [ExtensionType.Permanent, ExtensionType.Selection, ExtensionType.PrimarySelection, ExtensionType.PrimarySelectionContainer, ExtensionType.OnlyOneItemSelected, ExtensionType.MultipleItemsSelected]);\r\n      }\r\n    }\r\n  }\r\n\r\n  private setCodeViewSelection(position: IStringPosition) {\r\n    if (!position)\r\n      return;\r\n\r\n    const key = `${position.start}:${position.length}`;\r\n    if (this._lastCodeSelectionKey === key)\r\n      return;\r\n\r\n    this._lastCodeSelectionKey = key;\r\n    this.codeView.setSelection(position);\r\n  }\r\n\r\n  public get instanceServiceContainer(): InstanceServiceContainer {\r\n    return this.designerView.instanceServiceContainer;\r\n  }\r\n}\r\n\r\ncustomElements.define(\"node-projects-document-container\", DocumentContainer);\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/ArrangeHelper.ts",
    "content": "import { Orientation } from '../../enums/Orientation.js';\nimport { IDesignItem } from '../item/IDesignItem.js';\nimport { ChangeGroup } from '../services/undoService/ChangeGroup.js';\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\n\nexport abstract class ArrangeHelper {\n    public static arrangeElements(orientation: Orientation, designerCanvas: IDesignerCanvas, arrangeElements: IDesignItem[]) {\n        switch (orientation) {\n            case Orientation.TOP: {\n                const grp = this.formGroup(ArrangeDirection.TOP, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetY = primaryCoordinates.y;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetY != selectedCoordinates.y) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('bottom') && !elem.hasStyle('top')) {\n                            let bottom = parent.y + parent.height - targetY - selectedCoordinates.height;\n                            this.arrange(elem, 'bottom', bottom + \"px\");\n                        } else {\n                            let top = targetY - parent.y;\n                            this.arrange(elem, 'top', top + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n            case Orientation.BOTTOM: {\n                const grp = this.formGroup(ArrangeDirection.BOTTOM, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetBottom = primaryCoordinates.y + primaryCoordinates.height;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetBottom != selectedCoordinates.y + selectedCoordinates.height) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('bottom') && !elem.hasStyle('top')) {\n                            let bottom = parent.y + parent.height - targetBottom;\n                            this.arrange(elem, 'bottom', bottom + \"px\");\n                        } else {\n                            let top = targetBottom - selectedCoordinates.height - parent.y;\n                            this.arrange(elem, 'top', top + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n            case Orientation.LEFT: {\n                const grp = this.formGroup(ArrangeDirection.LEFT, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetX = primaryCoordinates.x;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetX != selectedCoordinates.x) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('right') && !elem.hasStyle('left')) {\n                            let right = parent.x + parent.width - targetX - selectedCoordinates.width;\n                            this.arrange(elem, 'right', right + \"px\");\n                        } else {\n                            let left = targetX - parent.x;\n                            this.arrange(elem, 'left', left + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n            case Orientation.RIGHT: {\n                const grp = this.formGroup(ArrangeDirection.RIGHT, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetRight = primaryCoordinates.x + primaryCoordinates.width;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetRight != selectedCoordinates.x + selectedCoordinates.width) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('right') && !elem.hasStyle('left')) {\n                            let right = parent.x + parent.width - targetRight;\n                            this.arrange(elem, 'right', right + \"px\");\n                        } else {\n                            let left = targetRight - selectedCoordinates.width - parent.x;\n                            this.arrange(elem, 'left', left + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n            case Orientation.VERTICAL_CENTER: {\n                const grp = this.formGroup(ArrangeDirection.VERTICAL_CENTER, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetCenterY = primaryCoordinates.y + primaryCoordinates.height / 2;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetCenterY != selectedCoordinates.y + selectedCoordinates.height / 2) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('bottom') && !elem.hasStyle('top')) {\n                            let bottom = parent.y + parent.height - targetCenterY - selectedCoordinates.height / 2;\n                            this.arrange(elem, 'bottom', bottom + \"px\");\n                        } else {\n                            let top = targetCenterY - selectedCoordinates.height / 2 - parent.y;\n                            this.arrange(elem, 'top', top + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n\n            case Orientation.HORIZONTAL_CENTER: {\n                const grp = this.formGroup(ArrangeDirection.HORIZONTAL_CENTER, designerCanvas);\n                const primaryCoordinates = designerCanvas.getNormalizedElementCoordinates(arrangeElements[0].element);\n                const targetCenterX = primaryCoordinates.x + primaryCoordinates.width / 2;\n\n                for (let elem of arrangeElements) {\n                    let selectedCoordinates = designerCanvas.getNormalizedElementCoordinates(elem.element);\n                    if (targetCenterX != selectedCoordinates.x + selectedCoordinates.width / 2) {\n                        let parent = designerCanvas.getNormalizedElementCoordinates(elem.parent.element);\n                        if (elem.hasStyle('right') && !elem.hasStyle('left')) {\n                            let right = parent.x + parent.width - targetCenterX - selectedCoordinates.width / 2;\n                            this.arrange(elem, 'right', right + \"px\");\n                        } else {\n                            let left = targetCenterX - selectedCoordinates.width / 2 - parent.x;\n                            this.arrange(elem, 'left', left + \"px\");\n                        }\n                    }\n                }\n                grp.commit();\n                break;\n            }\n        }\n    }\n\n    private static arrange(element: IDesignItem, attribut: string, value: string) {\n        element.setStyle(attribut, value);\n    }\n\n    private static formGroup(name: string, designerCanvas: IDesignerCanvas): ChangeGroup {\n        return designerCanvas.instanceServiceContainer.selectionService.primarySelection.openGroup(name);\n    }\n}\n\nenum ArrangeDirection {\n    TOP = 'arrangeTop',\n    RIGHT = 'arrangeRight',\n    BOTTOM = 'arrangeBottom',\n    LEFT = 'arrangeLeft',\n    HORIZONTAL_CENTER = 'arrangeHorizontalCenter',\n    VERTICAL_CENTER = 'arrangeVerticalCenter',\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/Browser.ts",
    "content": "export const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/ClipboardHelper.ts",
    "content": "export async function copyTextToClipboard(text) {\r\n    copyToClipboard([['text/plain', text]]);\r\n}\r\n\r\n//used, so you could copy internal if you have no clipboard access\r\nlet internalClipboard = null;\r\n\r\nexport async function copyToClipboard(items: [format: string, data: string][]) {\r\n\r\n    if (navigator.clipboard) {\r\n        try {\r\n            let data = [];\r\n            for (let n of items) {\r\n                data.push(new ClipboardItem({ [n[0]]: new Blob([n[1]], { type: n[0] }) }));\r\n            }\r\n            await navigator.clipboard.write(data);\r\n        } catch (err) {\r\n            await navigator.clipboard.writeText(items[0][1]);\r\n            internalClipboard = items[0][1];\r\n        }\r\n        console.info('Copy to clipboard successful');\r\n    } else {\r\n        let activeElement: HTMLElement = <HTMLElement>document.activeElement;\r\n        while (activeElement?.shadowRoot?.activeElement)\r\n            activeElement = <HTMLElement>activeElement.shadowRoot.activeElement;\r\n\r\n        internalClipboard = items[0][1];\r\n\r\n        const textArea = document.createElement('textarea');\r\n        textArea.style.position = 'fixed';\r\n        textArea.style.top = '0';\r\n        textArea.style.left = '0';\r\n        textArea.style.width = '2em';\r\n        textArea.style.height = '2em';\r\n        textArea.style.padding = '0';\r\n        textArea.style.border = 'none';\r\n        textArea.style.outline = 'none';\r\n        textArea.style.boxShadow = 'none';\r\n        textArea.style.background = 'transparent';\r\n        textArea.value = items[0][1];\r\n        document.body.appendChild(textArea);\r\n        textArea.select();\r\n        try {\r\n            document.execCommand('copy');\r\n        } catch (err) {\r\n            try {\r\n                document.execCommand('copy');\r\n            } catch (err) {\r\n                console.error(err);\r\n            }\r\n        }\r\n        document.body.removeChild(textArea);\r\n\r\n        activeElement.focus()\r\n    }\r\n}\r\n\r\nexport async function getTextFromClipboard(): Promise<string> {\r\n    if (navigator.clipboard) {\r\n        return new Promise(async (resolve, reject) => {\r\n            const clipText = await navigator.clipboard.readText();\r\n            resolve(clipText);\r\n        });\r\n    } else {\r\n        return new Promise(async (resolve, reject) => {\r\n            const textArea = document.createElement('textarea');\r\n            textArea.style.position = 'fixed';\r\n            textArea.style.top = '0';\r\n            textArea.style.left = '0';\r\n            textArea.style.width = '2em';\r\n            textArea.style.height = '2em';\r\n            textArea.style.padding = '0';\r\n            textArea.style.border = 'none';\r\n            textArea.style.outline = 'none';\r\n            textArea.style.boxShadow = 'none';\r\n            textArea.style.background = 'transparent';\r\n            document.body.appendChild(textArea);\r\n            textArea.focus();\r\n            textArea.select();\r\n            document.execCommand('paste');\r\n            let value = textArea.value;\r\n            if (!value)\r\n                value = internalClipboard;\r\n            document.body.removeChild(textArea);\r\n            resolve(value);\r\n        });\r\n    }\r\n}\r\n\r\nexport async function getFromClipboard() {\r\n    if (navigator.clipboard) {\r\n        return await navigator.clipboard.read();\r\n    } else {\r\n        return null;\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/CssAttributeParser.ts",
    "content": "\r\nenum Token {\r\n  Name,\r\n  Value,\r\n  InQuote\r\n}\r\n\r\nexport class CssEntry {\n  constructor(name: string, value: string, important: boolean) {\n    this.name = name.trim();\n    this.value = value.trim();\n    this.important = important;\n  }\n  name: string;\n  value: string;\n  important: boolean;\n}\n\r\nexport class CssAttributeParser {\r\n\r\n  entries: CssEntry[] = [];\r\n\r\n  public parse(text: string, quoteType: string = '\\'') {\r\n    this.entries = [];\r\n\r\n    let name = '';\r\n    let value = '';\r\n    let token = Token.Name;\r\n\r\n    for (let n = 0; n < text.length; n++) {\r\n      let c = text[n];\r\n      if (token === Token.Name) {\r\n        if (c === ':')\r\n          token = Token.Value;\r\n        else if (c === ';') {\r\n          name = '';\r\n        } else\r\n          name += c;\r\n      } else if (token === Token.Value) {\r\n        if (c === ';') {\r\n          const entry = this.createEntry(name, value);\n          this.entries.push(entry);\n          name = '';\r\n          value = '';\r\n          token = Token.Name;\r\n        } else {\r\n          if (c === quoteType) {\r\n            token = Token.InQuote;\r\n          }\r\n          value += c;\r\n        }\r\n      } else if (token === Token.InQuote) {\r\n        if (c === '\\\\') {\r\n          value += c;\r\n          n++;\r\n          c = text[n];\r\n          value += c;\r\n        } else if (c === quoteType) {\r\n          value += c;\r\n          token = Token.Value;\r\n        } else {\r\n          value += c;\r\n        }\r\n      }\r\n    }\r\n\r\n    if (name.trim() !== '') {\r\n      this.entries.push(this.createEntry(name, value));\n    }\n  }\n\n  private createEntry(name: string, value: string) {\n    const match = value.match(/\\s*!\\s*important\\s*$/i);\n    if (!match)\n      return new CssEntry(name, value, false);\n\n    return new CssEntry(name, value.substring(0, match.index), true);\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/CssCombiner.ts",
    "content": "export class CssCombiner {\r\n  private static _helperElement = document.createElement('div');\r\n\r\n  static combine(styles: Map<string, string>, globalStyles?: Map<string, string>) {\r\n    CssCombiner.applyStylesToHelper(styles);\r\n    CssCombiner.combineBorder(styles);\r\n    CssCombiner.combineMargin(styles);\r\n    CssCombiner.combinePadding(styles);\r\n    CssCombiner.combineInset(styles);\r\n    CssCombiner.combineBackground(styles);\r\n    CssCombiner.combineFont(styles);\r\n    styles = CssCombiner.combineBrowserSupportedShorthands(styles);\r\n\r\n    if (globalStyles) {\r\n      for (let g of globalStyles) {\r\n        if (styles.has(g[0])) {\r\n          if (styles.get(g[0]) === g[1])\r\n            styles.delete(g[0]);\r\n        }\r\n      }\r\n    }\r\n    return styles;\r\n  }\r\n\r\n  private static applyStylesToHelper(styles: Map<string, string>) {\r\n    let e = CssCombiner._helperElement;\r\n    e.setAttribute('style', '');\r\n    for (let s of styles) {\r\n      if (s[0].startsWith('--') || s[0].includes('-'))\r\n        e.style.setProperty(s[0], s[1]);\r\n      else\r\n        (<any>e.style)[s[0]] = s[1];\r\n    }\r\n    return e;\r\n  }\r\n\r\n  private static combineBrowserSupportedShorthands(styles: Map<string, string>) {\r\n    const originalNormalizedValues = CssCombiner.getNormalizedStyleValues(styles);\r\n    let combinedStyles = new Map(styles);\r\n\r\n    while (true) {\r\n      const candidateStyles = CssCombiner.parseStyleDeclarationList(CssCombiner.applyStylesToHelper(combinedStyles).style.cssText);\r\n      let bestCandidate: Map<string, string> | null = null;\r\n      let bestSavings = 0;\r\n\r\n      for (let candidate of candidateStyles) {\r\n        const coverage = CssCombiner.getCandidateCoverage(combinedStyles, originalNormalizedValues, candidate[0], candidate[1]);\r\n        if (coverage.length === 0)\r\n          continue;\r\n\r\n        let tentativeStyles = new Map(combinedStyles);\r\n        for (let coveredStyle of coverage)\r\n          tentativeStyles.delete(coveredStyle);\r\n        tentativeStyles.set(candidate[0], candidate[1]);\r\n\r\n        const savings = CssCombiner.getSerializedSize(combinedStyles) - CssCombiner.getSerializedSize(tentativeStyles);\r\n        if (savings <= 0)\r\n          continue;\r\n\r\n        if (!CssCombiner.matchesNormalizedStyleValues(tentativeStyles, originalNormalizedValues))\r\n          continue;\r\n\r\n        if (savings > bestSavings) {\r\n          bestSavings = savings;\r\n          bestCandidate = tentativeStyles;\r\n        }\r\n      }\r\n\r\n      if (!bestCandidate)\r\n        return combinedStyles;\r\n\r\n      combinedStyles = bestCandidate;\r\n    }\r\n  }\r\n\r\n  private static getNormalizedStyleValues(styles: Map<string, string>) {\r\n    const element = CssCombiner.applyStylesToHelper(styles);\r\n    const normalizedValues = new Map<string, string>();\r\n\r\n    for (let style of styles)\r\n      normalizedValues.set(style[0], CssCombiner.readStyleValue(element.style, style[0]));\r\n\r\n    return normalizedValues;\r\n  }\r\n\r\n  private static getCandidateCoverage(styles: Map<string, string>, originalNormalizedValues: Map<string, string>, candidateName: string, candidateValue: string) {\r\n    const element = CssCombiner.applyStylesToHelper(new Map([[candidateName, candidateValue]]));\r\n    const coverage: string[] = [];\r\n\r\n    for (let style of styles) {\r\n      const originalValue = originalNormalizedValues.get(style[0]);\r\n      if (!originalValue)\r\n        continue;\r\n\r\n      if (CssCombiner.readStyleValue(element.style, style[0]) === originalValue)\r\n        coverage.push(style[0]);\r\n    }\r\n\r\n    return coverage;\r\n  }\r\n\r\n  private static matchesNormalizedStyleValues(styles: Map<string, string>, originalNormalizedValues: Map<string, string>) {\r\n    const element = CssCombiner.applyStylesToHelper(styles);\r\n\r\n    for (let normalizedValue of originalNormalizedValues) {\r\n      if (CssCombiner.readStyleValue(element.style, normalizedValue[0]) !== normalizedValue[1])\r\n        return false;\r\n    }\r\n\r\n    return true;\r\n  }\r\n\r\n  private static readStyleValue(style: CSSStyleDeclaration, name: string) {\r\n    if (name.startsWith('--') || name.includes('-'))\r\n      return style.getPropertyValue(name).trim();\r\n\r\n    return String((<any>style)[name] ?? '').trim();\r\n  }\r\n\r\n  private static getSerializedSize(styles: Map<string, string>) {\r\n    let size = 0;\r\n    for (let style of styles)\r\n      size += style[0].length + style[1].length + 2;\r\n    return size;\r\n  }\r\n\r\n  private static parseStyleDeclarationList(cssText: string) {\r\n    let styles = new Map<string, string>();\r\n    let start = 0;\r\n    let quote: string | null = null;\r\n    let parenthesisDepth = 0;\r\n\r\n    for (let i = 0; i < cssText.length; i++) {\r\n      const c = cssText[i];\r\n      if (quote) {\r\n        if (c === quote && cssText[i - 1] !== '\\\\')\r\n          quote = null;\r\n        continue;\r\n      }\r\n\r\n      if (c === '\"' || c === '\\'') {\r\n        quote = c;\r\n        continue;\r\n      }\r\n\r\n      if (c === '(') {\r\n        parenthesisDepth++;\r\n        continue;\r\n      }\r\n      if (c === ')' && parenthesisDepth > 0) {\r\n        parenthesisDepth--;\r\n        continue;\r\n      }\r\n\r\n      if (c === ';' && parenthesisDepth === 0) {\r\n        CssCombiner.addStyleDeclaration(styles, cssText.substring(start, i));\r\n        start = i + 1;\r\n      }\r\n    }\r\n\r\n    CssCombiner.addStyleDeclaration(styles, cssText.substring(start));\r\n    return styles;\r\n  }\r\n\r\n  private static addStyleDeclaration(styles: Map<string, string>, declaration: string) {\r\n    const trimmedDeclaration = declaration.trim();\r\n    if (!trimmedDeclaration)\r\n      return;\r\n\r\n    let quote: string | null = null;\r\n    let parenthesisDepth = 0;\r\n\r\n    for (let i = 0; i < trimmedDeclaration.length; i++) {\r\n      const c = trimmedDeclaration[i];\r\n      if (quote) {\r\n        if (c === quote && trimmedDeclaration[i - 1] !== '\\\\')\r\n          quote = null;\r\n        continue;\r\n      }\r\n\r\n      if (c === '\"' || c === '\\'') {\r\n        quote = c;\r\n        continue;\r\n      }\r\n\r\n      if (c === '(') {\r\n        parenthesisDepth++;\r\n        continue;\r\n      }\r\n      if (c === ')' && parenthesisDepth > 0) {\r\n        parenthesisDepth--;\r\n        continue;\r\n      }\r\n\r\n      if (c === ':' && parenthesisDepth === 0) {\r\n        const name = trimmedDeclaration.substring(0, i).trim();\r\n        const value = trimmedDeclaration.substring(i + 1).trim();\r\n        if (name)\r\n          styles.set(name, value);\r\n        return;\r\n      }\r\n    }\r\n  }\r\n\r\n  private static combineBorder(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-left-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-right-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-top-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-bottom-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-left-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-right-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-top-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-bottom-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-left-width')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-right-width')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-top-width')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-bottom-width')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-width')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-top')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-right')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-left')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-bottom')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'border-width')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    let bls = e.style.borderLeftStyle;\r\n    let blc = e.style.borderLeftColor;\r\n    if (bls && blc &&\r\n      e.style.borderRightStyle === bls && e.style.borderTopStyle === bls && e.style.borderBottomStyle === bls &&\r\n      e.style.borderRightColor === blc && e.style.borderTopColor === blc && e.style.borderBottomColor === blc) {\r\n      let btw = e.style.borderTopWidth;\r\n      let brw = e.style.borderRightWidth;\r\n      let bbw = e.style.borderBottomWidth;\r\n      let blw = e.style.borderLeftWidth;\r\n\r\n      styles.delete('border-left-style');\r\n      styles.delete('border-right-style');\r\n      styles.delete('border-top-style');\r\n      styles.delete('border-bottom-style');\r\n      styles.delete('border-left-color');\r\n      styles.delete('border-right-color');\r\n      styles.delete('border-top-color');\r\n      styles.delete('border-bottom-color');\r\n      styles.delete('border-left-width');\r\n      styles.delete('border-right-width');\r\n      styles.delete('border-top-width');\r\n      styles.delete('border-bottom-width');\r\n      styles.delete('border-width');\r\n      styles.delete('border-style');\r\n      styles.delete('border-color');\r\n      styles.delete('border-top');\r\n      styles.delete('border-right');\r\n      styles.delete('border-left');\r\n      styles.delete('border-bottom');\r\n\r\n      if (e.style.borderRightWidth == blw && e.style.borderTopWidth === blw && e.style.borderBottomWidth === blw) {\r\n        styles.set('border', blw + ' ' + bls + ' ' + blc);\r\n      } else {\r\n        styles.set('border', bls + ' ' + blc);\r\n        if (btw === bbw && brw === blw) {\r\n          styles.set('border-width', btw + ' ' + brw);\r\n        } else {\r\n          styles.set('border-width', btw + ' ' + brw + ' ' + bbw + ' ' + blw);\r\n        }\r\n      }\r\n    }\r\n\r\n    if (e.style.borderImageSource === 'initial')\r\n      styles.delete('border-image-source');\r\n    if (e.style.borderImageSlice === 'initial')\r\n      styles.delete('border-image-slice');\r\n    if (e.style.borderImageWidth === 'initial')\r\n      styles.delete('border-image-width');\r\n    if (e.style.borderImageOutset === 'initial')\r\n      styles.delete('border-image-outset');\r\n    if (e.style.borderImageRepeat === 'initial')\r\n      styles.delete('border-image-repeat');\r\n  }\r\n\r\n  private static combineMargin(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'margin-top')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'margin-right')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'margin-bottom')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'margin-left')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'margin')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    if (e.style.marginTop && e.style.marginRight && e.style.marginBottom && e.style.marginLeft) {\r\n      styles.delete('margin-top');\r\n      styles.delete('margin-right');\r\n      styles.delete('margin-bottom');\r\n      styles.delete('margin-left');\r\n      if (e.style.marginTop == e.style.marginRight && e.style.marginTop == e.style.marginBottom && e.style.marginTop == e.style.marginLeft) {\r\n        styles.set('margin', e.style.marginTop);\r\n      } else\r\n        styles.set('margin', e.style.marginTop + ' ' + e.style.marginRight + ' ' + e.style.marginBottom + ' ' + e.style.marginLeft);\r\n    }\r\n  }\r\n\r\n  private static combinePadding(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'padding-top')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'padding-right')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'padding-bottom')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'padding-left')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'padding')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    if (e.style.paddingTop && e.style.paddingRight && e.style.paddingBottom && e.style.paddingLeft) {\r\n      styles.delete('padding-top');\r\n      styles.delete('padding-right');\r\n      styles.delete('padding-bottom');\r\n      styles.delete('padding-left');\r\n      if (e.style.paddingTop == e.style.paddingRight && e.style.paddingTop == e.style.paddingBottom && e.style.paddingTop == e.style.paddingLeft) {\r\n        styles.set('padding', e.style.paddingTop);\r\n      } else\r\n        styles.set('padding', e.style.paddingTop + ' ' + e.style.paddingRight + ' ' + e.style.paddingBottom + ' ' + e.style.paddingLeft);\r\n    }\r\n  }\r\n\r\n  private static combineInset(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'top')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'right')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'bottom')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'left')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'inset')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    if (e.style.top && e.style.right && e.style.bottom && e.style.left) {\r\n      styles.delete('top');\r\n      styles.delete('right');\r\n      styles.delete('bottom');\r\n      styles.delete('left');\r\n      styles.set('inset', e.style.top + ' ' + e.style.right + ' ' + e.style.bottom + ' ' + e.style.left);\r\n    }\r\n  }\r\n\r\n  private static combineBackground(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-image')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-position')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-position-x')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-position-y')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-size')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-repeat')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-repeat-x')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-repeat-y')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-attachment')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-origin')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-clip')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background-color')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'background')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    styles.delete('background-image');\r\n    styles.delete('background-position');\r\n    styles.delete('background-position-x'); //TODO\r\n    styles.delete('background-position-y'); //TODO\r\n    styles.delete('background-size');\r\n    styles.delete('background-repeat');\r\n    styles.delete('background-repeat-x'); //TODO\r\n    styles.delete('background-repeat-y'); //TODO\r\n    styles.delete('background-attachment');\r\n    styles.delete('background-origin');\r\n    styles.delete('background-clip');\r\n    styles.delete('background-color');\r\n    styles.delete('background');\r\n\r\n    let background = '';\r\n    if (e.style.backgroundImage && e.style.backgroundImage !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundImage;\r\n    if (e.style.backgroundPosition && e.style.backgroundPosition !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundPosition;\r\n    if (e.style.backgroundSize && e.style.backgroundSize !== 'initial')\r\n      background += (background === '' ? '' : ' / ') + e.style.backgroundSize;\r\n    if (e.style.backgroundRepeat && e.style.backgroundRepeat !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundRepeat;\r\n    if (e.style.backgroundAttachment && e.style.backgroundAttachment !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundAttachment;\r\n    if (e.style.backgroundOrigin && e.style.backgroundOrigin !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundOrigin;\r\n    if (e.style.backgroundClip && e.style.backgroundClip !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundClip;\r\n    if (e.style.backgroundColor && e.style.backgroundColor !== 'initial')\r\n      background += (background === '' ? '' : ' ') + e.style.backgroundColor;\r\n\r\n    if (background)\r\n      styles.set('background', background);\r\n  }\r\n\r\n  private static combineFont(styles: Map<string, string>) {\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'font-style')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'font-weight')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'font-size')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'line-height')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'font-family')) return;\r\n    if (!CssCombiner.checkIfStyleIsCombinable(styles, 'font')) return;\r\n\r\n    let e = CssCombiner._helperElement;\r\n    if (e.style.fontFamily) {\r\n      styles.delete('font-style');\r\n      styles.delete('font-weight');\r\n      styles.delete('font-size');\r\n      styles.delete('line-height');\r\n      styles.delete('font-family');\r\n      styles.delete('font');\r\n\r\n      let font = '';\r\n      if (e.style.fontStyle)\r\n        font += (font === '' ? '' : ' ') + e.style.fontStyle;\r\n      if (e.style.fontWeight)\r\n        font += (font === '' ? '' : ' ') + e.style.fontWeight;\r\n      if (e.style.fontSize)\r\n        font += (font === '' ? '' : ' ') + e.style.fontSize;\r\n      if (e.style.lineHeight)\r\n        font += '/' + e.style.lineHeight;\r\n      if (e.style.fontFamily)\r\n        font += (font === '' ? '' : ' ') + e.style.fontFamily;\r\n      styles.set('font', font);\r\n    }\r\n  }\r\n\r\n  private static checkIfStyleIsCombinable(styles: Map<string, string>, name: string) {\r\n    if (styles.has(name)) {\r\n      const st = styles.get(name);\r\n      if (typeof st == 'string') {\r\n        if (st.startsWith('var('))\r\n          return false;\r\n        return true;\r\n      }\r\n      return false;\r\n    }\r\n    return true;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/CssImportant.ts",
    "content": "export function splitCssImportant(value: string): { value: string, important: boolean } {\n  const match = value.match(/\\s*!\\s*important\\s*$/i);\n  if (!match)\n    return { value, important: false };\n\n  return { value: value.substring(0, match.index).trimEnd(), important: true };\n}\n\nexport function appendCssImportant(value: string, important: boolean) {\n  if (!important)\n    return value;\n\n  return value + ' !important';\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/CssUnitConverter.ts",
    "content": "//unsupported: ex, ch, svw, svh, vw, lvh, dvw, dvh, vi, ic, ric\r\n\r\nconst units = ['px', 'cm', 'mm', 'q', 'in', 'pc', 'pt', 'rem', 'em', 'vw', 'vh', 'vmin', 'vmax', 'lh', 'rlh', '%', 'ms', 's', 'deg', 'rad', 'grad', 'turn', 'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax', 'fr'];\r\nconst pattern = new RegExp(`^([\\-\\+]?(?:\\\\d+(?:\\\\.\\\\d+)?))(${units.join('|')})$`, 'i');\r\n\r\nexport function convertCssUnitToPixel(cssValue: string, target: HTMLElement, percentTarget: 'width' | 'height'): number {\r\n\r\n    if (!cssValue)\r\n        return null;\r\n\r\n    const supportedUnits = {\r\n\r\n        // Absolute sizes\r\n        'px': value => value,\r\n        'cm': value => value * 38,\r\n        'mm': value => value * 3.8,\r\n        'q': value => value * 0.95,\r\n        'in': value => value * 96,\r\n        'pc': value => value * 16,\r\n        'pt': value => value * 1.333333,\r\n\r\n\r\n        // Relative sizes\r\n        'rem': value => value * parseFloat(getComputedStyle(document.documentElement).fontSize),\r\n        'em': value => value * parseFloat(getComputedStyle(target).fontSize),\r\n        'vw': value => value / 100 * window.innerWidth,\r\n        'vh': value => value / 100 * window.innerHeight,\r\n        'vmin': value => value / 100 * (window.innerHeight < window.innerWidth ? window.innerHeight : window.innerWidth),\r\n        'vmax': value => value / 100 * (window.innerHeight > window.innerWidth ? window.innerHeight : window.innerWidth),\r\n        'lh': value => value * parseFloat(getComputedStyle(target).lineHeight),\r\n        'rlh': value => value * parseFloat(getComputedStyle(document.documentElement).lineHeight),\r\n        '%': value => value / 100 * (percentTarget == 'height' ? getOriginalSizeBeforeTransformation(target).height : getOriginalSizeBeforeTransformation(target).width),\r\n\r\n        /*TODO: container units\r\n        //find parent with computed style where container-type is inline-size or size (regarding to query type)\r\n        //use this size for calculation\r\n        'cqw':\r\n        'cqh':\r\n        'cqi':\r\n        'cqb':\r\n        'cqmin':\r\n        'cqmax':\r\n        */\r\n\r\n        // Times\r\n        'ms': value => value,\r\n        's': value => value * 1000,\r\n\r\n        // Angles\r\n        'deg': value => value,\r\n        'rad': value => value * (180 / Math.PI),\r\n        'grad': value => value * (180 / 200),\r\n        'turn': value => value * 360\r\n    };\r\n\r\n    // If is a match, return example: [ \"-2.75rem\", \"-2.75\", \"rem\" ]\r\n    const matches = cssValue.trim().match(pattern);\r\n\r\n    if (matches) {\r\n        const value = Number(matches[1]);\r\n        const unit = matches[2].toLowerCase();\r\n\r\n        // Sanity check, make sure unit conversion function exists\r\n        if (unit in supportedUnits) {\r\n            return supportedUnits[unit](value);\r\n        }\r\n    }\r\n\r\n    //@ts-ignore\r\n    return cssValue;\r\n}\r\n\r\nexport function getCssUnit(cssValue: string) {\r\n    const matches = cssValue.trim().match(pattern);\r\n    if (matches)\r\n        return matches[2].toLowerCase();\r\n    return null;\r\n}\r\n\r\nexport function convertCssUnit(cssValue: string | number, target: HTMLElement, percentTarget: 'width' | 'height', unit: string, roundFunc?: (val: number) => string): string {\r\n\r\n    if (!cssValue)\r\n        return null;\r\n\r\n    const supportedUnits = {\r\n\r\n        // Absolute sizes\r\n        'px': value => value,\r\n        'cm': value => value / 38,\r\n        'mm': value => value / 3.8,\r\n        'q': value => value / 0.95,\r\n        'in': value => value / 96,\r\n        'pc': value => value / 16,\r\n        'pt': value => value / 1.333333,\r\n\r\n        // Relative sizes\r\n        'rem': value => value / parseFloat(getComputedStyle(document.documentElement).fontSize),\r\n        'em': value => value / parseFloat(getComputedStyle(target).fontSize),\r\n        'vw': value => value * 100 / window.innerWidth,\r\n        'vh': value => value * 100 / window.innerHeight,\r\n        'vmin': value => value * 100 / (window.innerHeight < window.innerWidth ? window.innerHeight : window.innerWidth),\r\n        'vmax': value => value * 100 / (window.innerHeight > window.innerWidth ? window.innerHeight : window.innerWidth),\r\n        'lh': value => value / parseFloat(getComputedStyle(target).lineHeight),\r\n        'rlh': value => value / parseFloat(getComputedStyle(document.documentElement).lineHeight),\r\n        '%': value => value * 100 / (percentTarget == 'height' ? getOriginalSizeBeforeTransformation(target).height : getOriginalSizeBeforeTransformation(target).width),\r\n\r\n        // Times\r\n        'ms': value => value,\r\n        's': value => value / 1000,\r\n\r\n        // Angles\r\n        'deg': value => value,\r\n        'rad': value => value / (180 / Math.PI),\r\n        'grad': value => value / (180 / 200),\r\n        'turn': value => value / 360\r\n    };\r\n\r\n    if (typeof cssValue == 'string')\r\n        cssValue = convertCssUnitToPixel(cssValue, target, percentTarget);\r\n    if (unit in supportedUnits) {\r\n        const val = supportedUnits[unit](cssValue);\r\n        if (roundFunc)\r\n            return roundFunc(val) + unit\r\n        return val + unit;\r\n    }\r\n\r\n    if (roundFunc)\r\n        return roundFunc(cssValue);\r\n    return <any>cssValue;\r\n}\r\n\r\nfunction getOriginalSizeBeforeTransformation(element: HTMLElement): { width: number, height: number } {\r\n    return { width: element.offsetWidth, height: element.offsetHeight };\r\n}\r\n\r\nexport function splitCssGridColumnSizes(sizes: String) {\r\n    const parts = sizes.split(' ');\r\n    const ret: string[] = [];\r\n    for (let i = 0; i < parts.length; i++) {\r\n        let p = parts[i];\r\n        if (p.startsWith('repeat(')) {\r\n            while (!p.includes(\")\")) {\r\n                i++;\r\n                if (!parts[i])\r\n                    continue;\r\n                p += parts[i];\r\n            }\r\n\r\n        }\r\n        ret.push(p);\r\n    }\r\n    return ret;\r\n}\r\n\r\nexport function getExpandedCssGridColumnSizes(sizes: String) {\r\n    const parts = splitCssGridColumnSizes(sizes);\r\n    const ret: string[] = [];\r\n    for (let p of parts) {\r\n        if (p.startsWith('repeat(')) {\r\n            const prt = p.split(',');\r\n            for (let i = 0; i < parseInt(prt[0].substring(7)); i++)\r\n                ret.push(prt[1].substring(0, prt[1].length - 1));\r\n        } else\r\n            ret.push(p);\r\n    }\r\n    return ret.map(x => getCssUnit(x));\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/DesignerStylesheetPatcher.ts",
    "content": "export interface IDesignerStylesheetPatchAttributes {\n  forceHoverAttributeName: string;\n  forceActiveAttributeName: string;\n  forceVisitedAttributeName: string;\n  forceFocusAttributeName: string;\n  forceFocusWithinAttributeName: string;\n  forceFocusVisibleAttributeName: string;\n}\n\nexport function patchStylesheetSelectorForDesigner(text: string, attributes: IDesignerStylesheetPatchAttributes) {\n  return text\n    .replaceAll(/:root\\b/g, ':host')\n    .replaceAll(':focus-within', '[' + attributes.forceFocusWithinAttributeName + ']')\n    .replaceAll(':focus-visible', '[' + attributes.forceFocusVisibleAttributeName + ']')\n    .replaceAll(':hover', '[' + attributes.forceHoverAttributeName + ']')\n    .replaceAll(':active', '[' + attributes.forceActiveAttributeName + ']')\n    .replaceAll(':visited', '[' + attributes.forceVisitedAttributeName + ']')\n    .replaceAll(':focus', '[' + attributes.forceFocusAttributeName + ']');\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/ElementHelper.ts",
    "content": "import { IPoint } from '../../interfaces/IPoint.js';\r\nimport { IRect } from '../../interfaces/IRect.js';\r\nimport { IDesignItem } from '../item/IDesignItem.js';\r\nimport { NodeType } from '../item/NodeType.js';\r\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\r\n\r\nexport function inDesigner(element: Element): boolean {\r\n  let node = element.getRootNode();\r\n  if ((<ShadowRoot>node)?.host?.localName == \"node-projects-designer-canvas\")\r\n    return true;\r\n  return false;\r\n}\r\n\r\nexport function newElementFromString(text, document: Document): Element {\r\n  const range = document.createRange();\r\n  //@ts-ignore\r\n  const fragment = range.createContextualFragment(text, { includeShadowRoots: true });\r\n  return fragment.firstChild as Element;\r\n}\r\n\r\nexport enum ElementDisplayType {\r\n  none,\r\n  inline,\r\n  block,\r\n}\r\n\r\nexport function instanceOf<T extends Function>(node: any, fnc: T): node is T {\r\n  if (node instanceof fnc || node instanceof (node.ownerDocument.defaultView ?? window)[fnc.name])\r\n    return true;\r\n  return false;\r\n}\r\n\r\nexport function instanceOfAny(node: Node, ...fnc: Function[]) {\r\n  for (const f of fnc)\r\n    if (node instanceof f || node instanceof (node.ownerDocument.defaultView ?? window)[f.name])\r\n      return true;\r\n  return false;\r\n}\r\n\r\nexport function isInline(element: HTMLElement): boolean {\r\n  if (element == null)\r\n    return false;\r\n  if (instanceOfAny(element, SVGElement, HTMLHtmlElement, HTMLHeadElement, HTMLBodyElement, HTMLSelectElement, HTMLOptionElement))\r\n    return false;\r\n  return (element.ownerDocument.defaultView ?? window).getComputedStyle(element).display.startsWith('inline');\r\n}\r\n\r\nexport function isInlineAfter(element: HTMLElement): boolean {\r\n  if (element == null)\r\n    return false;\r\n  if (instanceOfAny(element, SVGElement, HTMLHtmlElement, HTMLHeadElement, HTMLBodyElement, HTMLSelectElement, HTMLOptionElement))\r\n    return false;\r\n  return (element.ownerDocument.defaultView ?? window).getComputedStyle(element).display.startsWith('inline');\r\n}\r\n\r\nexport function getElementDisplaytype(element: HTMLElement): ElementDisplayType {\r\n  if (instanceOfAny(element, SVGElement, HTMLHtmlElement, HTMLHeadElement, HTMLBodyElement))\r\n    return ElementDisplayType.block;\r\n  if (instanceOf(element, MathMLElement))\r\n    return ElementDisplayType.block;\r\n  const display = (element.ownerDocument.defaultView ?? window).getComputedStyle(element).display;\r\n  return display == 'none' ? ElementDisplayType.none : display.startsWith('inline') ? ElementDisplayType.inline : ElementDisplayType.block;\r\n}\r\n\r\nexport function isEmptyTextNode(node: Node): boolean {\r\n  return node.textContent.trim() == '' && node.textContent.indexOf('\\xa0' /* &nbsp; */) < 0;\r\n}\r\n\r\nexport function getActiveElement(): Element {\r\n  let activeElement = document.activeElement;\r\n  let lastActive = null;\r\n  while (activeElement != lastActive) {\r\n    lastActive = activeElement;\r\n    if (activeElement.shadowRoot != null && activeElement.shadowRoot.activeElement)\r\n      activeElement = activeElement.shadowRoot.activeElement;\r\n  }\r\n  return activeElement;\r\n}\r\n\r\nexport function getElementOffsetsInContainer(element: Element) {\r\n  if (instanceOf(element, HTMLElement)) {\r\n    //@ts-ignore\r\n    return { x: element.offsetLeft, y: element.offsetTop };\r\n  } else {\r\n    //const cs = (element.ownerDocument.defaultView ?? window).getComputedStyle(element);\r\n\r\n    //todo: this will not work correctly with transformed SVGs or MathML Elements \r\n    const r1 = getBoundingClientRectAlsoForDisplayContents(element);\r\n    const r2 = getBoundingClientRectAlsoForDisplayContents(element.parentElement);\r\n    return { x: r1.x - r2.x, y: r1.y - r2.y }\r\n  }\r\n}\r\n\r\nexport function getBoundingClientRectAlsoForDisplayContents(element: Element): DOMRect {\r\n  let r = element.getBoundingClientRect();\r\n  if (r.width == 0 && r.height == 0) {\r\n    const cs = (element.ownerDocument.defaultView ?? window).getComputedStyle(element);\r\n    if (cs.display == 'contents') {\r\n      if (element.shadowRoot) {\r\n        for (let c of element.shadowRoot.children) {\r\n          const rc = getBoundingClientRectAlsoForDisplayContents(c);\r\n          r = new DOMRect(\r\n            Math.min(r.x, rc.x),\r\n            Math.min(r.y, rc.y),\r\n            Math.max(r.width, rc.width),\r\n            Math.max(r.height, rc.height)\r\n          );\r\n        }\r\n      } else {\r\n        for (let c of element.children) {\r\n          const rc = getBoundingClientRectAlsoForDisplayContents(c);\r\n          r = new DOMRect(\r\n            Math.min(r.x, rc.x),\r\n            Math.min(r.y, rc.y),\r\n            Math.max(r.width, rc.width),\r\n            Math.max(r.height, rc.height)\r\n          );\r\n        }\r\n      }\r\n    }\r\n  }\r\n  return r;\r\n}\r\n\r\nexport function getElementZoomFactor(element: Element): number {\r\n  const zoom = (element.ownerDocument.defaultView ?? window).getComputedStyle(element).zoom;\r\n  if (!zoom || zoom === 'normal')\r\n    return 1;\r\n  if (zoom.endsWith('%')) {\r\n    const percentage = parseFloat(zoom);\r\n    return Number.isFinite(percentage) && percentage > 0 ? percentage / 100 : 1;\r\n  }\r\n  const value = parseFloat(zoom);\r\n  return Number.isFinite(value) && value > 0 ? value : 1;\r\n}\r\n\r\nexport function getContentBoxContentOffsets(element): IPoint {\r\n  let xOffset = parseInt(getComputedStyle(element).paddingLeft.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).marginLeft.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).borderLeft.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).paddingRight.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).marginRight.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).borderRight.replace('px', ''));\r\n\r\n  let yOffset = parseInt(getComputedStyle(element).paddingTop.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).marginTop.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).borderTop.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).paddingBottom.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).marginBottom.replace('px', ''))\r\n    + parseInt(getComputedStyle(element).borderBottom.replace('px', ''));\r\n\r\n  return { x: xOffset, y: yOffset };\r\n}\r\n\r\nexport function calculateOuterRect(designItems: IDesignItem[], designerCanvas: IDesignerCanvas): IRect {\r\n  let min: IPoint = { x: Number.MAX_VALUE, y: Number.MAX_VALUE };\r\n  let max: IPoint = { x: Number.MIN_VALUE, y: Number.MIN_VALUE };\r\n  let elementRect: IRect;\r\n\r\n  for (let s of designItems) {\r\n    if (s.nodeType == NodeType.TextNode || s.nodeType == NodeType.Comment)\r\n      continue;\r\n    elementRect = {\r\n      x: designerCanvas.getNormalizedElementCoordinates(s.element).x,\r\n      y: designerCanvas.getNormalizedElementCoordinates(s.element).y,\r\n      width: designerCanvas.getNormalizedElementCoordinates(s.element).width,\r\n      height: designerCanvas.getNormalizedElementCoordinates(s.element).height\r\n    }\r\n\r\n    // calculate min and max of selection\r\n    if (elementRect.x < min.x)\r\n      min.x = elementRect.x;\r\n    if (elementRect.y < min.y)\r\n      min.y = elementRect.y;\r\n    if (elementRect.x + elementRect.width > max.x)\r\n      max.x = elementRect.x + elementRect.width;\r\n    if (elementRect.y + elementRect.height > max.y)\r\n      max.y = elementRect.y + elementRect.height;\r\n  }\r\n\r\n  // calculate reckt around selection\r\n  return {\r\n    x: min.x,\r\n    y: min.y,\r\n    width: max.x - min.x,\r\n    height: max.y - min.y\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/GridHelper.ts",
    "content": "import { IPoint } from \"../../interfaces/IPoint.js\";\r\nimport { IDesignItem } from \"../item/IDesignItem.js\";\r\nimport { getElementSize } from \"./getBoxQuads.js\";\r\n\r\nexport interface IGridCellInformation {\r\n  x: number;\r\n  y: number;\r\n  width: number;\r\n  height: number;\r\n  name: string;\r\n  localX: number;\r\n  localY: number;\r\n}\r\n\r\nexport interface IGridGapInformation {\r\n  x: number;\r\n  y: number;\r\n  width: number;\r\n  height: number;\r\n  localX: number;\r\n  localY: number;\r\n  column?: number;\r\n  row?: number;\r\n  type: 'h' | 'v';\r\n}\r\n\r\nexport interface IGridInformation {\r\n  cells: IGridCellInformation[][];\r\n  gaps: IGridGapInformation[];\r\n  xGap: number;\r\n  yGap: number;\r\n}\r\n\r\nexport interface IGridCellHitResult {\r\n  row: number;\r\n  column: number;\r\n  cell: IGridCellInformation;\r\n  localPoint: IPoint;\r\n}\r\n\r\nexport function getElementGridInformation(element: HTMLElement) {\r\n  let cs = getComputedStyle(element);\r\n  let rowSpan = 1;\r\n  let colSpan = 1;\r\n  if (cs.gridRowEnd == 'auto')\r\n    rowSpan = 1\r\n  else if (cs.gridRowEnd.startsWith('span'))\r\n    rowSpan = parseInt(cs.gridRowEnd.substring(4));\r\n  else\r\n    rowSpan = parseInt(cs.gridRowEnd) - parseInt(cs.gridRowStart);\r\n  if (cs.gridColumnEnd == 'auto')\r\n    colSpan = 1\r\n  else if (cs.gridColumnEnd.startsWith('span'))\r\n    colSpan = parseInt(cs.gridColumnEnd.substring(4));\r\n  else\r\n    colSpan = parseInt(cs.gridColumnEnd) - parseInt(cs.gridColumnStart);\r\n\r\n  return { colSpan, rowSpan };\r\n}\r\n\r\nexport function getGridLocalPoint(designItem: IDesignItem, point: IPoint): IPoint {\r\n  const designerCanvas = designItem.instanceServiceContainer.designerCanvas;\r\n  const localPoint = designItem.element.convertPointFromNode(new DOMPoint(point.x, point.y), designerCanvas.canvas, { iframes: designerCanvas.iframes });\r\n  return { x: localPoint.x, y: localPoint.y };\r\n}\r\n\r\nexport function getElementLocalToCanvasMatrix(designItem: IDesignItem): DOMMatrix {\r\n  const designerCanvas = designItem.instanceServiceContainer.designerCanvas;\r\n  const origin = designerCanvas.canvas.convertPointFromNode(new DOMPoint(0, 0), designItem.element, { iframes: designerCanvas.iframes });\r\n  const xAxis = designerCanvas.canvas.convertPointFromNode(new DOMPoint(1, 0), designItem.element, { iframes: designerCanvas.iframes });\r\n  const yAxis = designerCanvas.canvas.convertPointFromNode(new DOMPoint(0, 1), designItem.element, { iframes: designerCanvas.iframes });\r\n\r\n  return new DOMMatrix([\r\n    xAxis.x - origin.x,\r\n    xAxis.y - origin.y,\r\n    yAxis.x - origin.x,\r\n    yAxis.y - origin.y,\r\n    origin.x,\r\n    origin.y,\r\n  ]);\r\n}\r\n\r\nexport function getGridColumnIndexFromLocalX(gridInformation: IGridInformation, localX: number): number {\r\n  const columns = gridInformation.cells[0];\r\n  if (!columns?.length || !Number.isFinite(localX))\r\n    return 0;\r\n\r\n  let column = 0;\r\n  for (let i = 0; i < columns.length; i++) {\r\n    const cell = columns[i];\r\n    if (localX > cell.localX + cell.width / 2) {\r\n      column = i;\r\n    }\r\n  }\r\n  return column;\r\n}\r\n\r\nexport function getGridColumnStartLineFromLocalX(gridInformation: IGridInformation, localX: number): number {\r\n  const columns = gridInformation.cells[0];\r\n  if (!columns?.length || !Number.isFinite(localX))\r\n    return 1;\r\n\r\n  let line = 1;\r\n  for (let i = 0; i < columns.length; i++) {\r\n    const cell = columns[i];\r\n    if (localX > cell.localX + cell.width / 2) {\r\n      line = i + 2;\r\n    }\r\n  }\r\n  return line;\r\n}\r\n\r\nexport function getGridRowIndexFromLocalY(gridInformation: IGridInformation, localY: number): number {\r\n  if (!gridInformation.cells.length || !Number.isFinite(localY))\r\n    return 0;\r\n\r\n  let row = 0;\r\n  for (let i = 0; i < gridInformation.cells.length; i++) {\r\n    const cell = gridInformation.cells[i][0];\r\n    if (localY > cell.localY + cell.height / 2) {\r\n      row = i;\r\n    }\r\n  }\r\n  return row;\r\n}\r\n\r\nexport function getGridRowStartLineFromLocalY(gridInformation: IGridInformation, localY: number): number {\r\n  if (!gridInformation.cells.length || !Number.isFinite(localY))\r\n    return 1;\r\n\r\n  let line = 1;\r\n  for (let i = 0; i < gridInformation.cells.length; i++) {\r\n    const cell = gridInformation.cells[i][0];\r\n    if (localY > cell.localY + cell.height / 2) {\r\n      line = i + 2;\r\n    }\r\n  }\r\n  return line;\r\n}\r\n\r\nexport function getGridCellFromPoint(designItem: IDesignItem, point: IPoint, gridInformation: IGridInformation = calculateGridInformation(designItem)): IGridCellHitResult {\r\n  const localPoint = getGridLocalPoint(designItem, point);\r\n  if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y))\r\n    return null;\r\n\r\n  for (let row = 0; row < gridInformation.cells.length; row++) {\r\n    for (let column = 0; column < gridInformation.cells[row].length; column++) {\r\n      const cell = gridInformation.cells[row][column];\r\n      if (localPoint.x >= cell.localX && localPoint.x <= cell.localX + cell.width && localPoint.y >= cell.localY && localPoint.y <= cell.localY + cell.height) {\r\n        return { row, column, cell, localPoint };\r\n      }\r\n    }\r\n  }\r\n\r\n  return null;\r\n}\r\n\r\nexport function calculateGridInformation(designItem: IDesignItem): IGridInformation {\r\n\r\n  //TODO: same name should combine columns/rows\r\n\r\n  const designerCanvas = designItem.instanceServiceContainer.designerCanvas;\r\n  const transformedCornerPoints: DOMQuad = designItem.element.getBoxQuads({ relativeTo: designerCanvas.canvas, iframes: designerCanvas.iframes })[0];\r\n  const itemSize = getElementSize(designItem.element);\r\n\r\n  const computedStyle = getComputedStyle(designItem.element);\r\n  const rows = computedStyle.gridTemplateRows.split(' ');\r\n  const columns = computedStyle.gridTemplateColumns.split(' ');\r\n\r\n  const paddingLeft = Number.parseFloat(computedStyle.paddingLeft);\r\n  const paddingTop = Number.parseFloat(computedStyle.paddingTop);\r\n  const borderLeft = Number.parseFloat(computedStyle.borderLeftWidth);\r\n  const borderTop = Number.parseFloat(computedStyle.borderTopWidth);\r\n\r\n\r\n  let y = 0;\r\n  let xGap = 0;\r\n  let yGap = 0;\r\n  let rw = 0;\r\n  let xOffset = transformedCornerPoints.p1.x + borderLeft;\r\n  let yOffset = transformedCornerPoints.p1.y + borderTop;\r\n  let localXOffset = borderLeft;\r\n  let localYOffset = borderTop;\r\n\r\n  let gridA: string[] = null;\r\n  if (computedStyle.gridTemplateAreas && computedStyle.gridTemplateAreas !== 'none')\r\n    gridA = computedStyle.gridTemplateAreas.split('\\\"');\r\n  if (computedStyle.columnGap && computedStyle.columnGap != 'normal')\r\n    xGap = Number.parseFloat(computedStyle.columnGap.replace('px', ''));\r\n  if (computedStyle.rowGap && computedStyle.rowGap != 'normal')\r\n    yGap = Number.parseFloat(computedStyle.rowGap.replace('px', ''));\r\n\r\n  let gesX = 0;\r\n  let gesY = 0;\r\n  for (let c of columns) {\r\n    const currX = Number.parseFloat(c.replace('px', ''));\r\n    gesX += currX + xGap;\r\n  }\r\n  gesX -= xGap;\r\n  for (let r of rows) {\r\n    const currY = Number.parseFloat(r.replace('px', ''));\r\n    gesY += currY + yGap;\r\n  }\r\n  gesY -= yGap;\r\n\r\n  if (computedStyle.justifyContent == 'center') {\r\n    const diff = (itemSize.width - gesX) / 2;\r\n    xOffset += diff;\r\n    localXOffset += diff;\r\n  } else if (computedStyle.justifyContent == 'end') {\r\n    const diff = itemSize.width - gesX;\r\n    xOffset += diff;\r\n    localXOffset += diff;\r\n  } else if (computedStyle.justifyContent == 'space-between') {\r\n    xGap += (itemSize.width - gesX) / (columns.length - 1);\r\n  } else if (computedStyle.justifyContent == 'space-around') {\r\n    let gp = (itemSize.width - gesX) / (columns.length * 2);\r\n    xGap += gp * 2;\r\n    xOffset += gp;\r\n    localXOffset += gp;\r\n  } else if (computedStyle.justifyContent == 'space-evenly') {\r\n    let gp = (itemSize.width - gesX) / (columns.length + 1);\r\n    xGap += gp;\r\n    xOffset += gp;\r\n    localXOffset += gp;\r\n  }\r\n\r\n  if (computedStyle.alignContent == 'center') {\r\n    const diff = (itemSize.height - gesY) / 2;\r\n    yOffset += diff;\r\n    localYOffset += diff;\r\n  } else if (computedStyle.alignContent == 'end') {\r\n    const diff = itemSize.height - gesY;\r\n    yOffset += diff;\r\n    localYOffset += diff;\r\n  } else if (computedStyle.alignContent == 'space-between') {\r\n    yGap += (itemSize.height - gesY) / (rows.length - 1);\r\n  } else if (computedStyle.alignContent == 'space-around') {\r\n    let gp = (itemSize.height - gesY) / (rows.length * 2);\r\n    yGap += gp * 2;\r\n    yOffset += gp;\r\n    localYOffset += gp;\r\n  } else if (computedStyle.alignContent == 'space-evenly') {\r\n    let gp = (itemSize.height - gesY) / (rows.length + 1);\r\n    yGap += gp;\r\n    yOffset += gp;\r\n    localYOffset += gp;\r\n  }\r\n\r\n  const retVal: IGridInformation = { cells: [], gaps: [], xGap, yGap };\r\n\r\n  for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {\r\n    const r = rows[rowIdx];\r\n    let areas: string[] = null;\r\n    if (gridA && gridA[rw + 1]) {\r\n      areas = gridA[rw + 1].split(' ');\r\n    }\r\n    let x = 0;\r\n    let cl = 0;\r\n    const currY = Number.parseFloat(r.replace('px', ''));\r\n    let cellList: IGridCellInformation[] = [];\r\n    retVal.cells.push(cellList);\r\n    for (let colIdx = 0; colIdx < columns.length; colIdx++) {\r\n      const c = columns[colIdx];\r\n      if (colIdx > 0) {\r\n        retVal.gaps.push({ x: x + xOffset + paddingLeft, y: y + yOffset + paddingTop, width: xGap, height: currY, localX: x + localXOffset + paddingLeft, localY: y + localYOffset + paddingTop, column: colIdx, row: rowIdx, type: 'v' });\r\n        x += xGap\r\n      }\r\n      const currX = Number.parseFloat(c.replace('px', ''));\r\n      if (rowIdx > 0) {\r\n        retVal.gaps.push({ x: x + xOffset + paddingLeft, y: y + yOffset - yGap + paddingTop, width: currX, height: yGap, localX: x + localXOffset + paddingLeft, localY: y + localYOffset - yGap + paddingTop, column: colIdx, row: rowIdx, type: 'h' });\r\n      }\r\n      let name = null;\r\n      if (areas && areas[cl]) {\r\n        const nm = areas[cl].trim();\r\n        if (nm != '.') {\r\n          name = nm;\r\n        }\r\n      }\r\n      const cell = { x: x + xOffset + paddingLeft, y: y + yOffset + paddingTop, width: currX, height: currY, name: name, localX: x + localXOffset + paddingLeft, localY: y + localYOffset + paddingTop };\r\n      cellList.push(cell);\r\n      x += currX;\r\n      cl++;\r\n    }\r\n    y += currY + yGap;\r\n    rw += 2;\r\n  }\r\n\r\n  return retVal;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/Helper.ts",
    "content": "import { IPoint } from \"../../interfaces/IPoint.js\";\r\nimport { IRect } from \"../../interfaces/IRect.js\";\r\n\r\nexport function htmlAsString(strings: TemplateStringsArray, ...values: any[]) {\r\n  return strings.reduce((result, str, i) => {\r\n    return result + str + (values[i] ?? '');\r\n  }, '');\r\n}\r\n\r\nexport function isAppleDevice() {\r\n  return window.navigator.platform?.startsWith(\"Mac\") || window.navigator.platform === \"iPhone\" || window.navigator.platform === \"iPad\" || window.navigator.platform === \"iPod\";\r\n}\r\n\r\nexport function sleep(ms): Promise<unknown> {\r\n  return new Promise(resolve => setTimeout(resolve, ms));\r\n}\r\n\r\nexport async function exportData(blob: Blob, fileName: string): Promise<void> {\r\n  const url = URL.createObjectURL(blob);\r\n  const a = document.createElement('a');\r\n\r\n  a.href = url;\r\n  a.style.display = 'none';\r\n  a.download = fileName;\r\n  document.body.appendChild(a);\r\n  a.click();\r\n  document.body.removeChild(a);\r\n  await sleep(300);\r\n}\r\n\r\nexport function dataURItoBlob(dataURI) {\r\n  var mime = dataURI.split(',')[0].split(':')[1].split(';')[0];\r\n  var binary = atob(dataURI.split(',')[1]);\r\n  var array = [];\r\n  for (var i = 0; i < binary.length; i++) {\r\n    array.push(binary.charCodeAt(i));\r\n  }\r\n  return new Blob([new Uint8Array(array)], { type: mime });\r\n}\r\n\r\nexport function pointInRect(point: IPoint, rect: IRect) {\r\n  return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;\r\n}\r\n\r\nexport function removeTrailing(text: string, char: string) {\r\n  if (text.endsWith(char ?? '/'))\r\n    return text.substring(0, text.length - 1);\r\n  return text;\r\n}\r\n\r\nexport function removeLeading(text: string, char: string) {\r\n  if (text.startsWith(char ?? '/'))\r\n    return text.substring(1);\r\n  return text;\r\n}\r\n\r\nexport function requestAnimationFramePromise() {\r\n  return new Promise(resolve => requestAnimationFrame(resolve));\r\n}\r\n\r\nexport function arraysEqual<T>(a: T[], b: T[]) {\r\n  if (a === b) return true;\r\n  if (a == null || b == null) return false;\r\n  if (a.length !== b.length) return false;\r\n\r\n  for (var i = 0; i < a.length; ++i) {\r\n    if (a[i] !== b[i]) return false;\r\n  }\r\n  return true;\r\n}\r\n\r\nlet nullObject: {};\r\nexport function deepValue(obj, path: string, returnNullObject = false, splitter = '.') {\r\n  if (path === undefined || path === null) {\r\n    return obj;\r\n  }\r\n\r\n  const pathParts = path.split(splitter);\r\n  for (let i = 0; i < pathParts.length; i++) {\r\n    if (obj != null) {\r\n      obj = obj[pathParts[i]];\r\n    } else {\r\n      return returnNullObject ? nullObject : null;\r\n    }\r\n  }\r\n  return obj;\r\n}\r\n\r\nexport function setDeepValue(obj, path: string, value, splitter = '.') {\r\n  if (path === undefined || path === null) {\r\n    return;\r\n  }\r\n\r\n  const pathParts = path.split(splitter);\r\n  for (let i = 0; i < pathParts.length - 1; i++) {\r\n    if (obj != null) {\r\n      let newObj = obj[pathParts[i]];\r\n      if (newObj == null) {\r\n        newObj = {};\r\n        obj[pathParts[i]] = newObj;\r\n      }\r\n      obj = newObj;\r\n    }\r\n  }\r\n\r\n  if (obj != null)\r\n    obj[pathParts[pathParts.length - 1]] = value;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/ITextWriter.ts",
    "content": "export interface ITextWriter {\r\n  get position(): number;\r\n  isLastCharNewline(): boolean;\r\n  levelRaise(): void\r\n  levelShrink(): void\r\n  write(text: string): void\r\n  writeLine(text: string): void\r\n  writeIndent(): void\r\n  writeNewline(): void\r\n  getString(): string\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/IndentedTextWriter.ts",
    "content": "import { ITextWriter } from './ITextWriter.js';\r\n\r\nexport class IndentedTextWriter implements ITextWriter  {\r\n  private _textHolder: string = ''\r\n  public readonly indent: number = 4;\r\n  public level: number = 0;\r\n\r\n  public get position(): number {\r\n    return this._textHolder.length;\r\n  }\r\n\r\n  public isLastCharNewline() {\r\n    return this._textHolder[this._textHolder.length - 1] === '\\n';\r\n  }\r\n\r\n  public levelRaise() {\r\n    this.level++;\r\n  }\r\n\r\n  public levelShrink() {\r\n    this.level--;\r\n  }\r\n\r\n  public write(text: string) {\r\n    this._textHolder += text;\r\n  }\r\n\r\n  public writeLine(text: string) {\r\n    this.writeIndent();\r\n    this._textHolder += text;\r\n    this.writeNewline();\r\n  }\r\n\r\n  public writeIndent() {\r\n    this._textHolder += ''.padEnd(this.level * this.indent, ' ');\r\n  }\r\n\r\n  public writeNewline() {\r\n    this._textHolder += '\\n';\r\n  }\r\n\r\n  public getString() {\r\n    return this._textHolder;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/KeyboardHelper.ts",
    "content": "import { isAppleDevice } from \"./Helper.js\";\n\nexport function hasCommandKey(event: KeyboardEvent | MouseEvent | PointerEvent | DragEvent | WheelEvent) {\n    if (isAppleDevice())\n        return event.metaKey;\n    return event.ctrlKey;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/LayoutHelper.ts",
    "content": "//TODO:  this function should return the correct property to change a layout,\r\n// for example left/right when left or right is used,\r\n//maybe margin on grid? or transform??\r\n\r\nimport { IPoint } from \"../../interfaces/IPoint.js\";\r\nimport { IDesignItem } from \"../item/IDesignItem.js\";\r\nimport { getBoundingClientRectAlsoForDisplayContents } from \"./ElementHelper.js\";\r\nimport { getGeometryReader } from \"../widgets/designerView/extensions/svg/geometry/GeometryReaderFactory.js\";\r\nimport { applyGeometryWritesToDesignItem } from '../widgets/designerView/extensions/svg/geometry/GeometryWriteHelper.js';\r\n\r\n/**\r\n * This function filters a items list, so only the outer elments are used for example in a move\r\n */\r\nexport function filterChildPlaceItems(items: IDesignItem[]) {\r\n  const filterdPlaceItems: IDesignItem[] = [];\r\n  next:\r\n  for (let i of items) {\r\n    let par = i.parent;\r\n    while (par != null && !par.isRootItem) {\r\n      if (items.indexOf(par) >= 0)\r\n        continue next;\r\n      par = par.parent;\r\n    }\r\n    filterdPlaceItems.push(i);\r\n\r\n  }\r\n  return filterdPlaceItems;\r\n}\r\n\r\nexport function getDesignItemCurrentPos(designItem: IDesignItem, mode: 'position' | 'transform' | 'margin' | 'padding'): IPoint {\r\n  if (mode === 'position') {\r\n    const computedStyleMovedElement = getComputedStyle(designItem.element);\r\n    let oldLeft: number | null = parseFloat(computedStyleMovedElement.left);\r\n    oldLeft = Number.isNaN(oldLeft) ? null : oldLeft;\r\n    let oldTop: number | null = parseFloat(computedStyleMovedElement.top);\r\n    oldTop = Number.isNaN(oldTop) ? null : oldTop;\r\n    return { x: oldLeft ?? 0, y: oldTop ?? 0 }\r\n  }\r\n  return { x: 0, y: 0 }\r\n}\r\n\r\nexport function placeDesignItem(container: IDesignItem, designItem: IDesignItem, offset: IPoint, mode: 'position' | 'transform' | 'margin' | 'padding') {\n  const movedElement = designItem.element;\r\n  const computedStyleMovedElement = getComputedStyle(movedElement);\r\n\r\n  if (mode === 'position') {\r\n    if (_placeSvgDesignItem(designItem, offset)) {\r\n      return;\r\n    }\r\n\r\n    let positionedContainerElement: Element | null = container.element;\r\n    let computedStylePositionedContainer = container.getComputedStyle();\r\n    if (computedStylePositionedContainer.position !== 'relative' && computedStylePositionedContainer.position !== 'absolute' && positionedContainerElement && (<HTMLElement>positionedContainerElement).offsetParent) {\r\n      positionedContainerElement = (<HTMLElement>positionedContainerElement).offsetParent;\r\n      if (positionedContainerElement)\r\n        computedStylePositionedContainer = container.window.getComputedStyle(positionedContainerElement);\r\n    }\r\n\r\n    let oldLeft = null;\r\n    let oldRight = null;\r\n    let oldTop = null;\r\n    let oldBottom = null;\r\n\r\n    let containerLeft = 0;\r\n    let containerRight = 0;\r\n    let containerTop = 0;\r\n    let containerBottom = 0;\r\n\r\n    let hasPositionedLayout = false;\r\n    if (computedStyleMovedElement.position === 'relative' || computedStyleMovedElement.position === 'absolute') {\r\n      oldLeft = parseFloat((<HTMLElement>movedElement).style.left);\r\n      oldLeft = Number.isNaN(oldLeft) ? null : oldLeft;\r\n      oldTop = parseFloat((<HTMLElement>movedElement).style.top);\r\n      oldTop = Number.isNaN(oldTop) ? null : oldTop;\r\n      oldRight = parseFloat((<HTMLElement>movedElement).style.right);\r\n      oldRight = Number.isNaN(oldRight) ? null : oldRight;\r\n      oldBottom = parseFloat((<HTMLElement>movedElement).style.bottom);\r\n      oldBottom = Number.isNaN(oldBottom) ? null : oldBottom;\r\n      hasPositionedLayout = true;\r\n    } else {\r\n      if (positionedContainerElement && positionedContainerElement !== container.element) {\r\n        let posContainerRect = getBoundingClientRectAlsoForDisplayContents(positionedContainerElement);\r\n        let elementRect = getBoundingClientRectAlsoForDisplayContents(designItem.element);\r\n        containerLeft = elementRect.left - posContainerRect.left;\r\n        containerRight = elementRect.right - posContainerRect.right;\r\n        containerTop = elementRect.top - posContainerRect.top;\r\n        containerBottom = elementRect.bottom - posContainerRect.bottom;\r\n      }\r\n    }\r\n\r\n    if (!hasPositionedLayout)\r\n      designItem.setStyle('position', 'absolute');\r\n    if (oldLeft || oldRight == null)\r\n      designItem.setStyle('left', roundValue(designItem, offset.x + (oldLeft ?? 0) + containerLeft) + \"px\");\r\n    if (oldTop || oldBottom == null)\r\n      designItem.setStyle('top', roundValue(designItem, offset.y + (oldTop ?? 0) + containerTop) + \"px\");\r\n    if (oldRight != null)\r\n      designItem.setStyle('right', roundValue(designItem, (oldRight ?? 0) - offset.x + containerRight) + \"px\");\r\n    if (oldBottom != null)\r\n      designItem.setStyle('bottom', roundValue(designItem, (oldBottom ?? 0) - offset.y + containerBottom) + \"px\");\r\n  }\r\n}\n\nexport function transformOffsetByInverseLinearMatrix(offset: IPoint, matrix: Pick<DOMMatrixReadOnly, 'a' | 'b' | 'c' | 'd'>): IPoint {\n  const determinant = matrix.a * matrix.d - matrix.b * matrix.c;\n  if (Math.abs(determinant) < 1e-10) {\n    return offset;\n  }\n\n  return {\n    x: (matrix.d * offset.x - matrix.c * offset.y) / determinant,\n    y: (-matrix.b * offset.x + matrix.a * offset.y) / determinant,\n  };\n}\n\nfunction _placeSvgDesignItem(designItem: IDesignItem, offset: IPoint): boolean {\n  const element = designItem.element;\r\n  if (!(element instanceof SVGGraphicsElement) || element instanceof SVGSVGElement) {\r\n    return false;\r\n  }\r\n\r\n  const reader = getGeometryReader(element);\r\n  if (!reader) {\r\n    return false;\r\n  }\r\n\r\n  const geometry = reader.read(element);\r\n  for (const segment of geometry.segments) {\r\n    if (segment.point) {\r\n      segment.point.x += offset.x;\r\n      segment.point.y += offset.y;\r\n    }\r\n    if (segment.cp1) {\r\n      segment.cp1.x += offset.x;\r\n      segment.cp1.y += offset.y;\r\n    }\r\n    if (segment.cp2) {\r\n      segment.cp2.x += offset.x;\r\n      segment.cp2.y += offset.y;\r\n    }\r\n  }\r\n\r\n  const attrs = reader.serialize(geometry);\r\n  if (!attrs.length) {\r\n    return false;\r\n  }\r\n\r\n  const group = designItem.openGroup('place svg geometry');\r\n  applyGeometryWritesToDesignItem(designItem, attrs);\r\n  group.commit();\r\n\r\n  return true;\r\n}\r\n\r\nexport function roundValue(designItem: IDesignItem, value: number) {\r\n  if (designItem.serviceContainer.options.roundPixelsToDecimalPlaces >= 0) {\r\n    return value.toFixed(designItem.serviceContainer.options.roundPixelsToDecimalPlaces);\r\n  }\r\n  return value.toString();\r\n}\r\n\r\n/*function placeViaPosition(container: IDesignItem, designItem: IDesignItem, offset: IPoint, mode: 'position' | 'transform' | 'margin' | 'padding') {\r\n  \r\n}*/\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/NpmPackageHacks.json",
    "content": "{\r\n    \"@shoelace-style/shoelace\": {\r\n        \"html\": \"<link rel=\\\"stylesheet\\\" media=\\\"(prefers-color-scheme:light)\\\" href=\\\"${baseUrl}dist/themes/light.css\\\">\\n<link rel=\\\"stylesheet\\\" media=\\\"(prefers-color-scheme:dark)\\\" href=\\\"${baseUrl}dist/themes/dark.css\\\" onload=\\\"document.documentElement.classList.add('sl-theme-dark');\\\">\"\r\n    },\r\n    \"@microsoft/fast-components\": {\r\n        \"script\": \"let res = await import('@microsoft/fast-components');\\nres.provideFASTDesignSystem().register(res.allComponents);\"\r\n    },\r\n    \"@zooplus/zoo-web-components\": {\r\n        \"script\": \"let res = await import('@zooplus/zoo-web-components');\\nres.registerComponents(res);\",\r\n        \"style\":\":root {\\n--primary-mid: #3C9700;\\n--primary-light: #66B100;\\n--primary-dark: #286400;\\n--primary-ultralight: #EBF4E5;\\n--secondary-mid: #FF6200;\\n--secondary-light: #F80;\\n--secondary-dark: #CC4E00;\\n--info-ultralight: #ECF5FA;\\n--info-mid: #459FD0;\\n--warning-ultralight: #FDE8E9;\\n--warning-mid: #ED1C24;\\n}\"\r\n    },\r\n    \"@material/web\": {\r\n        \"import\": \"@material/web/all.js\"\r\n    },\r\n    \"@stencil/core\": {\r\n        \"map\": {\r\n            \"@stencil/core/internal/client\": \"internal/client/index.js\",\r\n            \"@stencil/core/internal/app-data\": \"internal/app-data/index.js\"\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/NpmPackageLoader.ts",
    "content": "import { IDesignerAddonJson } from \"../services/designerAddons/IDesignerAddonJson.js\";\r\nimport { IElementsJson } from \"../services/elementsService/IElementsJson.js\";\r\nimport { PreDefinedElementsService } from \"../services/elementsService/PreDefinedElementsService.js\";\r\nimport { WebcomponentManifestElementsService } from \"../services/elementsService/WebcomponentManifestElementsService.js\";\r\nimport { WebcomponentManifestPropertiesService } from \"../services/propertiesService/services/WebcomponentManifestPropertiesService.js\";\r\nimport { ServiceContainer } from \"../services/ServiceContainer.js\";\r\nimport { removeLeading, removeTrailing } from \"./Helper.js\";\r\nimport { ObservedCustomElementsRegistry } from \"./ObservedCustomElementsRegistry.js\";\r\n\r\nexport class NpmPackageLoader {\r\n\r\n    private static registryPatchedTohandleErrors: boolean;\r\n\r\n    private static packageHacks;\r\n\r\n    //packageSource = '//unpkg.com/';\r\n    private _packageSource: string;\r\n    private _dependecies = new Map<string, boolean>();\r\n\r\n    constructor(packageSource: string = '//cdn.jsdelivr.net/npm/') {\r\n        this._packageSource = packageSource;\r\n        NpmPackageLoader.patchCustomElementsRegistryToHandleErrors();\r\n    }\r\n\r\n    static patchCustomElementsRegistryToHandleErrors() {\r\n        if (!NpmPackageLoader.registryPatchedTohandleErrors) {\r\n            NpmPackageLoader.registryPatchedTohandleErrors = true;\r\n            let customElementsRegistry = window.customElements;\r\n            const registry: any = {};\r\n            registry.define = function (name, constructor, options) {\r\n                try {\r\n                    customElementsRegistry.define(name, constructor, options);\r\n                }\r\n                catch (err) {\r\n                    console.warn(err);\r\n                }\r\n            }\r\n            registry.get = function (name) {\r\n                return customElementsRegistry.get(name);\r\n            }\r\n            registry.upgrade = function (node) {\r\n                return customElementsRegistry.upgrade(node);\r\n            }\r\n            registry.whenDefined = function (name) {\r\n                return customElementsRegistry.whenDefined(name);\r\n            }\r\n\r\n            Object.defineProperty(window, \"customElements\", {\r\n                get() {\r\n                    return registry\r\n                }\r\n            });\r\n        }\r\n    }\r\n\r\n    //TODO: remove paletteTree form params. elements should be added to serviceconatiner, and the container should notify\r\n    async loadNpmPackage(pkg: string, serviceContainer?: ServiceContainer, paletteTree?: any, loadAllImports?: boolean, reportState?: (state: string) => void): Promise<{ html: string, style: string }> {\r\n        if (!NpmPackageLoader.packageHacks) {\r\n            NpmPackageLoader.packageHacks = (await import(\"./NpmPackageHacks.json\", { assert: { type: 'json' } })).default;\r\n        }\r\n\r\n        const baseUrl = window.location.protocol + this._packageSource + pkg + '/';\r\n\r\n        const packageJsonUrl = baseUrl + 'package.json';\r\n        if (reportState)\r\n            reportState(pkg + \": loading package.json\");\r\n        const packageJson = await fetch(packageJsonUrl);\r\n        const packageJsonObj = await packageJson.json();\r\n\r\n        this.addToImportmap(baseUrl, packageJsonObj);\r\n\r\n        const depPromises: Promise<void>[] = []\r\n        if (packageJsonObj.dependencies) {\r\n            for (let d in packageJsonObj.dependencies) {\r\n                depPromises.push(this.loadDependency(d, packageJsonObj.dependencies[d]));\r\n            }\r\n        }\r\n        await Promise.all(depPromises)\r\n        let customElementsUrl = baseUrl + 'custom-elements.json';\r\n        let elementsRootPath = baseUrl;\r\n        if (packageJsonObj.customElements) {\r\n            customElementsUrl = baseUrl + removeTrailing(packageJsonObj.customElements, '/');\r\n            if (customElementsUrl.includes('/')) {\r\n                let idx = customElementsUrl.lastIndexOf('/');\r\n                elementsRootPath = customElementsUrl.substring(0, idx + 1);\r\n            }\r\n        }\r\n        let webComponentDesignerUrl = baseUrl + 'web-component-designer.json';\r\n        if (packageJsonObj.webComponentDesigner) {\r\n            webComponentDesignerUrl = baseUrl + removeLeading(packageJsonObj.webComponentDesigner, '/');\r\n        }\r\n        if (reportState)\r\n            reportState(pkg + \": loading custom-elements.json\");\r\n        let customElementsJson = await fetch(customElementsUrl);\r\n\r\n        if (!customElementsJson.ok && packageJsonObj.homepage) {\r\n            try {\r\n                const url = new URL(packageJsonObj.homepage);\r\n                const newurl = 'https://raw.githubusercontent.com/' + url.pathname + '/master/custom-elements.json';\r\n                customElementsJson = await fetch(newurl);\r\n                console.warn(\"custom-elements.json was missing from npm package, but was loaded from github as a fallback.\")\r\n            }\r\n            catch (err) {\r\n                console.warn(\"github custom elments json fallback\", err);\r\n            }\r\n        }\r\n\r\n        if (serviceContainer) {\r\n            fetch(webComponentDesignerUrl).then(async x => {\r\n                if (x.ok) {\r\n                    const webComponentDesignerJson = <IDesignerAddonJson>await x.json();\r\n                    if (webComponentDesignerJson.services) {\r\n                        for (let o in webComponentDesignerJson.services) {\r\n                            for (let s of webComponentDesignerJson.services[o]) {\r\n                                if (s.startsWith('./'))\r\n                                    s = s.substring(2);\r\n                                //@ts-ignore\r\n                                const classDefinition = (await importShim(baseUrl + s)).default;\r\n                                //@ts-ignore\r\n                                serviceContainer.register(o, new classDefinition());\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            });\r\n        }\r\n\r\n        if (customElementsJson.ok) {\r\n            const customElementsJsonObj = await customElementsJson.json();\r\n            let elements = new WebcomponentManifestElementsService(packageJsonObj.name, elementsRootPath, customElementsJsonObj);\r\n            if (serviceContainer)\r\n                serviceContainer.register('elementsService', elements);\r\n            if (serviceContainer) {\r\n                let properties = new WebcomponentManifestPropertiesService(packageJsonObj.name, customElementsJsonObj);\r\n                serviceContainer.register('propertyService', properties);\r\n            }\r\n\r\n            if (loadAllImports) {\r\n                for (let e of await elements.getElements()) {\r\n                    //@ts-ignore\r\n                    importShim(e.import);\r\n                }\r\n            }\r\n\r\n            if (serviceContainer && paletteTree) {\r\n                //TODO: should be retriggered by service container, or changeing list in container\r\n                paletteTree.loadControls(serviceContainer, serviceContainer.elementsServices);\r\n            }\r\n\r\n            /* Package Hacks */\r\n            if (NpmPackageLoader.packageHacks[pkg]?.import) {\r\n                import(NpmPackageLoader.packageHacks[pkg]?.import);\r\n            }\r\n            if (NpmPackageLoader.packageHacks[pkg]?.script) {\r\n                const scriptUrl = URL.createObjectURL(new Blob([NpmPackageLoader.packageHacks[pkg]?.script], { type: 'application/javascript' }));\r\n                import(scriptUrl);\r\n            }\r\n        } else {\r\n            console.warn('npm package: ' + pkg + ' - no custom-elements.json found, only loading javascript module');\r\n\r\n            const observedCustomElementsRegistry = new ObservedCustomElementsRegistry();\r\n\r\n            if (packageJsonObj.module) {\r\n                //@ts-ignore\r\n                await importShim(baseUrl + removeLeading(packageJsonObj.module, '/'))\r\n            } else if (packageJsonObj.main) {\r\n                //@ts-ignore\r\n                await importShim(baseUrl + removeLeading(packageJsonObj.main, '/'))\r\n            } else if (packageJsonObj.unpkg) {\r\n                //@ts-ignore\r\n                await importShim(baseUrl + removeLeading(packageJsonObj.unpkg, '/'))\r\n            } else {\r\n                console.warn('npm package: ' + pkg + ' - no entry point in package found.');\r\n            }\r\n\r\n            /* Package Hacks */\r\n            if (NpmPackageLoader.packageHacks[pkg]?.import) {\r\n                await import(NpmPackageLoader.packageHacks[pkg]?.import);\r\n            }\r\n            if (NpmPackageLoader.packageHacks[pkg]?.script) {\r\n                const scriptUrl = URL.createObjectURL(new Blob([NpmPackageLoader.packageHacks[pkg]?.script], { type: 'application/javascript' }));\r\n                await import(scriptUrl);\r\n            }\r\n\r\n            const newElements = observedCustomElementsRegistry.getNewElements();\r\n            if (newElements.length > 0 && serviceContainer && paletteTree) {\r\n                const elementsCfg: IElementsJson = {\r\n                    elements: newElements\r\n                }\r\n                let elService = new PreDefinedElementsService(pkg, elementsCfg)\r\n                serviceContainer.register('elementsService', elService);\r\n                paletteTree.loadControls(serviceContainer, serviceContainer.elementsServices);\r\n            }\r\n\r\n            observedCustomElementsRegistry.dispose();\r\n        }\r\n        if (reportState)\r\n            reportState(pkg + \": done\");\r\n\r\n        let retVal: any = {};\r\n\r\n        if (NpmPackageLoader.packageHacks[pkg]?.html) {\r\n            retVal.html = (<string>NpmPackageLoader.packageHacks[pkg]?.html).replaceAll(\"${baseUrl}\", baseUrl);\r\n        }\r\n        if (NpmPackageLoader.packageHacks[pkg]?.style) {\r\n            retVal.style = (<string>NpmPackageLoader.packageHacks[pkg]?.style).replaceAll(\"${baseUrl}\", baseUrl);\r\n        }\r\n        return retVal;\r\n    }\r\n\r\n    async loadDependency(dependency: string, version?: string, reportState?: (state: string) => void) {\r\n        if (this._dependecies.has(dependency))\r\n            return;\r\n\r\n        this._dependecies.set(dependency, true);\r\n\r\n        if (dependency.startsWith('@types')) {\r\n            console.warn('ignoring wrong dependency: ', dependency);\r\n            return;\r\n        }\r\n        if (reportState)\r\n            reportState(dependency + \": loading dependency: \" + dependency);\r\n        const baseUrl = window.location.protocol + this._packageSource + dependency + '/';\r\n\r\n        const packageJsonUrl = baseUrl + 'package.json';\r\n        const packageJson = await fetch(packageJsonUrl);\r\n        const packageJsonObj = await packageJson.json();\r\n\r\n        const depPromises: Promise<void>[] = []\r\n        if (packageJsonObj.dependencies) {\r\n            for (let d in packageJsonObj.dependencies) {\r\n                depPromises.push(this.loadDependency(d, packageJsonObj.dependencies[d]));\r\n            }\r\n        }\r\n        await Promise.all(depPromises)\r\n\r\n        this.addToImportmap(baseUrl, packageJsonObj);\r\n    }\r\n\r\n    async addToImportmap(baseUrl: string, packageJsonObj: { name?: string, module?: string, main?: string, unpkg?: string, exports?: Record<string, string> }) {\r\n        //@ts-ignore\r\n        const map = importShim.getImportMap().imports;\r\n        const importMap = { imports: {}, scopes: {} };\r\n\r\n        if (!map.hasOwnProperty(packageJsonObj.name)) {\r\n            //TODO: use exports of package.json for importMap\r\n            if (packageJsonObj.exports) {\r\n\r\n                /* \"exports\": {\r\n                \".\": {\r\n                    \"browser\": \"./index.browser.js\",\r\n                    \"default\": \"./index.js\"\r\n                },\r\n                \"./async\": {\r\n                    \"browser\": \"./async/index.browser.js\",\r\n                    \"default\": \"./async/index.js\"\r\n                },\r\n                \"./non-secure\": \"./non-secure/index.js\",\r\n                \"./package.json\": \"./package.json\"\r\n            }\r\n           \r\n            \"exports\": {\r\n                \"node\": {\r\n                  \"import\": \"./feature-node.mjs\",\r\n                  \"require\": \"./feature-node.cjs\"\r\n                },\r\n                \"default\": \"./feature.mjs\"\r\n              }\r\n            \r\n            \r\n               \"exports\": {\r\n                \".\": \"./index.js\",\r\n                \"./feature.js\": {\r\n                  \"node\": \"./feature-node.js\",\r\n                  \"default\": \"./feature.js\"\r\n                }\r\n              }\r\n\r\n            \"exports\": {\r\n                \".\": {\r\n                    \"types\": \"./dist/index.d.ts\",\r\n                    \"import\": {\r\n                        \"browser\": {\r\n                            \"development\": \"./dist/composed-offset-position.browser.mjs\",\r\n                            \"default\": \"./dist/composed-offset-position.browser.min.mjs\"\r\n                        },\r\n                        \"default\": \"./dist/composed-offset-position.mjs\"\r\n                    },\r\n                    \"module\": \"./dist/composed-offset-position.esm.js\",\r\n                    \"default\": \"./dist/composed-offset-position.umd.js\"\r\n                },\r\n                \"./package.json\": \"./package.json\"\r\n            }\r\n            \r\n            */\r\n\r\n                /*  \r\n                \"exports\": {\r\n                    \"import\": \"./index-module.js\",\r\n                    \"require\": \"./index-require.cjs\"\r\n                }, \r\n                */\r\n                let getImport = (obj: any) => {\r\n                    if (obj?.browser)\r\n                        return obj.browser;\r\n                    if (obj?.import)\r\n                        return obj.import;\r\n                    if (obj?.module)\r\n                        return obj.module;\r\n                    if (obj?.default)\r\n                        return obj.default;\r\n                    return obj?.node;\r\n                }\r\n                /*\r\n                for support of this:\r\n                \"exports\": {\r\n                \".\": {\r\n                    \"types\": \"./dist/index.d.ts\",\r\n                    \"import\": {\r\n                        \"browser\": {\r\n                            \"development\": \"./dist/composed-offset-position.browser.mjs\",\r\n                            \"default\": \"./dist/composed-offset-position.browser.min.mjs\"\r\n                        },\r\n                */\r\n                let getImportFlat = (obj: any) => {\r\n                    let i = getImport(obj);\r\n                    if (!(typeof i == 'string'))\r\n                        i = getImport(i);\r\n                    if (!(typeof i == 'string'))\r\n                        i = getImport(i);\r\n                    if (!(typeof i == 'string'))\r\n                        i = null;\r\n                    return i;\r\n                }\r\n                //Names to use: browser, import, default, node\r\n                let imp = getImportFlat(packageJsonObj.exports);\r\n                if (imp) {\r\n                    importMap.imports[packageJsonObj.name] = baseUrl + removeLeading(removeLeading(imp, '.'), '/');\r\n                } else if (imp = getImportFlat(packageJsonObj.exports?.['.'])) {\r\n                    importMap.imports[packageJsonObj.name] = baseUrl + removeLeading(removeLeading(imp, '.'), '/');\r\n                }\r\n            }\r\n\r\n            let mainImport = packageJsonObj.main;\r\n            if (packageJsonObj.module)\r\n                mainImport = packageJsonObj.module;\r\n            if (packageJsonObj.unpkg && !mainImport)\r\n                mainImport = packageJsonObj.unpkg;\r\n            if (!importMap.imports[packageJsonObj.name]) {\r\n                if (mainImport)\r\n                    importMap.imports[packageJsonObj.name] = baseUrl + removeLeading(removeLeading(mainImport, '.'), '/');\r\n                else\r\n                    console.warn('package: ' + baseUrl + 'no main import found');\r\n            }\r\n\r\n            importMap.imports[packageJsonObj.name + '/'] = baseUrl;\r\n\r\n            if (NpmPackageLoader.packageHacks[packageJsonObj.name]?.map) {\r\n                for (let h in NpmPackageLoader.packageHacks[packageJsonObj.name]?.map) [\r\n                    importMap.imports[h] = baseUrl + NpmPackageLoader.packageHacks[packageJsonObj.name].map[h]\r\n                ]\r\n            }\r\n\r\n            //@ts-ignore\r\n            importShim.addImportMap(importMap);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/ObservedCustomElementsRegistry.ts",
    "content": "import { IDisposable } from \"../../interfaces/IDisposable.js\";\n\nexport class ObservedCustomElementsRegistry implements IDisposable {\n\n    private _originalCustomElementsRegistry: CustomElementRegistry\n    private _newElements: string[] = [];\n\n    constructor() {\n        this._originalCustomElementsRegistry = window.customElements;\n        \n        const registry: any = {};\n        const originalCustomElementsRegistry = this._originalCustomElementsRegistry\n        const newElements = this._newElements\n        \n        registry.define = function (name, constructor, options) {\n            newElements.push(name);\n            originalCustomElementsRegistry.define(name, constructor, options);\n        }\n        registry.get = function (name) {\n            return originalCustomElementsRegistry.get(name);\n        }\n        registry.upgrade = function (node) {\n            return originalCustomElementsRegistry.upgrade(node);\n        }\n        registry.whenDefined = function (name) {\n            return originalCustomElementsRegistry.whenDefined(name);\n        }\n\n        Object.defineProperty(window, \"customElements\", {\n            get() {\n                return registry;\n            }\n        });\n    }\n\n    dispose(): void {\n        const orgReg = this._originalCustomElementsRegistry;\n        Object.defineProperty(window, \"customElements\", {\n            get() {\n                return orgReg;\n            }\n        });\n    }\n\n    getNewElements(): string[] {\n        const newElements = this._newElements;\n        this._newElements = [];\n        return newElements;\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/PathDataPolyfill.ts",
    "content": "// https://github.com/jarek-foksa/path-data-polyfill\r\n// @info\r\n//   Polyfill for SVG getPathData() and setPathData() methods. Based on:\r\n//   - SVGPathSeg polyfill by Philip Rogers (MIT License)\r\n//     https://github.com/progers/pathseg\r\n//   - SVGPathNormalizer by Tadahisa Motooka (MIT License)\r\n//     https://github.com/motooka/SVGPathNormalizer/tree/master/src\r\n//   - arcToCubicCurves() by Dmitry Baranovskiy (MIT License)\r\n//     https://github.com/DmitryBaranovskiy/raphael/blob/v2.1.1/raphael.core.js#L1837\r\n// @author\r\n//   Jarosław Foksa\r\n// @license\r\n\r\nimport { IPoint } from \"../../interfaces/IPoint.js\";\r\n\r\n//   MIT License\r\nif (!SVGPathElement.prototype.getPathData || !SVGPathElement.prototype.setPathData) {\r\n  (function () {\r\n    var commandsMap = {\r\n      \"Z\": \"Z\", \"M\": \"M\", \"L\": \"L\", \"C\": \"C\", \"Q\": \"Q\", \"A\": \"A\", \"H\": \"H\", \"V\": \"V\", \"S\": \"S\", \"T\": \"T\",\r\n      \"z\": \"Z\", \"m\": \"m\", \"l\": \"l\", \"c\": \"c\", \"q\": \"q\", \"a\": \"a\", \"h\": \"h\", \"v\": \"v\", \"s\": \"s\", \"t\": \"t\"\r\n    };\r\n\r\n    var Source = function (string) {\r\n      //@ts-ignore\r\n      this._string = string;\r\n      //@ts-ignore\r\n      this._currentIndex = 0;\r\n      //@ts-ignore\r\n      this._endIndex = this._string.length;\r\n      //@ts-ignore\r\n      this._prevCommand = null;\r\n      //@ts-ignore\r\n      this._skipOptionalSpaces();\r\n    };\r\n\r\n    Source.prototype = {\r\n      parseSegment: function () {\r\n        var char = this._string[this._currentIndex];\r\n        var command = commandsMap[char] ? commandsMap[char] : null;\r\n\r\n        if (command === null) {\r\n          // Possibly an implicit command. Not allowed if this is the first command.\r\n          if (this._prevCommand === null) {\r\n            return null;\r\n          }\r\n\r\n          // Check for remaining coordinates in the current command.\r\n          if (\r\n            (char === \"+\" || char === \"-\" || char === \".\" || (char >= \"0\" && char <= \"9\")) && this._prevCommand !== \"Z\"\r\n          ) {\r\n            if (this._prevCommand === \"M\") {\r\n              command = \"L\";\r\n            }\r\n            else if (this._prevCommand === \"m\") {\r\n              command = \"l\";\r\n            }\r\n            else {\r\n              command = this._prevCommand;\r\n            }\r\n          }\r\n          else {\r\n            command = null;\r\n          }\r\n\r\n          if (command === null) {\r\n            return null;\r\n          }\r\n        }\r\n        else {\r\n          this._currentIndex += 1;\r\n        }\r\n\r\n        this._prevCommand = command;\r\n\r\n        var values = null;\r\n        var cmd = command.toUpperCase();\r\n\r\n        if (cmd === \"H\" || cmd === \"V\") {\r\n          values = [this._parseNumber()];\r\n        }\r\n        else if (cmd === \"M\" || cmd === \"L\" || cmd === \"T\") {\r\n          values = [this._parseNumber(), this._parseNumber()];\r\n        }\r\n        else if (cmd === \"S\" || cmd === \"Q\") {\r\n          values = [this._parseNumber(), this._parseNumber(), this._parseNumber(), this._parseNumber()];\r\n        }\r\n        else if (cmd === \"C\") {\r\n          values = [\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseNumber()\r\n          ];\r\n        }\r\n        else if (cmd === \"A\") {\r\n          values = [\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseNumber(),\r\n            this._parseArcFlag(),\r\n            this._parseArcFlag(),\r\n            this._parseNumber(),\r\n            this._parseNumber()\r\n          ];\r\n        }\r\n        else if (cmd === \"Z\") {\r\n          this._skipOptionalSpaces();\r\n          values = [];\r\n        }\r\n\r\n        if (values === null || values.indexOf(null) >= 0) {\r\n          // Unknown command or known command with invalid values\r\n          return null;\r\n        }\r\n        else {\r\n          return { type: command, values: values };\r\n        }\r\n      },\r\n\r\n      hasMoreData: function () {\r\n        return this._currentIndex < this._endIndex;\r\n      },\r\n\r\n      peekSegmentType: function () {\r\n        var char = this._string[this._currentIndex];\r\n        return commandsMap[char] ? commandsMap[char] : null;\r\n      },\r\n\r\n      initialCommandIsMoveTo: function () {\r\n        // If the path is empty it is still valid, so return true.\r\n        if (!this.hasMoreData()) {\r\n          return true;\r\n        }\r\n\r\n        var command = this.peekSegmentType();\r\n        // Path must start with moveTo.\r\n        return command === \"M\" || command === \"m\";\r\n      },\r\n\r\n      _isCurrentSpace: function () {\r\n        var char = this._string[this._currentIndex];\r\n        return char <= \" \" && (char === \" \" || char === \"\\n\" || char === \"\\t\" || char === \"\\r\" || char === \"\\f\");\r\n      },\r\n\r\n      _skipOptionalSpaces: function () {\r\n        while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {\r\n          this._currentIndex += 1;\r\n        }\r\n\r\n        return this._currentIndex < this._endIndex;\r\n      },\r\n\r\n      _skipOptionalSpacesOrDelimiter: function () {\r\n        if (\r\n          this._currentIndex < this._endIndex &&\r\n          !this._isCurrentSpace() &&\r\n          this._string[this._currentIndex] !== \",\"\r\n        ) {\r\n          return false;\r\n        }\r\n\r\n        if (this._skipOptionalSpaces()) {\r\n          if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === \",\") {\r\n            this._currentIndex += 1;\r\n            this._skipOptionalSpaces();\r\n          }\r\n        }\r\n        return this._currentIndex < this._endIndex;\r\n      },\r\n\r\n      // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from\r\n      // Source/core/svg/SVGParserUtilities.cpp.\r\n      // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF\r\n      _parseNumber: function () {\r\n        var exponent = 0;\r\n        var integer = 0;\r\n        var frac = 1;\r\n        var decimal = 0;\r\n        var sign = 1;\r\n        var expsign = 1;\r\n        var startIndex = this._currentIndex;\r\n\r\n        this._skipOptionalSpaces();\r\n\r\n        // Read the sign.\r\n        if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === \"+\") {\r\n          this._currentIndex += 1;\r\n        }\r\n        else if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === \"-\") {\r\n          this._currentIndex += 1;\r\n          sign = -1;\r\n        }\r\n\r\n        if (\r\n          this._currentIndex === this._endIndex ||\r\n          (\r\n            (this._string[this._currentIndex] < \"0\" || this._string[this._currentIndex] > \"9\") &&\r\n            this._string[this._currentIndex] !== \".\"\r\n          )\r\n        ) {\r\n          // The first character of a number must be one of [0-9+-.].\r\n          return null;\r\n        }\r\n\r\n        // Read the integer part, build right-to-left.\r\n        var startIntPartIndex = this._currentIndex;\r\n\r\n        while (\r\n          this._currentIndex < this._endIndex &&\r\n          this._string[this._currentIndex] >= \"0\" &&\r\n          this._string[this._currentIndex] <= \"9\"\r\n        ) {\r\n          this._currentIndex += 1; // Advance to first non-digit.\r\n        }\r\n\r\n        if (this._currentIndex !== startIntPartIndex) {\r\n          var scanIntPartIndex = this._currentIndex - 1;\r\n          var multiplier = 1;\r\n\r\n          while (scanIntPartIndex >= startIntPartIndex) {\r\n            integer += multiplier * (this._string[scanIntPartIndex] - <any>\"0\");\r\n            scanIntPartIndex -= 1;\r\n            multiplier *= 10;\r\n          }\r\n        }\r\n\r\n        // Read the decimals.\r\n        if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === \".\") {\r\n          this._currentIndex += 1;\r\n\r\n          // There must be a least one digit following the .\r\n          if (\r\n            this._currentIndex >= this._endIndex ||\r\n            this._string[this._currentIndex] < \"0\" ||\r\n            this._string[this._currentIndex] > \"9\"\r\n          ) {\r\n            return null;\r\n          }\r\n\r\n          while (\r\n            this._currentIndex < this._endIndex &&\r\n            this._string[this._currentIndex] >= \"0\" &&\r\n            this._string[this._currentIndex] <= \"9\"\r\n          ) {\r\n            frac *= 10;\r\n            decimal += (this._string.charAt(this._currentIndex) - <any>\"0\") / frac;\r\n            this._currentIndex += 1;\r\n          }\r\n        }\r\n\r\n        // Read the exponent part.\r\n        if (\r\n          this._currentIndex !== startIndex &&\r\n          this._currentIndex + 1 < this._endIndex &&\r\n          (this._string[this._currentIndex] === \"e\" || this._string[this._currentIndex] === \"E\") &&\r\n          (this._string[this._currentIndex + 1] !== \"x\" && this._string[this._currentIndex + 1] !== \"m\")\r\n        ) {\r\n          this._currentIndex += 1;\r\n\r\n          // Read the sign of the exponent.\r\n          if (this._string[this._currentIndex] === \"+\") {\r\n            this._currentIndex += 1;\r\n          }\r\n          else if (this._string[this._currentIndex] === \"-\") {\r\n            this._currentIndex += 1;\r\n            expsign = -1;\r\n          }\r\n\r\n          // There must be an exponent.\r\n          if (\r\n            this._currentIndex >= this._endIndex ||\r\n            this._string[this._currentIndex] < \"0\" ||\r\n            this._string[this._currentIndex] > \"9\"\r\n          ) {\r\n            return null;\r\n          }\r\n\r\n          while (\r\n            this._currentIndex < this._endIndex &&\r\n            this._string[this._currentIndex] >= \"0\" &&\r\n            this._string[this._currentIndex] <= \"9\"\r\n          ) {\r\n            exponent *= 10;\r\n            exponent += (this._string[this._currentIndex] - <any>\"0\");\r\n            this._currentIndex += 1;\r\n          }\r\n        }\r\n\r\n        var number = integer + decimal;\r\n        number *= sign;\r\n\r\n        if (exponent) {\r\n          number *= Math.pow(10, expsign * exponent);\r\n        }\r\n\r\n        if (startIndex === this._currentIndex) {\r\n          return null;\r\n        }\r\n\r\n        this._skipOptionalSpacesOrDelimiter();\r\n\r\n        return number;\r\n      },\r\n\r\n      _parseArcFlag: function () {\r\n        if (this._currentIndex >= this._endIndex) {\r\n          return null;\r\n        }\r\n\r\n        var flag = null;\r\n        var flagChar = this._string[this._currentIndex];\r\n\r\n        this._currentIndex += 1;\r\n\r\n        if (flagChar === \"0\") {\r\n          flag = 0;\r\n        }\r\n        else if (flagChar === \"1\") {\r\n          flag = 1;\r\n        }\r\n        else {\r\n          return null;\r\n        }\r\n\r\n        this._skipOptionalSpacesOrDelimiter();\r\n        return flag;\r\n      }\r\n    };\r\n\r\n    var parsePathDataString = function (string): PathData[] {\r\n      if (!string || string.length === 0) return [];\r\n\r\n      var source = new Source(string);\r\n      var pathData = [];\r\n\r\n      if (source.initialCommandIsMoveTo()) {\r\n        while (source.hasMoreData()) {\r\n          var pathSeg = source.parseSegment();\r\n\r\n          if (pathSeg === null) {\r\n            break;\r\n          }\r\n          else {\r\n            pathData.push(pathSeg);\r\n          }\r\n        }\r\n      }\r\n\r\n      return pathData;\r\n    }\r\n\r\n    // @info\r\n    //   Get an array of corresponding cubic bezier curve parameters for given arc curve paramters.\r\n    var arcToCubicCurves = function (x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, _recursive?) {\r\n      var degToRad = function (degrees) {\r\n        return (Math.PI * degrees) / 180;\r\n      };\r\n\r\n      var rotate = function (x, y, angleRad) {\r\n        var X = x * Math.cos(angleRad) - y * Math.sin(angleRad);\r\n        var Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);\r\n        return { x: X, y: Y };\r\n      };\r\n\r\n      var angleRad = degToRad(angle);\r\n      var params = [];\r\n      var f1, f2, cx, cy;\r\n\r\n      if (_recursive) {\r\n        f1 = _recursive[0];\r\n        f2 = _recursive[1];\r\n        cx = _recursive[2];\r\n        cy = _recursive[3];\r\n      }\r\n      else {\r\n        var p1 = rotate(x1, y1, -angleRad);\r\n        x1 = p1.x;\r\n        y1 = p1.y;\r\n\r\n        var p2 = rotate(x2, y2, -angleRad);\r\n        x2 = p2.x;\r\n        y2 = p2.y;\r\n\r\n        var x = (x1 - x2) / 2;\r\n        var y = (y1 - y2) / 2;\r\n        var h = (x * x) / (r1 * r1) + (y * y) / (r2 * r2);\r\n\r\n        if (h > 1) {\r\n          h = Math.sqrt(h);\r\n          r1 = h * r1;\r\n          r2 = h * r2;\r\n        }\r\n\r\n        var sign;\r\n\r\n        if (largeArcFlag === sweepFlag) {\r\n          sign = -1;\r\n        }\r\n        else {\r\n          sign = 1;\r\n        }\r\n\r\n        var r1Pow = r1 * r1;\r\n        var r2Pow = r2 * r2;\r\n\r\n        var left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x;\r\n        var right = r1Pow * y * y + r2Pow * x * x;\r\n\r\n        var k = sign * Math.sqrt(Math.abs(left / right));\r\n\r\n        cx = k * r1 * y / r2 + (x1 + x2) / 2;\r\n        cy = k * -r2 * x / r1 + (y1 + y2) / 2;\r\n\r\n        f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9)));\r\n        f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9)));\r\n\r\n        if (x1 < cx) {\r\n          f1 = Math.PI - f1;\r\n        }\r\n        if (x2 < cx) {\r\n          f2 = Math.PI - f2;\r\n        }\r\n\r\n        if (f1 < 0) {\r\n          f1 = Math.PI * 2 + f1;\r\n        }\r\n        if (f2 < 0) {\r\n          f2 = Math.PI * 2 + f2;\r\n        }\r\n\r\n        if (sweepFlag && f1 > f2) {\r\n          f1 = f1 - Math.PI * 2;\r\n        }\r\n        if (!sweepFlag && f2 > f1) {\r\n          f2 = f2 - Math.PI * 2;\r\n        }\r\n      }\r\n\r\n      var df = f2 - f1;\r\n\r\n      if (Math.abs(df) > (Math.PI * 120 / 180)) {\r\n        var f2old = f2;\r\n        var x2old = x2;\r\n        var y2old = y2;\r\n\r\n        if (sweepFlag && f2 > f1) {\r\n          f2 = f1 + (Math.PI * 120 / 180) * (1);\r\n        }\r\n        else {\r\n          f2 = f1 + (Math.PI * 120 / 180) * (-1);\r\n        }\r\n\r\n        x2 = cx + r1 * Math.cos(f2);\r\n        y2 = cy + r2 * Math.sin(f2);\r\n        params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]);\r\n      }\r\n\r\n      df = f2 - f1;\r\n\r\n      var c1 = Math.cos(f1);\r\n      var s1 = Math.sin(f1);\r\n      var c2 = Math.cos(f2);\r\n      var s2 = Math.sin(f2);\r\n      var t = Math.tan(df / 4);\r\n      var hx = 4 / 3 * r1 * t;\r\n      var hy = 4 / 3 * r2 * t;\r\n\r\n      var m1 = [x1, y1];\r\n      var m2 = [x1 + hx * s1, y1 - hy * c1];\r\n      var m3 = [x2 + hx * s2, y2 - hy * c2];\r\n      var m4 = [x2, y2];\r\n\r\n      m2[0] = 2 * m1[0] - m2[0];\r\n      m2[1] = 2 * m1[1] - m2[1];\r\n\r\n      if (_recursive) {\r\n        return [m2, m3, m4].concat(params);\r\n      }\r\n      else {\r\n        params = [m2, m3, m4].concat(params);\r\n\r\n        var curves = [];\r\n\r\n        for (var i = 0; i < params.length; i += 3) {\r\n          let r1 = rotate(params[i][0], params[i][1], angleRad);\r\n          let r2 = rotate(params[i + 1][0], params[i + 1][1], angleRad);\r\n          let r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad);\r\n          curves.push([r1.x, r1.y, r2.x, r2.y, r3.x, r3.y]);\r\n        }\r\n\r\n        return curves;\r\n      }\r\n    };\r\n\r\n    /*var clonePathData = function (pathData) {\r\n      return pathData.map(function (seg) {\r\n        return { type: seg.type, values: Array.prototype.slice.call(seg.values) }\r\n      });\r\n    };*/\r\n\r\n    // @info\r\n    //   Takes any path data, returns path data that consists only from absolute commands.\r\n    var absolutizePathData = function (pathData) {\r\n      var absolutizedPathData = [];\r\n\r\n      var currentX = null;\r\n      var currentY = null;\r\n\r\n      var subpathX = null;\r\n      var subpathY = null;\r\n\r\n      pathData.forEach(function (seg) {\r\n        var type = seg.type;\r\n\r\n        if (type === \"M\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1];\r\n\r\n          absolutizedPathData.push({ type: \"M\", values: [x, y] });\r\n\r\n          subpathX = x;\r\n          subpathY = y;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"m\") {\r\n          var x = currentX + seg.values[0];\r\n          var y = currentY + seg.values[1];\r\n\r\n          absolutizedPathData.push({ type: \"M\", values: [x, y] });\r\n\r\n          subpathX = x;\r\n          subpathY = y;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"L\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1];\r\n\r\n          absolutizedPathData.push({ type: \"L\", values: [x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"l\") {\r\n          var x = currentX + seg.values[0];\r\n          var y = currentY + seg.values[1];\r\n\r\n          absolutizedPathData.push({ type: \"L\", values: [x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"C\") {\r\n          var x1 = seg.values[0];\r\n          var y1 = seg.values[1];\r\n          var x2 = seg.values[2];\r\n          var y2 = seg.values[3];\r\n          var x = seg.values[4];\r\n          var y = seg.values[5];\r\n\r\n          absolutizedPathData.push({ type: \"C\", values: [x1, y1, x2, y2, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"c\") {\r\n          var x1 = currentX + seg.values[0];\r\n          var y1 = currentY + seg.values[1];\r\n          var x2 = currentX + seg.values[2];\r\n          var y2 = currentY + seg.values[3];\r\n          var x = currentX + seg.values[4];\r\n          var y = currentY + seg.values[5];\r\n\r\n          absolutizedPathData.push({ type: \"C\", values: [x1, y1, x2, y2, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"Q\") {\r\n          var x1 = seg.values[0];\r\n          var y1 = seg.values[1];\r\n          var x = seg.values[2];\r\n          var y = seg.values[3];\r\n\r\n          absolutizedPathData.push({ type: \"Q\", values: [x1, y1, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"q\") {\r\n          var x1 = currentX + seg.values[0];\r\n          var y1 = currentY + seg.values[1];\r\n          var x = currentX + seg.values[2];\r\n          var y = currentY + seg.values[3];\r\n\r\n          absolutizedPathData.push({ type: \"Q\", values: [x1, y1, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"A\") {\r\n          var x = seg.values[5];\r\n          var y = seg.values[6];\r\n\r\n          absolutizedPathData.push({\r\n            type: \"A\",\r\n            values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]\r\n          });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"a\") {\r\n          var x = currentX + seg.values[5];\r\n          var y = currentY + seg.values[6];\r\n\r\n          absolutizedPathData.push({\r\n            type: \"A\",\r\n            values: [seg.values[0], seg.values[1], seg.values[2], seg.values[3], seg.values[4], x, y]\r\n          });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"H\") {\r\n          var x = seg.values[0];\r\n          absolutizedPathData.push({ type: \"H\", values: [x] });\r\n          currentX = x;\r\n        }\r\n\r\n        else if (type === \"h\") {\r\n          var x = currentX + seg.values[0];\r\n          absolutizedPathData.push({ type: \"H\", values: [x] });\r\n          currentX = x;\r\n        }\r\n\r\n        else if (type === \"V\") {\r\n          var y = seg.values[0];\r\n          absolutizedPathData.push({ type: \"V\", values: [y] });\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"v\") {\r\n          var y = currentY + seg.values[0];\r\n          absolutizedPathData.push({ type: \"V\", values: [y] });\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"S\") {\r\n          var x2 = seg.values[0];\r\n          var y2 = seg.values[1];\r\n          var x = seg.values[2];\r\n          var y = seg.values[3];\r\n\r\n          absolutizedPathData.push({ type: \"S\", values: [x2, y2, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"s\") {\r\n          var x2 = currentX + seg.values[0];\r\n          var y2 = currentY + seg.values[1];\r\n          var x = currentX + seg.values[2];\r\n          var y = currentY + seg.values[3];\r\n\r\n          absolutizedPathData.push({ type: \"S\", values: [x2, y2, x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"T\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1]\r\n\r\n          absolutizedPathData.push({ type: \"T\", values: [x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"t\") {\r\n          var x = currentX + seg.values[0];\r\n          var y = currentY + seg.values[1]\r\n\r\n          absolutizedPathData.push({ type: \"T\", values: [x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (type === \"Z\" || type === \"z\") {\r\n          absolutizedPathData.push({ type: \"Z\", values: [] });\r\n\r\n          currentX = subpathX;\r\n          currentY = subpathY;\r\n        }\r\n      });\r\n\r\n      return absolutizedPathData;\r\n    };\r\n\r\n    // @info\r\n    //   Takes path data that consists only from absolute commands, returns path data that consists only from\r\n    //   \"M\", \"L\", \"C\" and \"Z\" commands.\r\n    var reducePathData = function (pathData) {\r\n      var reducedPathData = [];\r\n      var lastType = null;\r\n\r\n      var lastControlX = null;\r\n      var lastControlY = null;\r\n\r\n      var currentX = null;\r\n      var currentY = null;\r\n\r\n      var subpathX = null;\r\n      var subpathY = null;\r\n\r\n      pathData.forEach(function (seg) {\r\n        if (seg.type === \"M\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1];\r\n\r\n          reducedPathData.push({ type: \"M\", values: [x, y] });\r\n\r\n          subpathX = x;\r\n          subpathY = y;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"C\") {\r\n          var x1 = seg.values[0];\r\n          var y1 = seg.values[1];\r\n          var x2 = seg.values[2];\r\n          var y2 = seg.values[3];\r\n          var x = seg.values[4];\r\n          var y = seg.values[5];\r\n\r\n          reducedPathData.push({ type: \"C\", values: [x1, y1, x2, y2, x, y] });\r\n\r\n          lastControlX = x2;\r\n          lastControlY = y2;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"L\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1];\r\n\r\n          reducedPathData.push({ type: \"L\", values: [x, y] });\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"H\") {\r\n          var x = seg.values[0];\r\n\r\n          reducedPathData.push({ type: \"L\", values: [x, currentY] });\r\n\r\n          currentX = x;\r\n        }\r\n\r\n        else if (seg.type === \"V\") {\r\n          var y = seg.values[0];\r\n\r\n          reducedPathData.push({ type: \"L\", values: [currentX, y] });\r\n\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"S\") {\r\n          var x2 = seg.values[0];\r\n          var y2 = seg.values[1];\r\n          var x = seg.values[2];\r\n          var y = seg.values[3];\r\n\r\n          var cx1, cy1;\r\n\r\n          if (lastType === \"C\" || lastType === \"S\") {\r\n            cx1 = currentX + (currentX - lastControlX);\r\n            cy1 = currentY + (currentY - lastControlY);\r\n          }\r\n          else {\r\n            cx1 = currentX;\r\n            cy1 = currentY;\r\n          }\r\n\r\n          reducedPathData.push({ type: \"C\", values: [cx1, cy1, x2, y2, x, y] });\r\n\r\n          lastControlX = x2;\r\n          lastControlY = y2;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"T\") {\r\n          var x = seg.values[0];\r\n          var y = seg.values[1];\r\n\r\n          var x1, y1;\r\n\r\n          if (lastType === \"Q\" || lastType === \"T\") {\r\n            x1 = currentX + (currentX - lastControlX);\r\n            y1 = currentY + (currentY - lastControlY);\r\n          }\r\n          else {\r\n            x1 = currentX;\r\n            y1 = currentY;\r\n          }\r\n\r\n          var cx1 = currentX + 2 * (x1 - currentX) / 3;\r\n          var cy1 = currentY + 2 * (y1 - currentY) / 3;\r\n          var cx2 = x + 2 * (x1 - x) / 3;\r\n          var cy2 = y + 2 * (y1 - y) / 3;\r\n\r\n          reducedPathData.push({ type: \"C\", values: [cx1, cy1, cx2, cy2, x, y] });\r\n\r\n          lastControlX = x1;\r\n          lastControlY = y1;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"Q\") {\r\n          var x1 = seg.values[0];\r\n          var y1 = seg.values[1];\r\n          var x = seg.values[2];\r\n          var y = seg.values[3];\r\n\r\n          var cx1 = currentX + 2 * (x1 - currentX) / 3;\r\n          var cy1 = currentY + 2 * (y1 - currentY) / 3;\r\n          var cx2 = x + 2 * (x1 - x) / 3;\r\n          var cy2 = y + 2 * (y1 - y) / 3;\r\n\r\n          reducedPathData.push({ type: \"C\", values: [cx1, cy1, cx2, cy2, x, y] });\r\n\r\n          lastControlX = x1;\r\n          lastControlY = y1;\r\n\r\n          currentX = x;\r\n          currentY = y;\r\n        }\r\n\r\n        else if (seg.type === \"A\") {\r\n          let r1 = Math.abs(seg.values[0]);\r\n          let r2 = Math.abs(seg.values[1]);\r\n          var angle = seg.values[2];\r\n          var largeArcFlag = seg.values[3];\r\n          var sweepFlag = seg.values[4];\r\n          var x = seg.values[5];\r\n          var y = seg.values[6];\r\n\r\n          if (r1 === 0 || r2 === 0) {\r\n            reducedPathData.push({ type: \"C\", values: [currentX, currentY, x, y, x, y] });\r\n\r\n            currentX = x;\r\n            currentY = y;\r\n          }\r\n          else {\r\n            if (currentX !== x || currentY !== y) {\r\n              var curves = arcToCubicCurves(currentX, currentY, x, y, r1, r2, angle, largeArcFlag, sweepFlag);\r\n\r\n              curves.forEach(function (curve) {\r\n                reducedPathData.push({ type: \"C\", values: curve });\r\n              });\r\n\r\n              currentX = x;\r\n              currentY = y;\r\n            }\r\n          }\r\n        }\r\n\r\n        else if (seg.type === \"Z\") {\r\n          reducedPathData.push(seg);\r\n\r\n          currentX = subpathX;\r\n          currentY = subpathY;\r\n        }\r\n\r\n        lastType = seg.type;\r\n      });\r\n\r\n      return reducedPathData;\r\n    };\r\n\r\n    SVGPathElement.prototype.getPathData = function (options) {\r\n      if (options && options.normalize) {\r\n        /*if (this[$cachedNormalizedPathData]) {\r\n          return clonePathData(this[$cachedNormalizedPathData]);\r\n        }\r\n        else */ {\r\n          let pathData;\r\n\r\n          /*if (this[$cachedPathData]) {\r\n            pathData = clonePathData(this[$cachedPathData]);\r\n          }\r\n          else */{\r\n            pathData = parsePathDataString(this.getAttribute(\"d\") || \"\");\r\n            //this[$cachedPathData] = clonePathData(pathData);\r\n          }\r\n\r\n          let normalizedPathData = reducePathData(absolutizePathData(pathData));\r\n          //this[$cachedNormalizedPathData] = clonePathData(normalizedPathData);\r\n          return normalizedPathData;\r\n        }\r\n      }\r\n      else {\r\n        /*if (this[$cachedPathData]) {\r\n          return clonePathData(this[$cachedPathData]);\r\n        }\r\n        else*/ {\r\n          let pathData = parsePathDataString(this.getAttribute(\"d\") || \"\");\r\n          //this[$cachedPathData] = clonePathData(pathData);\r\n          return pathData;\r\n        }\r\n      }\r\n    };\r\n\r\n    SVGPathElement.prototype.setPathData = function (pathData) {\r\n      if (pathData.length === 0) {\r\n        this.removeAttribute(\"d\");\r\n      }\r\n      else {\r\n        let d = \"\";\r\n\r\n        for (let i = 0, l = pathData.length; i < l; i += 1) {\r\n          let seg: any = pathData[i];\r\n\r\n          if (i > 0) {\r\n            d += \" \";\r\n          }\r\n\r\n          d += seg.type;\r\n\r\n          if (seg.values && seg.values.length > 0) {\r\n            d += \" \" + seg.values.join(\" \");\r\n          }\r\n        }\r\n\r\n        this.setAttribute(\"d\", d);\r\n      }\r\n    };\r\n\r\n    SVGRectElement.prototype.getPathData = function (options) {\r\n      var x = this.x.baseVal.value;\r\n      var y = this.y.baseVal.value;\r\n      var width = this.width.baseVal.value;\r\n      var height = this.height.baseVal.value;\r\n      var rx = this.hasAttribute(\"rx\") ? this.rx.baseVal.value : this.ry.baseVal.value;\r\n      var ry = this.hasAttribute(\"ry\") ? this.ry.baseVal.value : this.rx.baseVal.value;\r\n\r\n      if (rx > width / 2) {\r\n        rx = width / 2;\r\n      }\r\n\r\n      if (ry > height / 2) {\r\n        ry = height / 2;\r\n      }\r\n\r\n      var pathData: any = [\r\n        { type: \"M\", values: [x + rx, y] },\r\n        { type: \"H\", values: [x + width - rx] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, x + width, y + ry] },\r\n        { type: \"V\", values: [y + height - ry] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, x + width - rx, y + height] },\r\n        { type: \"H\", values: [x + rx] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, x, y + height - ry] },\r\n        { type: \"V\", values: [y + ry] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, x + rx, y] },\r\n        { type: \"Z\", values: [] }\r\n      ];\r\n\r\n      // Get rid of redundant \"A\" segs when either rx or ry is 0\r\n      pathData = pathData.filter(function (s) {\r\n        return s.type === \"A\" && (s.values[0] === 0 || s.values[1] === 0) ? false : true;\r\n      });\r\n\r\n      if (options && options.normalize === true) {\r\n        pathData = reducePathData(pathData);\r\n      }\r\n\r\n      return pathData;\r\n    };\r\n\r\n    SVGCircleElement.prototype.getPathData = function (options) {\r\n      var cx = this.cx.baseVal.value;\r\n      var cy = this.cy.baseVal.value;\r\n      var r = this.r.baseVal.value;\r\n\r\n      var pathData: any = [\r\n        { type: \"M\", values: [cx + r, cy] },\r\n        { type: \"A\", values: [r, r, 0, 0, 1, cx, cy + r] },\r\n        { type: \"A\", values: [r, r, 0, 0, 1, cx - r, cy] },\r\n        { type: \"A\", values: [r, r, 0, 0, 1, cx, cy - r] },\r\n        { type: \"A\", values: [r, r, 0, 0, 1, cx + r, cy] },\r\n        { type: \"Z\", values: [] }\r\n      ];\r\n\r\n      if (options && options.normalize === true) {\r\n        pathData = reducePathData(pathData);\r\n      }\r\n\r\n      return pathData;\r\n    };\r\n\r\n    SVGEllipseElement.prototype.getPathData = function (options) {\r\n      var cx = this.cx.baseVal.value;\r\n      var cy = this.cy.baseVal.value;\r\n      var rx = this.rx.baseVal.value;\r\n      var ry = this.ry.baseVal.value;\r\n\r\n      var pathData: any = [\r\n        { type: \"M\", values: [cx + rx, cy] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, cx, cy + ry] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, cx - rx, cy] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, cx, cy - ry] },\r\n        { type: \"A\", values: [rx, ry, 0, 0, 1, cx + rx, cy] },\r\n        { type: \"Z\", values: [] }\r\n      ];\r\n\r\n      if (options && options.normalize === true) {\r\n        pathData = reducePathData(pathData);\r\n      }\r\n\r\n      return pathData;\r\n    };\r\n\r\n    SVGLineElement.prototype.getPathData = function () {\r\n      return <any>[\r\n        { type: \"M\", values: [this.x1.baseVal.value, this.y1.baseVal.value] },\r\n        { type: \"L\", values: [this.x2.baseVal.value, this.y2.baseVal.value] }\r\n      ];\r\n    };\r\n\r\n    SVGPolylineElement.prototype.getPathData = function () {\r\n      var pathData = [];\r\n\r\n      for (var i = 0; i < this.points.numberOfItems; i += 1) {\r\n        var point = this.points.getItem(i);\r\n\r\n        pathData.push({\r\n          type: (i === 0 ? \"M\" : \"L\"),\r\n          values: [point.x, point.y]\r\n        });\r\n      }\r\n\r\n      return pathData;\r\n    };\r\n\r\n    SVGPolygonElement.prototype.getPathData = function () {\r\n      var pathData = [];\r\n\r\n      for (var i = 0; i < this.points.numberOfItems; i += 1) {\r\n        var point = this.points.getItem(i);\r\n\r\n        pathData.push({\r\n          type: (i === 0 ? \"M\" : \"L\"),\r\n          values: [point.x, point.y]\r\n        });\r\n      }\r\n\r\n      pathData.push({\r\n        type: \"Z\",\r\n        values: []\r\n      });\r\n\r\n      return pathData;\r\n    };\r\n  })();\r\n}\r\n\r\nexport { }\r\n\r\nexport declare type PathDataM = { type: 'M' | 'm', values: [x: number, y: number] }\r\nexport declare type PathDataL = { type: 'L' | 'l', values: [x: number, y: number] }\r\nexport declare type PathDataT = { type: 'T' | 't', values: [x: number, y: number] }\r\nexport declare type PathDataH = { type: 'H' | 'h', values: [x: number] }\r\nexport declare type PathDataV = { type: 'V' | 'v', values: [y: number] }\r\nexport declare type PathDataZ = { type: 'Z' | 'z', values?: [] }\r\nexport declare type PathDataC = { type: 'C' | 'c', values: [x1: number, y1: number, x2: number, y2: number, x: number, y: number] }\r\nexport declare type PathDataS = { type: 'S' | 's', values: [x2: number, y2: number, x: number, y: number] }\r\nexport declare type PathDataQ = { type: 'Q' | 'q', values: [x1: number, y1: number, x: number, y: number] }\r\nexport declare type PathDataA = { type: 'A' | 'a', values: [rx: number, ry: number, ang: number, flag1: 0 | 1, flag2: 0 | 1, x: number, y: number] }\r\nexport declare type PathData = { type: string } & (PathDataM | PathDataL | PathDataH | PathDataV | PathDataZ | PathDataC | PathDataS | PathDataQ | PathDataT | PathDataA)[];\r\n\r\nexport function straightenLine(p1: IPoint, p2: IPoint, stepDeg = 45): IPoint {\r\n  const alpha = calculateAlpha(p1, p2);\r\n  const snapped = Math.round(alpha / stepDeg) * stepDeg;\r\n\r\n  const angle = (snapped + 360) % 360;\r\n\r\n  const rad = angle * Math.PI / 180;\r\n\r\n  const dx = p2.x - p1.x;\r\n  const dy = p2.y - p1.y;\r\n\r\n  const length = Math.sqrt(dx * dx + dy * dy);\r\n\r\n  const newX = p1.x + Math.cos(rad) * length;\r\n  const newY = p1.y - Math.sin(rad) * length;\r\n\r\n  return { x: newX, y: newY };\r\n}\r\n\r\nexport function calculateNormLegth(p1: IPoint, p2: IPoint): number {\r\n  let normLenght;\r\n  let currentLength = Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));\r\n  let alpha = calculateAlpha(p1, p2);\r\n  let beta = alpha - ((Math.floor(alpha / 90) * 90) + 45);\r\n  normLenght = currentLength * Math.cos(beta * (Math.PI / 180)) / Math.sqrt(2);\r\n\r\n  return normLenght;\r\n}\r\n\r\nexport function calculateAlpha(p1: IPoint, p2: IPoint): number {\r\n  let alpha = - 1 * Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;\r\n  if (alpha < 0)\r\n    alpha += 360;\r\n\r\n  return alpha;\r\n}\r\n\r\nexport function interpolateLinePoints(p1: IPoint, p2: IPoint, maxDistance: number): IPoint[] {\r\n  const dx = p2.x - p1.x;\r\n  const dy = p2.y - p1.y;\r\n  const distance = Math.sqrt(dx * dx + dy * dy);\r\n\r\n  if (distance === 0) {\r\n    return [];\r\n  }\r\n\r\n  if (maxDistance <= 0) {\r\n    return [p2];\r\n  }\r\n\r\n  const segments = Math.max(1, Math.ceil(distance / maxDistance));\r\n  const points: IPoint[] = [];\r\n\r\n  for (let i = 1; i <= segments; i++) {\r\n    points.push({\r\n      x: p1.x + dx * i / segments,\r\n      y: p1.y + dy * i / segments\r\n    });\r\n  }\r\n\r\n  return points;\r\n}\r\n\r\nexport function moveSVGPath(path: SVGPathElement, xFactor: number, yFactor: number): string {\r\n  let newPathData = \"\";\r\n  let pd = path.getPathData({ normalize: true });\r\n  {\r\n    for (let p of pd) {\r\n      switch (p.type) {\r\n        case ('M'):\r\n        case ('m'):\r\n        case ('L'):\r\n        case ('l'):\r\n        case ('T'):\r\n        case ('t'):\r\n          newPathData += p.type + \" \" + (p.values[0] - xFactor) + \" \" + (p.values[1] - yFactor) + \" \";\r\n          break;\r\n        case ('Z'):\r\n        case ('z'):\r\n          newPathData += p.type + \" \";\r\n          break;\r\n        case ('C'):\r\n        case ('c'):\r\n          newPathData += p.type + \" \" + (p.values[0] - xFactor) + \" \" + (p.values[1] - yFactor) + \" \" + (p.values[2] - xFactor) + \" \" + (p.values[3] - yFactor) + \" \" + (p.values[4] - xFactor) + \" \" + (p.values[5] - yFactor) + \" \";\r\n          break;\r\n        case ('S'):\r\n        case ('s'):\r\n        case ('Q'):\r\n        case ('q'):\r\n          newPathData += p.type + \" \" + (p.values[0] - xFactor) + \" \" + (p.values[1] - yFactor) + \" \" + (p.values[2] - xFactor) + \" \" + (p.values[3] - yFactor) + \" \";\r\n          break;\r\n        case ('A'):\r\n        case ('a'):\r\n          newPathData += p.type + \" \" + (p.values[0] - xFactor) + \" \" + (p.values[1] - yFactor) + \" \" + p.values[2] + \" \" + p.values[3] + \" \" + p.values[4] + \" \" + (p.values[5] - xFactor) + \" \" + (p.values[6] - yFactor) + \" \";\r\n          break;\r\n      }\r\n    }\r\n  }\r\n  return newPathData;\r\n}\r\n\r\nexport function createPathD(path: PathData[]) {\r\n  let pathD: string = \"\";\r\n  for (let p of path) {\r\n    pathD += p.type + \" \";\r\n    for (var i = 0; i < p.values.length; i++) {\r\n      if (p.values[i] != null && !isNaN(p.values[i])) {\r\n        pathD += p.values[i] + \" \";\r\n      }\r\n    }\r\n  }\r\n  return pathD;\r\n}\r\n\r\ndeclare global {\r\n  interface SVGGraphicsElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n    isPointInStroke(point: { x: number, y: number })\r\n    isPointInFill(point: { x: number, y: number })\r\n  }\r\n  interface SVGPathElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n    setPathData(pathData: PathData[])\r\n  }\r\n  interface SVGRectElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n  }\r\n  interface SVGCircleElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n  }\r\n  interface SVGEllipseElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n  }\r\n  interface SVGLineElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n\r\n  }interface SVGPolylineElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n  }\r\n  interface SVGPolygonElement {\r\n    getPathData(options?: { normalize?: boolean }): PathData[]\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/PopupHelper.ts",
    "content": "export function showPopup(content: Element, anchorEl: HTMLElement, closedCallback?: () => void): () => void {\n    const root = anchorEl.getRootNode();\n\n    // 🧱 Create popup element\n    const popupEl = document.createElement('div');\n\n    // Enable Popup API\n    popupEl.popover = 'auto';\n\n    // 📦 Insert content\n    if (typeof content === 'string') {\n        popupEl.innerHTML = content;\n    } else {\n        popupEl.append(content);\n    }\n\n    // 📍 Mount into SAME root (required for anchor positioning)\n    if (root instanceof ShadowRoot) {\n        root.appendChild(popupEl);\n    } else {\n        document.body.appendChild(popupEl);\n    }\n\n    Object.assign(popupEl.style, {\n        positionArea: 'right bottom',\n        positionTryFallbacks: `\n            flip-block,\n            flip-inline,\n            top,\n            bottom,\n            left,\n            right\n        `\n    });\n\n    // 🧹 cleanup when closed\n    popupEl.addEventListener('toggle', () => {\n        if (!popupEl.matches(':popover-open')) {\n            popupEl.remove();\n            closedCallback?.();\n        }\n    }, { once: true });\n\n    //@ts-ignore\n    // 🚀 Open via Popup API\n    popupEl.showPopover({ source: anchorEl });\n\n    // 🔙 return close callback\n    return () => {\n        if (popupEl.matches(':popover-open')) {\n            popupEl.hidePopover();\n        }\n        popupEl.remove();\n        closedCallback?.();\n    };\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/QuadEdgeHandleHelper.ts",
    "content": "import { IPoint } from '../../interfaces/IPoint.js';\n\nfunction normalize(vector: IPoint) {\n  const length = Math.hypot(vector.x, vector.y);\n  if (length < 1e-8) {\n    return { x: 0, y: 0 };\n  }\n  return { x: vector.x / length, y: vector.y / length };\n}\n\nexport function getQuadCenter(quad: DOMQuad): IPoint {\n  return {\n    x: (quad.p1.x + quad.p2.x + quad.p3.x + quad.p4.x) / 4,\n    y: (quad.p1.y + quad.p2.y + quad.p3.y + quad.p4.y) / 4\n  };\n}\n\nexport function getEdgeMidpoint(start: IPoint, end: IPoint): IPoint {\n  return {\n    x: (start.x + end.x) / 2,\n    y: (start.y + end.y) / 2\n  };\n}\n\nexport function getOutwardNormal(start: IPoint, end: IPoint, quadCenter: IPoint, fallback: IPoint): IPoint {\n  const dx = end.x - start.x;\n  const dy = end.y - start.y;\n  let normal = normalize({ x: -dy, y: dx });\n  if (normal.x === 0 && normal.y === 0) {\n    normal = normalize(fallback);\n  }\n\n  const midpoint = getEdgeMidpoint(start, end);\n  const toCenter = { x: quadCenter.x - midpoint.x, y: quadCenter.y - midpoint.y };\n  if ((normal.x * toCenter.x) + (normal.y * toCenter.y) > 0) {\n    normal = { x: -normal.x, y: -normal.y };\n  }\n  return normal;\n}\n\nexport function getEdgeOffsetPoint(start: IPoint, end: IPoint, quadCenter: IPoint, distance: number, fallback: IPoint): IPoint {\n  const midpoint = getEdgeMidpoint(start, end);\n  const normal = getOutwardNormal(start, end, quadCenter, fallback);\n  return {\n    x: midpoint.x + (normal.x * distance),\n    y: midpoint.y + (normal.y * distance)\n  };\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/Screenshot.ts",
    "content": "import { getBoundingClientRectAlsoForDisplayContents } from \"./ElementHelper.js\";\r\n\r\n// for screenshots to be genrated properly, you need to select the current tab only in media source selector\r\nexport class Screenshot {\r\n  private static _canvas: HTMLCanvasElement;\r\n  private static _context: CanvasRenderingContext2D;\r\n  private static _video: HTMLVideoElement;\r\n  private static _captureStream: MediaStream;\r\n\r\n  private static _disableStream() {\r\n    Screenshot._captureStream.getTracks().forEach(track => track.stop());\r\n    Screenshot._canvas = null;\r\n  }\r\n\r\n  static get screenshotsEnabled() {\r\n    return Screenshot._captureStream && Screenshot._captureStream.active;\r\n  }\r\n\r\n  static async enableScreenshots(elementHostForVideo: Element = document.body) {\r\n    if (Screenshot._captureStream && !Screenshot._captureStream.active) {\r\n      Screenshot._disableStream();\r\n    }\r\n    if (Screenshot._canvas == null) {\r\n      Screenshot._canvas = document.createElement(\"canvas\");\r\n      Screenshot._context = Screenshot._canvas.getContext(\"2d\");\r\n      Screenshot._video = document.createElement(\"video\");\r\n      const gdmOptions = {\r\n        video: {\r\n          cursor: \"never\",\r\n          displaySurface: 'browser'\r\n        },\r\n        audio: false,\r\n        selfBrowserSurface: \"include\",\r\n        preferCurrentTab: true\r\n      }\r\n      Screenshot._video.style.display = \"none\";\r\n      elementHostForVideo.appendChild(Screenshot._video);\r\n      try {\r\n        //@ts-ignore\r\n        Screenshot._captureStream = await navigator.mediaDevices.getDisplayMedia(gdmOptions);\r\n      } catch (e) {\r\n        Screenshot._canvas = null;\r\n        throw e;\r\n      }\r\n      //@ts-ignore\r\n      const captureType = Screenshot._captureStream.getVideoTracks()[0].getSettings().displaySurface\r\n      if (captureType != 'browser') {\r\n        Screenshot._disableStream();\r\n        alert('You need to share the current Tab, for the screenshot API to work');\r\n        throw 'You need to share the current Tab, for the screenshot API to work';\r\n      }\r\n      Screenshot._video.srcObject = Screenshot._captureStream;\r\n      Screenshot._video.play();\r\n      await Screenshot._sleep(2000);\r\n    }\r\n  }\r\n\r\n  static async takeScreenshot(element: Element, width: number = 100, height: number = 100, elementHostForVideo: Element = document.body): Promise<string> {\r\n    await Screenshot.enableScreenshots(elementHostForVideo);\r\n    const rect = getBoundingClientRectAlsoForDisplayContents(element);\r\n    Screenshot._canvas.width = width;\r\n    Screenshot._canvas.height = height;\r\n    Screenshot._context.drawImage(Screenshot._video, 0, 0, 1, 1, 0, 0, width, height);\r\n    const factorX = Screenshot._video.videoWidth / window.innerWidth;\r\n    const factorY = Screenshot._video.videoHeight / window.innerHeight;\r\n    Screenshot._context.drawImage(Screenshot._video, rect.left * factorX, rect.top * factorY, rect.width * factorX, rect.height * factorY, 0, 0, width, height);\r\n    const frame = Screenshot._canvas.toDataURL(\"image/png\");\r\n    return frame;\r\n  }\r\n\r\n  private static _sleep(timeout: number) {\r\n    let resolve = null\r\n    const promise = new Promise(r => resolve = r)\r\n    window.setTimeout(resolve, timeout)\r\n    return promise\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/SelectionHelper.ts",
    "content": "export function shadowrootGetSelection(shadowRoot: ShadowRoot): Selection | ArrayLike<StaticRange> | null {\n    let selection = document.getSelection();\n    if ((<any>shadowRoot).getSelection)\n        selection = (<any>shadowRoot).getSelection()\n    else if ((<any>selection).getComposedRanges)\n        selection = (<any>selection).getComposedRanges(shadowRoot);\n    return selection;\n}\n\nfunction wrapTextNodesInSpan(range: Range, spans: HTMLSpanElement[]) {\n    function wrapNode(node: Node) {\n        const parent = node.parentNode;\n        if (!parent)\n            return;\n\n        const span = document.createElement('span');\n        spans.push(span);\n        parent.insertBefore(span, node);\n        span.appendChild(node);\n    }\n\n    function canReuseSpan(node: Node): node is HTMLSpanElement {\n        return node instanceof HTMLSpanElement && Array.from(node.childNodes).every(x => x.nodeType === Node.TEXT_NODE);\n    }\n\n    function processNode(node: Node) {\n        switch (node.nodeType) {\n            case Node.TEXT_NODE:\n                wrapNode(node);\n                break;\n            case Node.DOCUMENT_FRAGMENT_NODE:\n            case Node.ELEMENT_NODE:\n                if (canReuseSpan(node)) {\n                    spans.push(node);\n                    break;\n                }\n                Array.from(node.childNodes).forEach(processNode);\n                break;\n        }\n    }\n\n    const fragment = range.extractContents();\n    processNode(fragment);\n    range.insertNode(fragment);\n}\n\nfunction staticRangeToRange(staticRange: StaticRange) {\n    const range = document.createRange();\n\n    range.setStart(staticRange.startContainer, staticRange.startOffset);\n    range.setEnd(staticRange.endContainer, staticRange.endOffset);\n\n    return range;\n}\n\nexport function wrapSelectionInSpans(selection: Selection | ArrayLike<StaticRange>) {\n    const spans: HTMLSpanElement[] = [];\n\n    if ('getRangeAt' in selection) {\n        if (!selection.rangeCount)\n            return spans;\n\n        const range = selection.getRangeAt(0);\n        wrapTextNodesInSpan(range, spans);\n    } else {\n        const staticRange = selection[0];\n        if (!staticRange)\n            return spans;\n\n        wrapTextNodesInSpan(staticRangeToRange(staticRange), spans);\n    }\n    if ('removeAllRanges' in selection && selection.removeAllRanges)\n        selection.removeAllRanges();\n\n    return spans;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/SimpleTextWriter.ts",
    "content": "import { ITextWriter } from './ITextWriter.js';\r\n\r\nexport class SimpleTextWriter implements ITextWriter {\r\n  private _textHolder: string = ''\r\n\r\n  public get position(): number {\r\n    return this._textHolder.length;\r\n  }\r\n\r\n  public isLastCharNewline() {\r\n    return this._textHolder[this._textHolder.length - 1] === '\\n';\r\n  }\r\n\r\n  public levelRaise() {\r\n  }\r\n\r\n  public levelShrink() {\r\n  }\r\n\r\n  public write(text: string) {\r\n    this._textHolder += text;\r\n  }\r\n\r\n  public writeLine(text: string) {\r\n    this._textHolder += text;\r\n  }\r\n\r\n  public writeIndent() {\r\n  }\r\n\r\n  public writeNewline() {\r\n  }\r\n\r\n  public getString() {\r\n    return this._textHolder;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/StylesheetHelper.ts",
    "content": "export function stylesheetFromString(window: Window, text: string) {\n    //@ts-ignore\n    const newStylesheet = new window.CSSStyleSheet();\n    newStylesheet.replaceSync(text);\n    return newStylesheet;\n}\n\nexport function stylesheetToString(stylesheet: CSSStyleSheet) {\n    return Array.from(stylesheet.cssRules).map(rule => rule.cssText).join('\\n');\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/SvgHelper.ts",
    "content": "import { IPoint } from '../../interfaces/IPoint.js';\r\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\r\nimport './PathDataPolyfill.js';\r\nimport { createPathD, PathData } from './PathDataPolyfill.js';\r\n\r\ntype ProjectiveMatrix = [number, number, number, number, number, number, number, number, number];\r\n\r\ninterface SvgOverlayTransform {\r\n  bbox: DOMRect;\r\n  matrix: ProjectiveMatrix;\r\n  inverseMatrix: ProjectiveMatrix;\r\n}\r\n\r\nexport interface SvgOverlayPathOptions {\r\n  includeMarkers?: boolean;\r\n}\r\n\r\ninterface NormalizedPathSegment {\r\n  start: IPoint;\r\n  end: IPoint;\r\n  startAngle: number;\r\n  endAngle: number;\r\n}\r\n\r\ninterface MarkerPlacement {\r\n  marker: SVGMarkerElement;\r\n  point: IPoint;\r\n  angle: number;\r\n}\r\n\r\ninterface SvgViewBoxTransform {\r\n  transform: string;\r\n  scaleX: number;\r\n  scaleY: number;\r\n  translateX: number;\r\n  translateY: number;\r\n}\r\n\r\nexport function isVisualSvgElement(element: SVGElement) {\r\n    let el: Element = element;\r\n    while (el) {\r\n        if (el instanceof (el.ownerDocument.defaultView ?? window).SVGSVGElement)\r\n            return true;\r\n        if (el instanceof (el.ownerDocument.defaultView ?? window).SVGDefsElement)\r\n            return false;\r\n        if (el instanceof (el.ownerDocument.defaultView ?? window).SVGMetadataElement)\r\n            return false;\r\n        el = el.parentElement;\r\n    }\r\n    return true;\r\n}\r\n\r\nexport function svg(strings: TemplateStringsArray, ...values: any[]) {\r\n    const svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\r\n    svgEl.innerHTML = svgAsString(strings, ...values);\r\n    return svgEl;\r\n}\r\n\r\nexport function svgAsString(strings: TemplateStringsArray, ...values: any[]) {\r\n    if (strings.length === 1)\r\n        return strings.raw[0];\r\n    else {\r\n        let r = ''\r\n        for (let i = 0; i < strings.length; i++) {\r\n            r += strings[i] + (values[i] ?? '');\r\n        }\r\n        return r;\r\n    }\r\n}\r\n\r\nexport function isSupportedSvgGeometryElement(element: Element): element is SVGGraphicsElement {\r\n  return element instanceof SVGPathElement ||\r\n    element instanceof SVGRectElement ||\r\n    element instanceof SVGLineElement ||\r\n    element instanceof SVGCircleElement ||\r\n    element instanceof SVGEllipseElement ||\r\n    element instanceof SVGPolygonElement ||\r\n    element instanceof SVGPolylineElement;\r\n}\r\n\r\nexport function toOverlayPointFromSvgUserSpace(element: Element, designerCanvas: IDesignerCanvas, point: IPoint): IPoint {\r\n  const transform = _getSvgOverlayTransform(element, designerCanvas);\r\n  if (transform) {\r\n    const local = _toElementReferenceBoxPoint(element, point);\r\n    return _applyProjectiveMatrix(transform.matrix, local.x / transform.bbox.width, local.y / transform.bbox.height);\r\n  }\r\n\r\n  const fallbackLocalPoint = _toElementReferenceBoxPoint(element, point);\r\n  const tp = designerCanvas.canvas.convertPointFromNode(fallbackLocalPoint, element, { iframes: designerCanvas.iframes });\r\n  return { x: tp.x, y: tp.y };\r\n}\r\n\r\nexport function fromOverlayPointToSvgUserSpace(element: Element, designerCanvas: IDesignerCanvas, point: IPoint): IPoint {\r\n  const transform = _getSvgOverlayTransform(element, designerCanvas);\r\n  if (transform) {\r\n    const normalized = _applyProjectiveMatrix(transform.inverseMatrix, point.x, point.y);\r\n    return {\r\n      x: transform.bbox.x + normalized.x * transform.bbox.width,\r\n      y: transform.bbox.y + normalized.y * transform.bbox.height,\r\n    };\r\n  }\r\n\r\n  const tp = element.convertPointFromNode({ x: point.x, y: point.y }, designerCanvas.canvas, { iframes: designerCanvas.iframes });\r\n  return _fromElementReferenceBoxPoint(element, { x: tp.x, y: tp.y });\r\n}\r\n\r\nexport function createOverlayPathDataFromSvgGeometryElement(element: Element, designerCanvas: IDesignerCanvas, options?: SvgOverlayPathOptions): string | null {\r\n  const basePathData = _createOverlayPathDataWithoutMarkers(element, designerCanvas);\r\n  const includeMarkers = options?.includeMarkers !== false;\r\n\r\n  if (!includeMarkers) {\r\n    return basePathData;\r\n  }\r\n\r\n  const markerPathData = _createMarkerOverlayPathData(element, designerCanvas);\r\n  if (basePathData && markerPathData) {\r\n    return `${basePathData} ${markerPathData}`;\r\n  }\r\n\r\n  return basePathData ?? markerPathData;\r\n}\r\n\r\nfunction _createOverlayPathDataWithoutMarkers(element: Element, designerCanvas: IDesignerCanvas): string | null {\r\n  if (!isSupportedSvgGeometryElement(element)) {\r\n    return null;\r\n  }\r\n\r\n  const getPathData = (element as any).getPathData as ((options?: { normalize?: boolean }) => PathData[]) | undefined;\r\n  if (!getPathData) {\r\n    return null;\r\n  }\r\n\r\n  const sourcePathData = getPathData.call(element, { normalize: true }) ?? [];\r\n  if (!sourcePathData.length) {\r\n    return null;\r\n  }\r\n\r\n  const transformedPathData: PathData[] = [];\r\n  let currentPoint: IPoint = { x: 0, y: 0 };\r\n  let subPathStart: IPoint = { x: 0, y: 0 };\r\n\r\n  for (const command of sourcePathData) {\r\n    const type = command.type.toUpperCase();\r\n    const isRelative = command.type !== type;\r\n    const values = command.values ?? [];\r\n\r\n    const toAbsolute = (x: number, y: number): IPoint => {\r\n      if (isRelative) {\r\n        return { x: currentPoint.x + x, y: currentPoint.y + y };\r\n      }\r\n      return { x, y };\r\n    };\r\n\r\n    const toOverlay = (x: number, y: number): IPoint => {\r\n      const absolutePoint = toAbsolute(x, y);\r\n      return toOverlayPointFromSvgUserSpace(element, designerCanvas, absolutePoint);\r\n    };\r\n\r\n    switch (type) {\r\n      case 'M': {\r\n        const overlayPoint = toOverlay(values[0], values[1]);\r\n        transformedPathData.push({ type: 'M', values: [overlayPoint.x, overlayPoint.y] } as any);\r\n        currentPoint = toAbsolute(values[0], values[1]);\r\n        subPathStart = { ...currentPoint };\r\n        break;\r\n      }\r\n      case 'L': {\r\n        const overlayPoint = toOverlay(values[0], values[1]);\r\n        transformedPathData.push({ type: 'L', values: [overlayPoint.x, overlayPoint.y] } as any);\r\n        currentPoint = toAbsolute(values[0], values[1]);\r\n        break;\r\n      }\r\n      case 'H': {\r\n        const target = isRelative ? { x: currentPoint.x + values[0], y: currentPoint.y } : { x: values[0], y: currentPoint.y };\r\n        const overlayPoint = toOverlayPointFromSvgUserSpace(element, designerCanvas, target);\r\n        transformedPathData.push({ type: 'L', values: [overlayPoint.x, overlayPoint.y] } as any);\r\n        currentPoint = target;\r\n        break;\r\n      }\r\n      case 'V': {\r\n        const target = isRelative ? { x: currentPoint.x, y: currentPoint.y + values[0] } : { x: currentPoint.x, y: values[0] };\r\n        const overlayPoint = toOverlayPointFromSvgUserSpace(element, designerCanvas, target);\r\n        transformedPathData.push({ type: 'L', values: [overlayPoint.x, overlayPoint.y] } as any);\r\n        currentPoint = target;\r\n        break;\r\n      }\r\n      case 'C': {\r\n        const cp1 = toOverlay(values[0], values[1]);\r\n        const cp2 = toOverlay(values[2], values[3]);\r\n        const end = toOverlay(values[4], values[5]);\r\n        transformedPathData.push({ type: 'C', values: [cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y] } as any);\r\n        currentPoint = toAbsolute(values[4], values[5]);\r\n        break;\r\n      }\r\n      case 'S': {\r\n        const cp2 = toOverlay(values[0], values[1]);\r\n        const end = toOverlay(values[2], values[3]);\r\n        transformedPathData.push({ type: 'S', values: [cp2.x, cp2.y, end.x, end.y] } as any);\r\n        currentPoint = toAbsolute(values[2], values[3]);\r\n        break;\r\n      }\r\n      case 'Q': {\r\n        const cp1 = toOverlay(values[0], values[1]);\r\n        const end = toOverlay(values[2], values[3]);\r\n        transformedPathData.push({ type: 'Q', values: [cp1.x, cp1.y, end.x, end.y] } as any);\r\n        currentPoint = toAbsolute(values[2], values[3]);\r\n        break;\r\n      }\r\n      case 'T': {\r\n        const end = toOverlay(values[0], values[1]);\r\n        transformedPathData.push({ type: 'T', values: [end.x, end.y] } as any);\r\n        currentPoint = toAbsolute(values[0], values[1]);\r\n        break;\r\n      }\r\n      case 'A': {\r\n        const end = toOverlay(values[5], values[6]);\r\n        transformedPathData.push({ type: 'A', values: [values[0], values[1], values[2], values[3], values[4], end.x, end.y] } as any);\r\n        currentPoint = toAbsolute(values[5], values[6]);\r\n        break;\r\n      }\r\n      case 'Z': {\r\n        transformedPathData.push({ type: 'Z', values: [] } as any);\r\n        currentPoint = { ...subPathStart };\r\n        break;\r\n      }\r\n    }\r\n  }\r\n\r\n  return createPathD(transformedPathData);\r\n}\r\n\r\nfunction _createMarkerOverlayPathData(element: Element, designerCanvas: IDesignerCanvas): string | null {\r\n  if (!(element instanceof SVGGraphicsElement)) {\r\n    return null;\r\n  }\r\n\r\n  const placements = _collectMarkerPlacements(element);\r\n  if (!placements.length) {\r\n    return null;\r\n  }\r\n\r\n  const ownerSvg = element.ownerSVGElement;\r\n  if (!ownerSvg) {\r\n    return null;\r\n  }\r\n\r\n  const tempGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\r\n  tempGroup.setAttribute('visibility', 'hidden');\r\n  tempGroup.setAttribute('pointer-events', 'none');\r\n  ownerSvg.appendChild(tempGroup);\r\n\r\n  try {\r\n    const pathParts: string[] = [];\r\n\r\n    for (const placement of placements) {\r\n      const markerInstance = _createMarkerInstanceGroup(element, placement);\r\n      if (!markerInstance) {\r\n        continue;\r\n      }\r\n\r\n      tempGroup.appendChild(markerInstance);\r\n      const markerGraphics = markerInstance.querySelectorAll('path, rect, line, circle, ellipse, polygon, polyline');\r\n      for (const markerGraphic of markerGraphics) {\r\n        const pathData = _createOverlayPathDataWithoutMarkers(markerGraphic, designerCanvas);\r\n        if (pathData) {\r\n          pathParts.push(pathData);\r\n        }\r\n      }\r\n    }\r\n\r\n    return pathParts.length ? pathParts.join(' ') : null;\r\n  } finally {\r\n    tempGroup.remove();\r\n  }\r\n}\r\n\r\nfunction _collectMarkerPlacements(element: SVGGraphicsElement): MarkerPlacement[] {\r\n  const segments = _getNormalizedPathSegments(element);\r\n  if (!segments.length) {\r\n    return [];\r\n  }\r\n\r\n  const placements: MarkerPlacement[] = [];\r\n  const startMarker = _resolveMarkerReference(element, 'marker-start');\r\n  const midMarker = _resolveMarkerReference(element, 'marker-mid');\r\n  const endMarker = _resolveMarkerReference(element, 'marker-end');\r\n\r\n  if (startMarker) {\r\n    placements.push({\r\n      marker: startMarker,\r\n      point: segments[0].start,\r\n      angle: _resolveMarkerAngle(startMarker, segments[0].startAngle, undefined, true)\r\n    });\r\n  }\r\n\r\n  if (midMarker) {\r\n    for (let i = 0; i < segments.length - 1; i++) {\r\n      if (!_pointsEqual(segments[i].end, segments[i + 1].start)) {\r\n        continue;\r\n      }\r\n\r\n      placements.push({\r\n        marker: midMarker,\r\n        point: segments[i].end,\r\n        angle: _resolveMarkerAngle(midMarker, segments[i].endAngle, segments[i + 1].startAngle, false)\r\n      });\r\n    }\r\n\r\n    if (_pointsEqual(segments[segments.length - 1].end, segments[0].start) && segments.length > 1) {\r\n      placements.push({\r\n        marker: midMarker,\r\n        point: segments[segments.length - 1].end,\r\n        angle: _resolveMarkerAngle(midMarker, segments[segments.length - 1].endAngle, segments[0].startAngle, false)\r\n      });\r\n    }\r\n  }\r\n\r\n  if (endMarker) {\r\n    const lastSegment = segments[segments.length - 1];\r\n    placements.push({\r\n      marker: endMarker,\r\n      point: lastSegment.end,\r\n      angle: _resolveMarkerAngle(endMarker, lastSegment.endAngle, undefined, false)\r\n    });\r\n  }\r\n\r\n  return placements;\r\n}\r\n\r\nfunction _getNormalizedPathSegments(element: SVGGraphicsElement): NormalizedPathSegment[] {\r\n  const getPathData = (element as any).getPathData as ((options?: { normalize?: boolean }) => PathData[]) | undefined;\r\n  if (!getPathData) {\r\n    return [];\r\n  }\r\n\r\n  const pathData = getPathData.call(element, { normalize: true }) ?? [];\r\n  const segments: NormalizedPathSegment[] = [];\r\n  let currentPoint: IPoint = { x: 0, y: 0 };\r\n  let subPathStart: IPoint = { x: 0, y: 0 };\r\n\r\n  for (const command of pathData) {\r\n    switch (command.type) {\r\n      case 'M': {\r\n        currentPoint = { x: command.values[0], y: command.values[1] };\r\n        subPathStart = { ...currentPoint };\r\n        break;\r\n      }\r\n      case 'L': {\r\n        const endPoint = { x: command.values[0], y: command.values[1] };\r\n        const angle = _angleBetween(currentPoint, endPoint);\r\n        if (angle != null) {\r\n          segments.push({ start: { ...currentPoint }, end: endPoint, startAngle: angle, endAngle: angle });\r\n        }\r\n        currentPoint = endPoint;\r\n        break;\r\n      }\r\n      case 'C': {\r\n        const cp1 = { x: command.values[0], y: command.values[1] };\r\n        const cp2 = { x: command.values[2], y: command.values[3] };\r\n        const endPoint = { x: command.values[4], y: command.values[5] };\r\n        const startAngle = _getFirstUsableAngle([currentPoint, cp1, cp2, endPoint]);\r\n        const endAngle = _getLastUsableAngle([currentPoint, cp1, cp2, endPoint]);\r\n        if (startAngle != null && endAngle != null) {\r\n          segments.push({ start: { ...currentPoint }, end: endPoint, startAngle, endAngle });\r\n        }\r\n        currentPoint = endPoint;\r\n        break;\r\n      }\r\n      case 'Z': {\r\n        const angle = _angleBetween(currentPoint, subPathStart);\r\n        if (angle != null) {\r\n          segments.push({ start: { ...currentPoint }, end: { ...subPathStart }, startAngle: angle, endAngle: angle });\r\n        }\r\n        currentPoint = { ...subPathStart };\r\n        break;\r\n      }\r\n    }\r\n  }\r\n\r\n  return segments;\r\n}\r\n\r\nfunction _resolveMarkerReference(element: SVGGraphicsElement, attributeName: 'marker-start' | 'marker-mid' | 'marker-end'): SVGMarkerElement | null {\r\n  const reference = element.getAttribute(attributeName)?.trim();\r\n  if (!reference) {\r\n    return null;\r\n  }\r\n\r\n  const urlMatch = /^url\\((['\"]?)(.*?)\\1\\)$/.exec(reference);\r\n  const url = urlMatch?.[2] ?? reference;\r\n  if (!url.startsWith('#')) {\r\n    return null;\r\n  }\r\n\r\n  const rootNode = element.getRootNode() as ParentNode;\r\n  const marker = typeof rootNode.querySelector === 'function' ? rootNode.querySelector(url) : null;\r\n  return marker instanceof SVGMarkerElement ? marker : null;\r\n}\r\n\r\nfunction _createMarkerInstanceGroup(sourceElement: SVGGraphicsElement, placement: MarkerPlacement): SVGGElement | null {\r\n  const marker = placement.marker;\r\n  if (!marker.childNodes.length) {\r\n    return null;\r\n  }\r\n\r\n  const markerScale = _getMarkerUnitsScale(sourceElement, marker);\r\n  const outerGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\r\n  const transformParts = [\r\n    `translate(${placement.point.x} ${placement.point.y})`,\r\n    `rotate(${placement.angle})`\r\n  ];\r\n  if (markerScale !== 1) {\r\n    transformParts.push(`scale(${markerScale})`);\r\n  }\r\n  outerGroup.setAttribute('transform', transformParts.join(' '));\r\n\r\n  const viewBoxTransform = _createViewBoxTransform(marker);\r\n  const mappedRefPoint = _mapMarkerPointToViewport(marker, viewBoxTransform, {\r\n    x: _getAnimatedLengthValue(marker.refX),\r\n    y: _getAnimatedLengthValue(marker.refY)\r\n  });\r\n\r\n  const refGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\r\n  refGroup.setAttribute('transform', `translate(${-mappedRefPoint.x} ${-mappedRefPoint.y})`);\r\n  outerGroup.appendChild(refGroup);\r\n\r\n  const viewBoxGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');\r\n  if (viewBoxTransform) {\r\n    viewBoxGroup.setAttribute('transform', viewBoxTransform.transform);\r\n  }\r\n  refGroup.appendChild(viewBoxGroup);\r\n\r\n  for (const childNode of Array.from(marker.childNodes)) {\r\n    viewBoxGroup.appendChild(childNode.cloneNode(true));\r\n  }\r\n\r\n  return outerGroup;\r\n}\r\n\r\nfunction _getMarkerUnitsScale(sourceElement: SVGGraphicsElement, marker: SVGMarkerElement): number {\r\n  if (marker.getAttribute('markerUnits') === 'userSpaceOnUse') {\r\n    return 1;\r\n  }\r\n\r\n  const strokeWidth = parseFloat(getComputedStyle(sourceElement).strokeWidth);\r\n  return Number.isFinite(strokeWidth) && strokeWidth > 0 ? strokeWidth : 1;\r\n}\r\n\r\nfunction _createViewBoxTransform(marker: SVGMarkerElement): SvgViewBoxTransform | null {\r\n  const viewBox = marker.viewBox.baseVal;\r\n  if (viewBox == null || (viewBox.width === 0 && viewBox.height === 0)) {\r\n    return null;\r\n  }\r\n\r\n  const viewportWidth = _getAnimatedLengthValue(marker.markerWidth);\r\n  const viewportHeight = _getAnimatedLengthValue(marker.markerHeight);\r\n  if (!viewportWidth || !viewportHeight || !viewBox.width || !viewBox.height) {\r\n    return null;\r\n  }\r\n\r\n  const preserveAspectRatio = marker.preserveAspectRatio.baseVal;\r\n  let scaleX = viewportWidth / viewBox.width;\r\n  let scaleY = viewportHeight / viewBox.height;\r\n  let translateX = -viewBox.x * scaleX;\r\n  let translateY = -viewBox.y * scaleY;\r\n\r\n  if (preserveAspectRatio.align !== SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE) {\r\n    const uniformScale = preserveAspectRatio.meetOrSlice === SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE\r\n      ? Math.max(scaleX, scaleY)\r\n      : Math.min(scaleX, scaleY);\r\n\r\n    scaleX = uniformScale;\r\n    scaleY = uniformScale;\r\n\r\n    const extraWidth = viewportWidth - viewBox.width * uniformScale;\r\n    const extraHeight = viewportHeight - viewBox.height * uniformScale;\r\n    const { xAlign, yAlign } = _getAlignFactors(preserveAspectRatio.align);\r\n    translateX = -viewBox.x * uniformScale + extraWidth * xAlign;\r\n    translateY = -viewBox.y * uniformScale + extraHeight * yAlign;\r\n  }\r\n\r\n  return {\r\n    transform: `matrix(${scaleX} 0 0 ${scaleY} ${translateX} ${translateY})`,\r\n    scaleX,\r\n    scaleY,\r\n    translateX,\r\n    translateY,\r\n  };\r\n}\r\n\r\nfunction _mapMarkerPointToViewport(marker: SVGMarkerElement, viewBoxTransform: SvgViewBoxTransform | null, point: IPoint): IPoint {\r\n  if (!viewBoxTransform) {\r\n    return point;\r\n  }\r\n\r\n  return {\r\n    x: point.x * viewBoxTransform.scaleX + viewBoxTransform.translateX,\r\n    y: point.y * viewBoxTransform.scaleY + viewBoxTransform.translateY,\r\n  };\r\n}\r\n\r\nfunction _getAlignFactors(align: number): { xAlign: number; yAlign: number } {\r\n  switch (align) {\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN:\r\n      return { xAlign: 0.5, yAlign: 0 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMIN:\r\n      return { xAlign: 1, yAlign: 0 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID:\r\n      return { xAlign: 0, yAlign: 0.5 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID:\r\n      return { xAlign: 0.5, yAlign: 0.5 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID:\r\n      return { xAlign: 1, yAlign: 0.5 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMAX:\r\n      return { xAlign: 0, yAlign: 1 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMAX:\r\n      return { xAlign: 0.5, yAlign: 1 };\r\n    case SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX:\r\n      return { xAlign: 1, yAlign: 1 };\r\n    default:\r\n      return { xAlign: 0, yAlign: 0 };\r\n  }\r\n}\r\n\r\nfunction _resolveMarkerAngle(marker: SVGMarkerElement, incomingAngle: number, outgoingAngle?: number, isStartMarker?: boolean): number {\r\n  const orient = marker.orientType.baseVal === SVGMarkerElement.SVG_MARKER_ORIENT_AUTO ? 'auto' : marker.getAttribute('orient')?.trim() ?? '0';\r\n  if (orient === 'auto' || orient === 'auto-start-reverse') {\r\n    let angle = outgoingAngle == null ? incomingAngle : _bisectAngles(incomingAngle, outgoingAngle);\r\n    if (isStartMarker && orient === 'auto-start-reverse') {\r\n      angle += 180;\r\n    }\r\n    return angle;\r\n  }\r\n\r\n  return _parseAngle(orient);\r\n}\r\n\r\nfunction _parseAngle(value: string): number {\r\n  if (value.endsWith('rad')) {\r\n    return parseFloat(value) * 180 / Math.PI;\r\n  }\r\n  if (value.endsWith('turn')) {\r\n    return parseFloat(value) * 360;\r\n  }\r\n  if (value.endsWith('grad')) {\r\n    return parseFloat(value) * 0.9;\r\n  }\r\n  return parseFloat(value);\r\n}\r\n\r\nfunction _bisectAngles(angleA: number, angleB: number): number {\r\n  const vectorA = { x: Math.cos(angleA * Math.PI / 180), y: Math.sin(angleA * Math.PI / 180) };\r\n  const vectorB = { x: Math.cos(angleB * Math.PI / 180), y: Math.sin(angleB * Math.PI / 180) };\r\n  const sum = { x: vectorA.x + vectorB.x, y: vectorA.y + vectorB.y };\r\n\r\n  if (Math.abs(sum.x) < 1e-10 && Math.abs(sum.y) < 1e-10) {\r\n    return angleB;\r\n  }\r\n\r\n  return Math.atan2(sum.y, sum.x) * 180 / Math.PI;\r\n}\r\n\r\nfunction _getFirstUsableAngle(points: IPoint[]): number | null {\r\n  for (let i = 0; i < points.length - 1; i++) {\r\n    const angle = _angleBetween(points[i], points[i + 1]);\r\n    if (angle != null) {\r\n      return angle;\r\n    }\r\n  }\r\n  return null;\r\n}\r\n\r\nfunction _getLastUsableAngle(points: IPoint[]): number | null {\r\n  for (let i = points.length - 1; i > 0; i--) {\r\n    const angle = _angleBetween(points[i - 1], points[i]);\r\n    if (angle != null) {\r\n      return angle;\r\n    }\r\n  }\r\n  return null;\r\n}\r\n\r\nfunction _angleBetween(start: IPoint, end: IPoint): number | null {\r\n  const dx = end.x - start.x;\r\n  const dy = end.y - start.y;\r\n  if (Math.abs(dx) < 1e-10 && Math.abs(dy) < 1e-10) {\r\n    return null;\r\n  }\r\n  return Math.atan2(dy, dx) * 180 / Math.PI;\r\n}\r\n\r\nfunction _pointsEqual(a: IPoint, b: IPoint): boolean {\r\n  return Math.abs(a.x - b.x) < 1e-10 && Math.abs(a.y - b.y) < 1e-10;\r\n}\r\n\r\nfunction _getAnimatedLengthValue(length: SVGAnimatedLength): number {\r\n  return length?.baseVal?.value ?? 0;\r\n}\r\n\r\nfunction _toElementReferenceBoxPoint(element: Element, point: IPoint): IPoint {\r\n  const bbox = _getSvgGeometryBBoxOffset(element);\r\n  return { x: point.x - bbox.x, y: point.y - bbox.y };\r\n}\r\n\r\nfunction _fromElementReferenceBoxPoint(element: Element, point: IPoint): IPoint {\r\n  const bbox = _getSvgGeometryBBoxOffset(element);\r\n  return { x: point.x + bbox.x, y: point.y + bbox.y };\r\n}\r\n\r\nfunction _getSvgGeometryBBoxOffset(element: Element): IPoint {\r\n  if (!(element instanceof SVGGraphicsElement) || element instanceof SVGSVGElement) {\r\n    return { x: 0, y: 0 };\r\n  }\r\n\r\n  const bbox = element.getBBox();\r\n  return { x: bbox.x, y: bbox.y };\r\n}\r\n\r\nfunction _getSvgOverlayTransform(element: Element, designerCanvas: IDesignerCanvas): SvgOverlayTransform | null {\r\n  if (!(element instanceof SVGGraphicsElement) || element instanceof SVGSVGElement) {\r\n    return null;\r\n  }\r\n\r\n  const bbox = element.getBBox();\r\n  if (Math.abs(bbox.width) < 1e-10 || Math.abs(bbox.height) < 1e-10) {\r\n    return null;\r\n  }\r\n\r\n  const quad = element.getBoxQuads({ relativeTo: designerCanvas.canvas, iframes: designerCanvas.iframes })[0];\r\n  if (!quad) {\r\n    return null;\r\n  }\r\n\r\n  const matrix = _createProjectiveMatrixForQuad(quad);\r\n  if (!matrix) {\r\n    return null;\r\n  }\r\n\r\n  const inverseMatrix = _invertProjectiveMatrix(matrix);\r\n  if (!inverseMatrix) {\r\n    return null;\r\n  }\r\n\r\n  return { bbox, matrix, inverseMatrix };\r\n}\r\n\r\nfunction _createProjectiveMatrixForQuad(quad: DOMQuad): ProjectiveMatrix | null {\r\n  const x1 = quad.p1.x;\r\n  const y1 = quad.p1.y;\r\n  const x2 = quad.p2.x;\r\n  const y2 = quad.p2.y;\r\n  const x3 = quad.p3.x;\r\n  const y3 = quad.p3.y;\r\n  const x4 = quad.p4.x;\r\n  const y4 = quad.p4.y;\r\n\r\n  const sx = x1 - x2 + x3 - x4;\r\n  const sy = y1 - y2 + y3 - y4;\r\n  const dx1 = x2 - x3;\r\n  const dy1 = y2 - y3;\r\n  const dx2 = x4 - x3;\r\n  const dy2 = y4 - y3;\r\n\r\n  let g = 0;\r\n  let h = 0;\r\n\r\n  if (Math.abs(sx) >= 1e-10 || Math.abs(sy) >= 1e-10) {\r\n    const denominator = dx1 * dy2 - dy1 * dx2;\r\n    if (Math.abs(denominator) < 1e-10) {\r\n      return null;\r\n    }\r\n\r\n    g = (sx * dy2 - sy * dx2) / denominator;\r\n    h = (dx1 * sy - dy1 * sx) / denominator;\r\n  }\r\n\r\n  return [\r\n    x2 - x1 + g * x2,\r\n    x4 - x1 + h * x4,\r\n    x1,\r\n    y2 - y1 + g * y2,\r\n    y4 - y1 + h * y4,\r\n    y1,\r\n    g,\r\n    h,\r\n    1,\r\n  ];\r\n}\r\n\r\nfunction _applyProjectiveMatrix(matrix: ProjectiveMatrix, x: number, y: number): IPoint {\r\n  const projectedX = matrix[0] * x + matrix[1] * y + matrix[2];\r\n  const projectedY = matrix[3] * x + matrix[4] * y + matrix[5];\r\n  const projectedW = matrix[6] * x + matrix[7] * y + matrix[8];\r\n  const safeW = Math.abs(projectedW) < 1e-10 ? (projectedW < 0 ? -1e-10 : 1e-10) : projectedW;\r\n  return {\r\n    x: projectedX / safeW,\r\n    y: projectedY / safeW,\r\n  };\r\n}\r\n\r\nfunction _invertProjectiveMatrix(matrix: ProjectiveMatrix): ProjectiveMatrix | null {\r\n  const determinant =\r\n    matrix[0] * (matrix[4] * matrix[8] - matrix[5] * matrix[7]) -\r\n    matrix[1] * (matrix[3] * matrix[8] - matrix[5] * matrix[6]) +\r\n    matrix[2] * (matrix[3] * matrix[7] - matrix[4] * matrix[6]);\r\n\r\n  if (Math.abs(determinant) < 1e-10) {\r\n    return null;\r\n  }\r\n\r\n  const inverseDeterminant = 1 / determinant;\r\n  return [\r\n    (matrix[4] * matrix[8] - matrix[5] * matrix[7]) * inverseDeterminant,\r\n    (matrix[2] * matrix[7] - matrix[1] * matrix[8]) * inverseDeterminant,\r\n    (matrix[1] * matrix[5] - matrix[2] * matrix[4]) * inverseDeterminant,\r\n    (matrix[5] * matrix[6] - matrix[3] * matrix[8]) * inverseDeterminant,\r\n    (matrix[0] * matrix[8] - matrix[2] * matrix[6]) * inverseDeterminant,\r\n    (matrix[2] * matrix[3] - matrix[0] * matrix[5]) * inverseDeterminant,\r\n    (matrix[3] * matrix[7] - matrix[4] * matrix[6]) * inverseDeterminant,\r\n    (matrix[1] * matrix[6] - matrix[0] * matrix[7]) * inverseDeterminant,\r\n    (matrix[0] * matrix[4] - matrix[1] * matrix[3]) * inverseDeterminant,\r\n  ];\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/SwitchContainerHelper.ts",
    "content": "import { IDesignItem } from '../item/IDesignItem.js';\r\nimport { NodeType } from '../item/NodeType.js';\r\n\r\nexport function switchContainer(items: IDesignItem[], newContainer: IDesignItem, resizeNewContainer: boolean = false, newContainerOffset: number = 0) {\r\n    //TODO: switch to other containers like grid, flexbox, ...\r\n    //TODO: position non absolute, or absolute from bottom or right\r\n\r\n    for (let i of items) {\r\n        if (i == newContainer || i.element.contains(newContainer.element)) {\r\n            console.warn('could not move items into of itself or a child');\r\n            return;\r\n        }\r\n    }\r\n\r\n    const firstItem = items[0];\r\n    const grp = firstItem.openGroup('switchContainerHelper');\r\n\r\n    const designerCanvas = firstItem.instanceServiceContainer.designerCanvas;\r\n\r\n    let minX = Number.MAX_VALUE;\r\n    let minY = Number.MAX_VALUE;\r\n    let maxX = 0;\r\n    let maxY = 0;\r\n    for (let e of items) {\r\n        let rect = designerCanvas.getNormalizedElementCoordinates(e.element);\r\n        if (rect.x < minX)\r\n            minX = rect.x;\r\n        if (rect.y < minY)\r\n            minY = rect.y;\r\n        if (rect.x + rect.width > maxX)\r\n            maxX = rect.x + rect.width;\r\n        if (rect.y + rect.height > maxY)\r\n            maxY = rect.y + rect.height;\r\n    }\r\n\r\n    let rectNewContainer = designerCanvas.getNormalizedElementCoordinates(newContainer.element);\r\n\r\n    for (let e of items) {\r\n        let rect = designerCanvas.getNormalizedElementCoordinates(e.element);\r\n        if (e.nodeType == NodeType.Element)  {\r\n            //TODO: use container service or the helper wich this uses, not fixed left and top.\r\n            if (resizeNewContainer) {\r\n                e.setStyle('left', (rect.x - minX + newContainerOffset).toString() + 'px');\r\n                e.setStyle('top', (rect.y - minY + newContainerOffset).toString() + 'px');\r\n            } else {\r\n                e.setStyle('left', (rect.x - rectNewContainer.x).toString() + 'px');\r\n                e.setStyle('top', (rect.y - rectNewContainer.y).toString() + 'px');\r\n            }\r\n        }\r\n        newContainer.insertChild(e);\r\n    }\r\n    if (resizeNewContainer) {\r\n        newContainer.setStyle('position', 'absolute');\r\n        newContainer.setStyle('left', (minX - newContainerOffset).toString() + 'px');\r\n        newContainer.setStyle('top', (minY - newContainerOffset).toString() + 'px');\r\n        newContainer.setStyle('width', (maxX - minX + 2 * newContainerOffset).toString() + 'px');\r\n        newContainer.setStyle('height', (maxY - minY + 2 * newContainerOffset).toString() + 'px');\r\n    }\r\n\r\n    grp.commit();\r\n}\r\n\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/TextHelper.ts",
    "content": "let canvas: HTMLCanvasElement;\r\n/**\r\n  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.\r\n  * \r\n  * @param {String} text The text to be rendered.\r\n  * @param {String} font The css font descriptor that text is to be rendered with (e.g. \"bold 14px verdana\").\r\n  * \r\n  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393\r\n  */\r\nexport function getTextWidth(text: string, font: string) {\r\n    if (!canvas)\r\n        canvas = document.createElement(\"canvas\");\r\n    const context = canvas.getContext(\"2d\");\r\n    context.font = font;\r\n    const metrics = context.measureText(text);\r\n    return metrics.width;\r\n}\r\n\r\nexport function getFont(el: Element) {\r\n    const fontWeight = getComputedStyle(el).fontWeight || 'normal';\r\n    const fontSize = getComputedStyle(el).fontSize || '16px';\r\n    const fontFamily = getComputedStyle(el).fontFamily || 'Times New Roman';\r\n\r\n    return `${fontWeight} ${fontSize} ${fontFamily}`;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/TouchGestureHelper.ts",
    "content": "const panThreshold = 10;\nconst zoomThreshold = 10;\n//const rotateThreshold = 0;\n\nexport class TouchGestureHelper {\n    public static addTouchEvents(element: HTMLElement) {\n        return new TouchGestureHelper(element);\n    }\n\n    private constructor(element: HTMLElement) {\n        this._target = element;\n\n        element.addEventListener('touchstart', (e) => this._touchStart(e));\n        element.addEventListener('touchmove', (e) => this._touchMove(e));\n        element.addEventListener('touchend', (e) => this._touchEnd(e));\n        element.addEventListener('touchcancel', (e) => this._touchEnd(e));\n    }\n\n    private _target: HTMLElement;\n\n    private _started: boolean;\n\n    private _startX_0: number;\n    private _startY_0: number;\n    //private _startX_1: number;\n    //private _startY_1: number;\n    private _lastZoom: number;\n    private _lastPanDistanceX: number;\n    private _lastPanDistanceY: number;\n\n    private _startZoomDistance: number;\n\n    public multitouchEventActive: boolean;\n\n\n    private _mode: 'pan' | 'zoom' | 'rotate' = null;\n\n    _touchStart(e: TouchEvent) {\n\n        if (e.touches.length === 2) {\n            this.multitouchEventActive = true;\n\n            this._mode = null;\n\n            this._started = true;\n            this._startX_0 = e.touches[0].screenX;\n            this._startY_0 = e.touches[0].screenY;\n            //this._startX_1 = e.touches[1].screenX;\n            //this._startY_1 = e.touches[1].screenY;\n\n            this._lastZoom = 0;\n            this._lastPanDistanceX = 0;\n            this._lastPanDistanceY = 0;\n\n            this._startZoomDistance = Math.hypot(\n                e.touches[0].screenX - e.touches[1].screenX,\n                e.touches[0].screenY - e.touches[1].screenY);\n        } else {\n            this.multitouchEventActive = false;\n            this._started = false;\n        }\n    }\n\n    _touchMove(e: TouchEvent) {\n        if (e.touches.length !== 2) {\n            this.multitouchEventActive = false;\n            this._started = false;\n        }\n        if (this._started) {\n            e.preventDefault();\n\n            let newZoomDistance = Math.hypot(\n                e.touches[0].screenX - e.touches[1].screenX,\n                e.touches[0].screenY - e.touches[1].screenY);\n\n            const newPanDistanceX = this._startX_0 - e.touches[0].screenX;\n            const newPanDistanceY = this._startY_0 - e.touches[0].screenY;\n            const panDiffX = newPanDistanceX - this._lastPanDistanceX;\n            const panDiffY = newPanDistanceY - this._lastPanDistanceY;\n            this._lastPanDistanceX = newPanDistanceX;\n            this._lastPanDistanceY = newPanDistanceY;\n\n            const zoom = newZoomDistance - this._startZoomDistance;\n            const zoomDiff = zoom - this._lastZoom;\n            this._lastZoom = zoom;\n\n            this._lastZoom\n            if (!this._mode) {\n                if (Math.abs(zoom) > zoomThreshold) {\n                    this._mode = 'zoom'\n                }\n                if (Math.abs(newPanDistanceX) > panThreshold || Math.abs(newPanDistanceY) > panThreshold) {\n                    this._mode = 'pan'\n                }\n            }\n\n            if (this._mode) {\n                if (this._mode == 'zoom') {\n                    const event = new CustomEvent(\"zoom\", { detail: { factor: zoom, diff: zoomDiff } });\n                    this._target.dispatchEvent(event);\n                } else if (this._mode == 'pan') {\n                    const event = new CustomEvent(\"pan\", { detail: { x: newPanDistanceX, deltaX: panDiffX, y: newPanDistanceY, deltaY: panDiffY } });\n                    this._target.dispatchEvent(event);\n                }\n            }\n        }\n    }\n\n    _touchEnd(e: TouchEvent) {\n        this.multitouchEventActive = false;\n        if (e.touches.length !== 2) {\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/TransformHelper.ts",
    "content": "let identityMatrix: number[] = [\r\n  1, 0, 0, 0,\r\n  0, 1, 0, 0,\r\n  0, 0, 1, 0,\r\n  0, 0, 0, 1\r\n];\r\n\r\nexport function combineTransforms(element: HTMLElement, actualTransforms: string, requestedTransformation: string) {\r\n  if (actualTransforms == null || actualTransforms == '') {\r\n    element.style.transform = requestedTransformation;\r\n    return;\r\n  }\r\n\r\n  const actualTransformationMatrix = new DOMMatrix(actualTransforms);\r\n  const requestedTransformationMatrix = new DOMMatrix(requestedTransformation);\r\n  const newTransformationMatrix = requestedTransformationMatrix.multiply(actualTransformationMatrix);\r\n  element.style.transform = newTransformationMatrix.toString();\r\n}\r\n\r\nexport function transformPointByInverseMatrix(point: DOMPoint, matrix: DOMMatrix) {\r\n  const inverse = matrix.inverse();\r\n  return point.matrixTransform(inverse);\r\n}\r\n\r\nexport function getRotationMatrix3d(axisOfRotation: 'x' | 'y' | 'z' | 'X' | 'Y' | 'Z', angle: number) {\r\n  const angleInRadians = angle / 180 * Math.PI;\r\n  const sin = Math.sin;\r\n  const cos = Math.cos;\r\n  let rotationMatrix3d = [];\r\n\r\n  switch (axisOfRotation.toLowerCase()) {\r\n    case 'x':\r\n      rotationMatrix3d = [\r\n        1, 0, 0, 0,\r\n        0, cos(angleInRadians), -sin(angleInRadians), 0,\r\n        0, sin(angleInRadians), cos(angleInRadians), 0,\r\n        0, 0, 0, 1\r\n      ];\r\n      break;\r\n    case 'y':\r\n      rotationMatrix3d = [\r\n        cos(angleInRadians), 0, sin(angleInRadians), 0,\r\n        0, 1, 0, 0,\r\n        -sin(angleInRadians), 0, cos(angleInRadians), 0,\r\n        0, 0, 0, 1\r\n      ];\r\n      break;\r\n    case 'z':\r\n      rotationMatrix3d = [\r\n        cos(angleInRadians), -sin(angleInRadians), 0, 0,\r\n        sin(angleInRadians), cos(angleInRadians), 0, 0,\r\n        0, 0, 1, 0,\r\n        0, 0, 0, 1\r\n      ];\r\n      break;\r\n    default:\r\n      rotationMatrix3d = null;\r\n      break;\r\n  }\r\n\r\n  return rotationMatrix3d;\r\n}\r\n\r\nexport function rotateElementByMatrix3d(element: HTMLElement, matrix: number[]) {\r\n  element.style.transform = \"matrix3d(\" + matrix.join(',') + \")\"\r\n}\r\n\r\n//maybe remove -> refactor rotate extension\r\nexport function cssMatrixToMatrixArray(cssMatrix: string) {\r\n  if (!cssMatrix.includes('matrix')) {\r\n    if (cssMatrix != 'none')\r\n      console.error('cssMatrixToMatrixArray: no css matrix passed');\r\n    return identityMatrix;\r\n  }\r\n  let matrixArray: number[] = cssMatrix.match(/^matrix.*\\((.*)\\)/)[1].split(',').map(Number);\r\n  return matrixArray;\r\n}\r\n\r\nexport function getRotationAngleFromMatrix(matrixArray: number[], domMatrix: DOMMatrix) {\r\n  let angle = null;\r\n  const a = domMatrix != null ? domMatrix.a : matrixArray[0];\r\n  const b = domMatrix != null ? domMatrix.b : matrixArray[1];\r\n  angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));\r\n\r\n  return angle;\r\n}\r\n\r\nexport function extractTranslationFromDOMMatrix(matrix: DOMMatrix): DOMPoint {\r\n  //TODO: maybe we also need m43 here??\r\n  return new DOMPoint(matrix.m41, matrix.m42, 0, 0);\r\n}\r\n\r\nexport function extractRotationAngleFromDOMMatrix(matrix: DOMMatrix): number {\r\n  return getRotationAngleFromMatrix(null, matrix);\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/XmlHelper.ts",
    "content": "export function encodeXMLChars(value: string) {\r\n    return value.replace(/&/g, '&amp;')\r\n        .replace(/</g, '&lt;')\r\n        .replace(/>/g, '&gt;')\r\n        .replace(/\"/g, '&quot;')\r\n        .replace(/'/g, '&apos;');\r\n};\r\n\r\nexport function decodeXMLChars(value: string) {\r\n    return value.replace(/&apos;/g, \"'\")\r\n        .replace(/&quot;/g, '\"')\r\n        .replace(/&gt;/g, '>')\r\n        .replace(/&lt;/g, '<')\r\n        .replace(/&amp;/g, '&');\r\n};"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/contextMenu/ContextMenu.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\r\nimport { IContextMenu, IContextMenuItem } from './IContextMenuItem.js';\r\n\r\n\r\nexport interface IContextMenuOptions {\r\n  defaultIcon?: string,\r\n  subIcon?: string,\r\n  mouseOffset?: number,\r\n  shadowRoot?: ShadowRoot | Document,\r\n  mode?: 'normal' | 'undo'\r\n}\r\n\r\nexport class ContextMenu implements IContextMenu {\r\n\r\n  private static _contextMenuCss = css`\r\n\t  .context_menu {\r\n\t\tposition: fixed;\r\n    inset: auto;\r\n    margin: 0;\r\n    border: none;\r\n    background: transparent;\r\n    overflow: visible;\r\n\t\topacity: 0;\r\n\t\ttransform: scale(0);\r\n\t\ttransition: transform 0.1s;\r\n\t\ttransform-origin: top left;\r\n\t\tpadding: 0;\r\n\t\tz-index: 2147483647;\r\n\t\tcolor: black;\r\n\t  }\r\n\t  \r\n\t  .context_menu.context_menu_display {\r\n\t\topacity: 1;\r\n\t\ttransform: scale(1);\r\n\t  }\r\n\t  \r\n\t  .context_menu,\r\n\t  .context_menu * {\r\n\t\tbox-sizing: border-box;\r\n\t  }\r\n\t  \r\n\t  .context_menu * {\r\n\t\tposition: relative;\r\n\t  }\r\n\t  \r\n\t  .context_menu ul {\r\n\t\tlist-style-type: none;\r\n\t\tpadding: 3px;\r\n\t\tmargin: 0;\r\n    border: none;\r\n\t\tbackground-color: #f5f7f7;\r\n\t\tbox-shadow: 0 0 5px #333;\r\n    max-inline-size: calc(100vw - 8px);\r\n    max-block-size: calc(100vh - 8px);\r\n    overflow: auto;\r\n    overscroll-behavior: contain;\r\n    isolation: isolate;\r\n\t  }\r\n\r\n    .context_menu ul[popover] {\r\n    inset: auto;\r\n    margin: 0;\r\n    border: none;\r\n    }\r\n\t  \r\n\t  .context_menu li {\r\n\t\tpadding: 0;\r\n\t\tpadding-right: 1.7em;\r\n\t\tcursor: pointer;\r\n\t\twhite-space: nowrap;\r\n\t\tdisplay: flex;\r\n\t\talign-items: center;\r\n\t  }\r\n\t  \r\n\t  .context_menu li:hover {\r\n\t\tbackground-color: #bbb;\r\n\t  }\r\n\t  \r\n\t  .context_menu li .context_menu_icon_span {\t\r\n\t\twidth: 28px;\r\n\t\tdisplay: inline-flex;\r\n\t\talign-items: center;\r\n\t\tjustify-content: center;\r\n\t  }\r\n\r\n\t  .context_menu li .context_menu_icon_span img {\t\r\n\t\theight: 18px;\r\n\t  }\r\n\r\n\t  .context_menu li .context_menu_text {\t\r\n\t\tpadding-left: 2px;\r\n\t\tvertical-align: middle;\r\n\t  }\r\n\t  \r\n\t  .context_menu li .context_menu_sub_span {\r\n\t\twidth: 1em;\r\n\t\tdisplay: inline-block;\r\n\t\ttext-align: center;\r\n\t\tposition: absolute;\r\n\t\ttop: 50%;\r\n\t\tright: 0.5em;\r\n\t\ttransform: translateY(-50%);\r\n\t  }\r\n\t  \r\n\t  .context_menu li>ul {\r\n\t\tposition: absolute;\r\n    inset: auto;\r\n\t\ttop: 0;\r\n\t\tleft: 100%;\r\n\t\topacity: 0;\r\n\t\ttransition: opacity 0.2s;\r\n\t\tvisibility: hidden;\r\n\t  }\r\n\r\n    .context_menu li>ul.context_menu_submenu_popover {\r\n    position: fixed;\r\n    }\r\n\t  \r\n    .context_menu li>ul:popover-open {\r\n\t\topacity: 1;\r\n\t\tvisibility: visible;\r\n\t  }\r\n\t  \r\n\t  .context_menu li.context_menu_divider {\r\n\t\tborder-bottom: 1px solid #aaa;\r\n\t\tmargin: 5px;\r\n\t\tpadding: 0;\r\n\t\tcursor: default;\r\n\t  }\r\n\t  \r\n\t  .context_menu li.context_menu_divider:hover {\r\n\t\tbackground-color: inherit;\r\n\t  }\r\n\t  \r\n\t  .context_menu li[disabled=\"\"] {\r\n\t\tcolor: #777;\r\n\t\tcursor: default;\r\n\t  }\r\n\t  \r\n\t  .context_menu li[disabled=\"\"]:hover {\r\n\t\tbackground-color: inherit;\r\n\t  }\r\n\t  \r\n\t  .context_menu li.context_menu_marked {\r\n\t\tbackground-color: #5ebdec;\r\n\t  }`;\r\n\r\n  static count = 0;\r\n\r\n  private static _openedContextMenus = new Set<ContextMenu>();\r\n\r\n  menu: IContextMenuItem[];\r\n  public options?: IContextMenuOptions;\r\n  public context: any\r\n  private num: number;\r\n  private _menuElement!: HTMLDivElement;\r\n\r\n  constructor(menu: IContextMenuItem[], options?: IContextMenuOptions, context?: any) {\r\n    this.num = ContextMenu.count++;\r\n    this.menu = menu;\r\n    this.options = options;\r\n    this.context = context;\r\n\r\n    this.reload();\r\n\r\n    this._windowDown = this._windowDown.bind(this);\r\n    this._windowKeyUp = this._windowKeyUp.bind(this);\r\n    this._windowResize = this._windowResize.bind(this);\r\n  }\r\n\r\n  reload() {\r\n    let shadowRoot = this.options?.shadowRoot ?? document;\r\n\r\n    if (this._menuElement == null) {\r\n      this._menuElement = document.createElement(\"div\");\r\n      this._menuElement.className = \"context_menu\";\r\n      this._menuElement.id = \"context_menu_\" + this.num;\r\n      this._menuElement.setAttribute('popover', 'manual');\r\n\r\n      if (shadowRoot === document)\r\n        document.body.appendChild(this._menuElement);\r\n      else\r\n        shadowRoot.appendChild(this._menuElement);\r\n    }\r\n\r\n    this._menuElement.innerHTML = \"\";\r\n\r\n    if (shadowRoot.adoptedStyleSheets.indexOf(ContextMenu._contextMenuCss) < 0) {\r\n      shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, ContextMenu._contextMenuCss];\r\n    }\r\n\r\n    this._menuElement.appendChild(this.renderLevel(this.menu));\r\n  }\r\n\r\n  renderLevel(level: IContextMenuItem[]) {\r\n    let ul_outer = document.createElement(\"ul\");\r\n\r\n    let addDivider = false;\r\n    level.forEach((item) => {\r\n      if (item.title !== '-') {\r\n        if (addDivider) {\r\n          let li = document.createElement(\"li\");\r\n          li.className = \"context_menu_divider\";\r\n          addDivider = false;\r\n          ul_outer.appendChild(li);\r\n        }\r\n\r\n        let li = document.createElement(\"li\");\r\n        let icon_span = document.createElement(\"span\");\r\n        icon_span.className = 'context_menu_icon_span';\r\n\r\n        if (item.checked === true) {\r\n          icon_span.innerHTML = '✔';\r\n        } else if ((item.icon ?? '') != '') {\r\n          icon_span.innerHTML = item.icon ?? '';\r\n        } else {\r\n          icon_span.innerHTML = this.options?.defaultIcon ?? '';\r\n        }\r\n\r\n\r\n\r\n        let text_span = document.createElement(\"span\");\r\n        text_span.className = 'context_menu_text';\r\n\r\n        text_span.innerHTML = item.title ?? '';\r\n\r\n        let sub_span = document.createElement(\"span\");\r\n        sub_span.className = 'context_menu_sub_span';\r\n\r\n        if (item.children != null) {\r\n          sub_span.innerHTML = this.options?.subIcon ?? '&#155;';\r\n        }\r\n\r\n        li.appendChild(icon_span);\r\n        li.appendChild(text_span);\r\n        li.appendChild(sub_span);\r\n\r\n        if (item.disabled) {\r\n          li.setAttribute(\"disabled\", \"\");\r\n        } else {\r\n          if (item.checkable) {\r\n            li.addEventListener('click', (e) => {\r\n              e.stopPropagation();\r\n              e.preventDefault();\r\n              item.checked = !item.checked;\r\n              icon_span.innerHTML = item.checked ? '✔' : (item.icon ?? this.options?.defaultIcon ?? '');\r\n              if (item.action)\r\n                item.action(e, item, this.context, this);\r\n            });\r\n          } else if (item.action)\r\n            li.addEventListener('click', (e) => {\r\n              e.stopPropagation();\r\n              e.preventDefault();\r\n              item.action?.(e, item, this.context, this);\r\n              this.close();\r\n            });\r\n          if (this.options?.mode == 'undo') {\r\n            li.addEventListener('mouseup', (e) => {\r\n              e.stopPropagation();\r\n              item.action?.(e, item, this.context, this);\r\n              this.close();\r\n            });\r\n          }\r\n\r\n          li.addEventListener('mouseenter', () => {\r\n            this.closeSiblingSubmenus(li);\r\n            if (this.options?.mode == 'undo') {\r\n              this.markUndoItems(li);\r\n            }\r\n          });\r\n\r\n          if (item.children != null) {\r\n            let childmenu = this.renderLevel(item.children);\r\n            this.configurePopoverSubmenu(childmenu);\r\n            li.appendChild(childmenu);\r\n            li.addEventListener('mouseenter', () => {\r\n              this.openPopoverSubmenu(li, childmenu);\r\n            });\r\n          }\r\n        }\r\n        ul_outer.appendChild(li);\r\n      } else {\r\n        addDivider = true;\r\n      }\r\n    });\r\n\r\n    return ul_outer;\r\n  }\r\n\r\n  public display(event: MouseEvent) {\r\n    let menu = this._menuElement;\r\n\r\n    let mouseOffset = this.options?.mouseOffset != null ? this.options.mouseOffset : 2;\r\n\r\n    this.showPopover(menu);\r\n    this.positionMenu(menu, event.clientX, event.clientY, mouseOffset);\r\n\r\n    menu.classList.add(\"context_menu_display\");\r\n\r\n    event.preventDefault();\r\n\r\n    ContextMenu._openedContextMenus.add(this);\r\n\r\n    window.addEventListener(\"keyup\", this._windowKeyUp);\r\n    window.addEventListener(\"resize\", this._windowResize);\r\n    window.addEventListener(\"mousedown\", this._windowDown);\r\n    setTimeout(() => {\r\n      if (!ContextMenu._openedContextMenus.has(this))\r\n        return;\r\n      window.addEventListener(\"contextmenu\", this._windowDown);\r\n    }, 150);\r\n  }\r\n\r\n  _windowResize() {\r\n    this.close();\r\n  }\r\n\r\n  _windowDown(e: MouseEvent) {\r\n    e.preventDefault();\r\n    if (!(e.target instanceof Node) || !this._menuElement.contains(e.target))\r\n      this.close();\r\n    return false;\r\n  }\r\n\r\n  _windowKeyUp(e: KeyboardEvent) {\r\n    if (e.key === 'Escape') {\r\n      this.close();\r\n    }\r\n  }\r\n\r\n  static show(menu: IContextMenuItem[], event: MouseEvent, options?: IContextMenuOptions, context?: any) {\r\n    let ctxMenu = new ContextMenu(menu, options, context);\r\n    ctxMenu.display(event);\r\n    return ctxMenu;\r\n  }\r\n\r\n  close() {\r\n    this.hideDescendantSubmenus(this._menuElement);\r\n    this.hidePopover(this._menuElement);\r\n    this._menuElement.remove();\r\n    window.removeEventListener(\"mousedown\", this._windowDown);\r\n    window.removeEventListener(\"resize\", this._windowResize);\r\n    window.removeEventListener(\"keyup\", this._windowKeyUp);\r\n    setTimeout(() => window.removeEventListener(\"contextmenu\", this._windowDown), 10);\r\n    ContextMenu._openedContextMenus.delete(this);\r\n  }\r\n\r\n  static closeAll() {\r\n    for (const c of ContextMenu._openedContextMenus.values())\r\n      c.close();\r\n  }\r\n\r\n  private configurePopoverSubmenu(childmenu: HTMLUListElement) {\r\n    childmenu.classList.add('context_menu_submenu_popover');\r\n    childmenu.setAttribute('popover', 'manual');\r\n  }\r\n\r\n  private markUndoItems(li: HTMLLIElement) {\r\n    if (li.parentElement == null)\r\n      return;\r\n\r\n    let select = true;\r\n    for (let node of li.parentElement.children) {\r\n      if (select)\r\n        (<HTMLElement>node).classList.add('context_menu_marked')\r\n      else\r\n        (<HTMLElement>node).classList.remove('context_menu_marked')\r\n      if (node == li)\r\n        select = false\r\n    }\r\n  }\r\n\r\n  private closeSiblingSubmenus(li: HTMLLIElement) {\r\n    if (li.parentElement == null)\r\n      return;\r\n\r\n    for (const node of li.parentElement.children) {\r\n      if (node !== li) {\r\n        this.hideDescendantSubmenus(node as HTMLElement);\r\n      }\r\n    }\r\n  }\r\n\r\n  private hideDescendantSubmenus(element: HTMLElement) {\r\n    const submenus = element.querySelectorAll('ul[popover]');\r\n    for (const submenu of submenus) {\r\n      this.hidePopover(submenu as HTMLElement);\r\n    }\r\n  }\r\n\r\n  private openPopoverSubmenu(li: HTMLLIElement, childmenu: HTMLUListElement) {\r\n    this.showPopover(childmenu);\r\n    this.positionSubmenuPopover(li, childmenu);\r\n  }\r\n\r\n  private showPopover(element: HTMLElement) {\r\n    if (this.isPopoverOpen(element))\r\n      return;\r\n\r\n    element.showPopover();\r\n  }\r\n\r\n  private hidePopover(element?: HTMLElement) {\r\n    if (element == null || !this.isPopoverOpen(element))\r\n      return;\r\n\r\n    element.hidePopover();\r\n  }\r\n\r\n  private isPopoverOpen(element: HTMLElement) {\r\n    return element.matches(':popover-open');\r\n  }\r\n\r\n  private positionMenu(menu: HTMLDivElement, clickCoordsX: number, clickCoordsY: number, mouseOffset: number) {\r\n    const menuWidth = menu.offsetWidth + 4;\r\n    const menuHeight = menu.offsetHeight + 4;\r\n\r\n    const spaceRight = window.innerWidth - clickCoordsX;\r\n    const spaceLeft = clickCoordsX;\r\n    const spaceBelow = window.innerHeight - clickCoordsY;\r\n    const spaceAbove = clickCoordsY;\r\n\r\n    let left = clickCoordsX + mouseOffset;\r\n    if (spaceRight < menuWidth && spaceLeft > spaceRight) {\r\n      left = clickCoordsX - menuWidth - mouseOffset;\r\n    }\r\n\r\n    let top = clickCoordsY + mouseOffset;\r\n    if (spaceBelow < menuHeight && spaceAbove > spaceBelow) {\r\n      top = clickCoordsY - menuHeight - mouseOffset;\r\n    }\r\n\r\n    menu.style.left = `${Math.max(0, Math.min(left, window.innerWidth - menuWidth))}px`;\r\n    menu.style.top = `${Math.max(0, Math.min(top, window.innerHeight - menuHeight))}px`;\r\n  }\r\n\r\n  private positionSubmenuPopover(li: HTMLLIElement, childmenu: HTMLUListElement) {\r\n    const parentRect = li.getBoundingClientRect();\r\n    const childRect = childmenu.getBoundingClientRect();\r\n    const menuWidth = childmenu.offsetWidth || childRect.width;\r\n    const menuHeight = childmenu.offsetHeight || childRect.height;\r\n\r\n    const spaceRight = window.innerWidth - parentRect.right;\r\n    const spaceLeft = parentRect.left;\r\n    let left = parentRect.right;\r\n    if (spaceRight < menuWidth && spaceLeft > spaceRight) {\r\n      left = parentRect.left - menuWidth;\r\n    }\r\n\r\n    const spaceBelow = window.innerHeight - parentRect.top;\r\n    const spaceAbove = parentRect.bottom;\r\n    let top = parentRect.top;\r\n    if (spaceBelow < menuHeight && spaceAbove > spaceBelow) {\r\n      top = parentRect.bottom - menuHeight;\r\n    }\r\n\r\n    childmenu.style.left = `${Math.max(0, Math.min(left, window.innerWidth - menuWidth))}px`;\r\n    childmenu.style.top = `${Math.max(0, Math.min(top, window.innerHeight - menuHeight))}px`;\r\n    childmenu.style.right = 'auto';\r\n    childmenu.style.bottom = 'auto';\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/contextMenu/IContextMenuItem.ts",
    "content": "export interface IContextMenu {\r\n\tclose: () => void;\r\n};\r\n\r\nexport interface IContextMenuItem {\r\n\treadonly id?: string,\r\n\treadonly title?: string,\r\n\treadonly icon?: string,\r\n\treadonly children?: IContextMenuItem[],\r\n\treadonly disabled?: boolean,\r\n\treadonly shortCut?: string;\r\n\treadonly checkable?: boolean;\r\n\tchecked?: boolean;\r\n\r\n\taction?: (event: MouseEvent, item: IContextMenuItem, context?: any, menu?: IContextMenu) => void\r\n};"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/getBoxQuads.global.d.ts",
    "content": "export { };\r\n\r\ndeclare global {\r\n    interface Node {\r\n        convertQuadFromNode(quad: DOMQuadInit, from: Element, options?: { fromBox?: 'margin' | 'border' | 'padding' | 'content', toBox?: 'margin' | 'border' | 'padding' | 'content', iframes?: HTMLIFrameElement[] }): DOMQuad\r\n        convertRectFromNode(rect: {x: number, y: number, width: number, height: number}, from: Element, options?: { fromBox?: 'margin' | 'border' | 'padding' | 'content', toBox?: 'margin' | 'border' | 'padding' | 'content', iframes?: HTMLIFrameElement[] }): DOMQuad\r\n        convertPointFromNode(point: DOMPointInit, from: Element, options?: { fromBox?: 'margin' | 'border' | 'padding' | 'content', toBox?: 'margin' | 'border' | 'padding' | 'content', iframes?: HTMLIFrameElement[] }): DOMPoint\r\n        getBoxQuads(options?: { box?: 'margin' | 'border' | 'padding' | 'content', relativeTo?: Element, iframes?: HTMLIFrameElement[] }): DOMQuad[]\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/getBoxQuads.js",
    "content": "//todo:\r\n//transform-box (SVGs)  https://developer.mozilla.org/en-US/docs/Web/CSS/transform-box\r\n\r\n// =============================================================================\r\n// PERFORMANCE FIXES APPLIED (14 total):\r\n//\r\n// FIX 1  – getElementCombinedTransform: check parent perspective inside fast-path\r\n//           so identity elements return immediately on non-3D pages.\r\n// FIX 2  – getResultingTransformationBetweenElementAndAllAncestors: guard initial\r\n//           self-transform multiply with isIdentity check.\r\n// FIX 3  – All translate matrix calls: skip new DOMMatrix().translateSelf(0,0)\r\n//           when both offsets are zero (hot loop, flat layouts).\r\n// FIX 4  – getElementOffsetsInContainer/HTMLElement: defer getCachedComputedStyle\r\n//           until inside the `includeScroll` branch where it's actually needed.\r\n// FIX 5  – getResultingTransformationBetweenElementAndAllAncestors: guard\r\n//           projectTo2D calls with !is2D check to skip style reads on 2D pages.\r\n// FIX 6  – getResultingTransformationBetweenElementAndAllAncestors: cache the\r\n//           result on the early-return (ancestor found) path, not just fallthrough.\r\n// FIX 7  – getBoxQuads: reuse the Range already created for the multi-fragment\r\n//           text check; pass clientRects/boundingRect into getElementSize to\r\n//           avoid creating a second Range for Text nodes.\r\n// FIX 8  – getBoxQuads: split the per-point `!o` branch out of the loop into\r\n//           two separate loops to eliminate the branch test on every iteration.\r\n// FIX 9  – getBoxQuads: hoist parseFloat calls for box offsets (margin/padding/\r\n//           content) so values are computed once, not 4x inside the loop.\r\n// FIX 10 – getElementCombinedTransform: check hasTransform first (most common\r\n//           non-identity case) so cheaper checks fire before heavier ones.\r\n// FIX 11 – getElementCombinedTransform: skip transform-origin wrap/unwrap when\r\n//           origin is (0, 0, 0), saving two DOMMatrix allocations.\r\n// FIX 12 – getResultingTransformationBetweenElementAndAllAncestors: reuse\r\n//           getElementCombinedTransform result across loop iterations (carry\r\n//           parent transform forward instead of recomputing next iteration).\r\n// FIX 13 – Repeated cross-realm instanceof checks: cache constructors from the\r\n//           node's window at function entry to avoid repeated property lookups.\r\n//           (Applied in getElementSize and getBoxQuads hot paths.)\r\n// FIX 14 – transformPointBox: parse style values once per call, not once per\r\n//           property access (avoids repeated parseFloat on shared string props).\r\n// =============================================================================\r\n\r\n/**\r\n* @param {globalThis} windowObj?\r\n* @param {boolean=} force\r\n*/\r\nexport function addPolyfill(windowObj = window, force = false) {\r\n    windowObj.__getBoxQuadsPolyfillFns ??= {};\r\n    windowObj.__getBoxQuadsPolyfillFns.getBoxQuads = getBoxQuads;\r\n    windowObj.__getBoxQuadsPolyfillFns.convertQuadFromNode = convertQuadFromNode;\r\n    windowObj.__getBoxQuadsPolyfillFns.convertRectFromNode = convertRectFromNode;\r\n    windowObj.__getBoxQuadsPolyfillFns.convertPointFromNode = convertPointFromNode;\r\n\r\n    if (force || !windowObj.Node.prototype.getBoxQuads) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.getBoxQuads = function (options) {\r\n            return windowObj.__getBoxQuadsPolyfillFns.getBoxQuads(this, options)\r\n        }\r\n    }\r\n\r\n    if (force || !windowObj.Node.prototype.convertQuadFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertQuadFromNode = function (quad, from, options) {\r\n            return windowObj.__getBoxQuadsPolyfillFns.convertQuadFromNode(this, quad, from, options)\r\n        }\r\n    }\r\n\r\n    if (force || !windowObj.Node.prototype.convertRectFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertRectFromNode = function (rect, from, options) {\r\n            return windowObj.__getBoxQuadsPolyfillFns.convertRectFromNode(this, rect, from, options)\r\n        }\r\n    }\r\n\r\n    if (force || !windowObj.Node.prototype.convertPointFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertPointFromNode = function (point, from, options) {\r\n            return windowObj.__getBoxQuadsPolyfillFns.convertPointFromNode(this, point, from, options)\r\n        }\r\n    }\r\n}\n/**\n* @param {globalThis} windowObj?\r\n*/\r\nexport function patchAdoptNode(windowObj = window) {\r\n    if (!windowObj.Node.prototype.getBoxQuads) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.getBoxQuads = function (options) {\r\n            return getBoxQuads(this, options)\r\n        }\r\n    }\r\n\r\n    if (!windowObj.Node.prototype.convertQuadFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertQuadFromNode = function (quad, from, options) {\r\n            return convertQuadFromNode(this, quad, from, options)\r\n        }\r\n    }\r\n\r\n    if (!windowObj.Node.prototype.convertRectFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertRectFromNode = function (rect, from, options) {\r\n            return convertRectFromNode(this, rect, from, options)\r\n        }\r\n    }\r\n\r\n    if (!windowObj.Node.prototype.convertPointFromNode) {\r\n        //@ts-ignore\r\n        windowObj.Node.prototype.convertPointFromNode = function (point, from, options) {\r\n            return convertPointFromNode(this, point, from, options)\r\n        }\r\n    }\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {DOMQuadInit} quad\r\n* @param {Element} from\r\n* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options\r\n* @returns {DOMQuad}\r\n*/\r\nexport function convertQuadFromNode(node, quad, from, options) {\r\n    const ancestor = (node.ownerDocument.defaultView ?? window).document.body;\r\n    const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options?.iframes, true);\r\n    const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options?.iframes, true).inverse();\r\n    if (options?.fromBox && options?.fromBox !== 'border') {\r\n        const fromStyle = getCachedComputedStyle(from);\r\n        quad = new DOMQuad(transformPointBox(quad.p1, options.fromBox, fromStyle, -1), transformPointBox(quad.p2, options.fromBox, fromStyle, -1), transformPointBox(quad.p3, options.fromBox, fromStyle, -1), transformPointBox(quad.p4, options.fromBox, fromStyle, -1))\r\n    }\r\n    let res = new DOMQuad(m2.transformPoint(m1.transformPoint(quad.p1)), m2.transformPoint(m1.transformPoint(quad.p2)), m2.transformPoint(m1.transformPoint(quad.p3)), m2.transformPoint(m1.transformPoint(quad.p4)));\r\n    if (options?.toBox && options?.toBox !== 'border' && (node instanceof Element || node instanceof (node.ownerDocument.defaultView ?? window).Element)) {\r\n        const nodeStyle = getCachedComputedStyle(node);\r\n        res = new DOMQuad(transformPointBox(res.p1, options.toBox, nodeStyle, -1), transformPointBox(res.p2, options.toBox, nodeStyle, -1), transformPointBox(res.p3, options.toBox, nodeStyle, -1), transformPointBox(res.p4, options.toBox, nodeStyle, -1))\r\n    }\r\n    return res;\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {{x: number, y: number, width: number, height: number}} rect\r\n* @param {Element} from\r\n* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options\r\n* @returns {DOMQuad}\r\n*/\r\nexport function convertRectFromNode(node, rect, from, options) {\r\n    const ancestor = (node.ownerDocument.defaultView ?? window).document.body.parentElement;\r\n    const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options?.iframes, true);\r\n    const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options?.iframes, true).inverse();\r\n    if (options?.fromBox && options?.fromBox !== 'border') {\r\n        const p = transformPointBox(new DOMPoint(rect.x, rect.y), options.fromBox, getCachedComputedStyle(from), 1);\r\n        rect = new DOMRect(p.x, p.y, rect.width, rect.height);\r\n    }\r\n    let res = new DOMQuad(m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x, rect.y))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x + rect.width, rect.y))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x + rect.width, rect.y + rect.height))), m2.transformPoint(m1.transformPoint(new DOMPoint(rect.x, rect.y + rect.height))));\r\n    if (options?.toBox && options?.toBox !== 'border' && (node instanceof Element || node instanceof (node.ownerDocument.defaultView ?? window).Element)) {\r\n        const nodeStyle = getCachedComputedStyle(node);\r\n        res = new DOMQuad(transformPointBox(res.p1, options.toBox, nodeStyle, -1), transformPointBox(res.p2, options.toBox, nodeStyle, -1), transformPointBox(res.p3, options.toBox, nodeStyle, -1), transformPointBox(res.p4, options.toBox, nodeStyle, -1))\r\n    }\r\n    return res;\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {DOMPointInit} point\r\n* @param {Element} from\r\n* @param {{fromBox?: 'margin'|'border'|'padding'|'content', toBox?: 'margin'|'border'|'padding'|'content', iframes?: HTMLIFrameElement[]}=} options\r\n* @returns {DOMPoint}\r\n*/\r\nexport function convertPointFromNode(node, point, from, options) {\r\n    const ancestor = (node.ownerDocument.defaultView ?? window).document.body.parentElement;\r\n    const m1 = getResultingTransformationBetweenElementAndAllAncestors(from, ancestor, options?.iframes, true);\r\n    const m2 = getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, options?.iframes, true).inverse();\r\n    if (options?.fromBox && options?.fromBox !== 'border') {\r\n        point = transformPointBox(point, options.fromBox, getCachedComputedStyle(from), 1);\r\n    }\r\n    let res = m2.transformPoint(m1.transformPoint(point));\r\n    if (options?.toBox && options?.toBox !== 'border' && (node instanceof Element || node instanceof (node.ownerDocument.defaultView ?? window).Element)) {\r\n        res = transformPointBox(res, options.toBox, getCachedComputedStyle(node), -1);\r\n    }\r\n    return res;\r\n}\r\n/**\r\n* @param {DOMPointInit} point\r\n* @param {'margin'|'border'|'padding'|'content'} box\r\n* @param {CSSStyleDeclaration} style\r\n* @param {number} operator\r\n* @returns {DOMPoint}\r\n*\r\n* FIX 14: Parse each style value once and reuse, rather than calling parseFloat\r\n*          multiple times on the same property (e.g. borderLeftWidth used twice in 'content').\r\n*/\r\nfunction transformPointBox(point, box, style, operator) {\r\n    if (box === 'margin') {\r\n        const mLeft = parseFloat(style.marginLeft);\r\n        const mTop  = parseFloat(style.marginTop);\r\n        return new DOMPoint(point.x - operator * mLeft, point.y - operator * mTop);\r\n    } else if (box === 'padding') {\r\n        const bLeft = parseFloat(style.borderLeftWidth);\r\n        const bTop  = parseFloat(style.borderTopWidth);\r\n        return new DOMPoint(point.x + operator * bLeft, point.y + operator * bTop);\r\n    } else if (box === 'content') {\r\n        const bLeft = parseFloat(style.borderLeftWidth);\r\n        const bTop  = parseFloat(style.borderTopWidth);\r\n        const pLeft = parseFloat(style.paddingLeft);\r\n        const pTop  = parseFloat(style.paddingTop);\r\n        return new DOMPoint(point.x + operator * (bLeft + pLeft), point.y + operator * (bTop + pTop));\r\n    }\r\n    //@ts-ignore\r\n    return point;\r\n}\r\n/** @type { WeakMap<Node, number> } */\r\nlet hash;\r\n/** @type { Map<string, DOMQuad[]> } */\r\nlet boxQuadsCache;\r\n/** @type { Map<string, DOMMatrix> } */\r\nlet transformCache;\r\n/** @type { WeakMap<Node, CSSStyleDeclaration> } */\r\nlet computedStyleCache;\r\nlet hashId = 0;\r\n\r\nexport function clearCache() {\r\n    boxQuadsCache.clear();\r\n    transformCache.clear();\r\n    computedStyleCache = new WeakMap();\r\n}\r\n\r\nexport function useCache() {\r\n    hash = new WeakMap();\r\n    boxQuadsCache = new Map();\r\n    transformCache = new Map();\r\n    computedStyleCache = new WeakMap();\r\n}\r\n/**\r\n* @param {Element} element\r\n* @returns {CSSStyleDeclaration}\r\n*/\r\nfunction getCachedComputedStyle(element) {\r\n    if (!computedStyleCache) {\r\n        return (element.ownerDocument.defaultView ?? window).getComputedStyle(element);\r\n    }\r\n    let style = computedStyleCache.get(element);\r\n    if (!style) {\r\n        style = (element.ownerDocument.defaultView ?? window).getComputedStyle(element);\r\n        computedStyleCache.set(element, style);\r\n    }\r\n    return style;\r\n}\r\n/**\r\n* @param {Node} node\r\n* @returns {boolean}\r\n*/\r\nfunction isElementNode(node) {\r\n    return !!node && node.nodeType === Node.ELEMENT_NODE;\r\n}\r\n/**\r\n* @param {Node} element\r\n* @returns {number}\r\n*/\r\nfunction getElementZoom(element) {\r\n    if (!isElementNode(element)) {\r\n        return 1;\r\n    }\r\n    /** @type {Element} */\r\n    // @ts-ignore\r\n    const actualElement = element;\r\n    const zoom = getCachedComputedStyle(actualElement).zoom;\r\n    if (!zoom || zoom === 'normal') {\r\n        return 1;\r\n    }\r\n    if (zoom.endsWith('%')) {\r\n        const percentage = parseFloat(zoom);\r\n        return Number.isFinite(percentage) && percentage > 0 ? percentage / 100 : 1;\r\n    }\r\n    const value = parseFloat(zoom);\r\n    return Number.isFinite(value) && value > 0 ? value : 1;\r\n}\r\n/**\r\n* `zoom` scales the element's internal coordinate space while also shifting the\r\n* element within its parent from the top-center anchor in the default writing-mode.\r\n* For descendant coordinates we keep the scale in the matrix pipeline and apply\r\n* the parent-position shift separately in the layout translation step.\r\n* @param {Node} element\r\n* @returns {DOMMatrix}\r\n*/\r\nfunction getElementZoomScaleTransform(element) {\r\n    if (!isElementNode(element)) {\r\n        return new DOMMatrix();\r\n    }\r\n    const zoom = getElementZoom(element);\r\n    if (zoom === 1) {\r\n        return new DOMMatrix();\r\n    }\r\n    return new DOMMatrix().scaleSelf(zoom);\r\n}\r\n/**\r\n* @param {Node} element\r\n* @param {HTMLIFrameElement[]=} iframes\r\n* @param {boolean=} includeZoom\r\n* @returns {DOMMatrix}\r\n*/\r\nfunction getElementTransformWithZoom(element, iframes, includeZoom = true) {\r\n    const transform = getElementCombinedTransform(element, iframes);\r\n    if (!includeZoom || !isElementNode(element)) {\r\n        return transform;\r\n    }\r\n    const zoomTransform = getElementZoomScaleTransform(element);\r\n    if (zoomTransform.isIdentity) {\r\n        return transform;\r\n    }\r\n    // `zoom` scales transform translations as well as the element's local axes,\r\n    // so it needs to wrap the transform matrix instead of being appended to it.\r\n    return zoomTransform.multiply(transform);\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {{box?: 'margin'|'border'|'padding'|'content', relativeTo?: Element, iframes?: HTMLIFrameElement[]}=} options\r\n* @returns {DOMQuad[]}\r\n*/\r\nexport function getBoxQuads(node, options) {\r\n    const defaultRelativeTo = node.ownerDocument.documentElement ?? node.ownerDocument.body;\r\n    const relativeTo = options?.relativeTo ?? defaultRelativeTo;\r\n    let key;\r\n    if (boxQuadsCache) {\r\n        let i1 = hash.get(node);\r\n        if (i1 === undefined)\r\n            hash.set(node, i1 = hashId++);\r\n        let i2 = hash.get(relativeTo);\r\n        if (i2 === undefined)\r\n            hash.set(relativeTo, i2 = hashId++);\r\n        key = i1 + '_' + i2 + '_' + (options?.box ?? 'border');\r\n        const q = boxQuadsCache.get(key);\r\n        if (q)\r\n            return q;\r\n    }\r\n\r\n    /** @type {DOMMatrix} */\r\n    let originalElementAndAllParentsMultipliedMatrix = getResultingTransformationBetweenElementAndAllAncestors(node, relativeTo, options?.iframes);\r\n\r\n    // FIX 13: Cache cross-realm constructors once per call.\r\n    const win = node.ownerDocument.defaultView ?? window;\r\n    const _Text = win.Text;\r\n    const _SVGGraphicsElement = win.SVGGraphicsElement;\r\n    const _SVGSVGElement = win.SVGSVGElement;\r\n\r\n    // For text nodes, check for multiple fragments (multi-column layout, line-wrapping).\r\n    // getClientRects() returns one rect per line-box fragment; getBoundingClientRect()\r\n    // only returns the union AABB, so without this we'd always get one quad.\r\n    if ((node instanceof Text || node instanceof _Text)) {\r\n        const canUseViewportTextRects = originalElementAndAllParentsMultipliedMatrix.is2D\r\n            && Math.abs(originalElementAndAllParentsMultipliedMatrix.b) < 1e-10\r\n            && Math.abs(originalElementAndAllParentsMultipliedMatrix.c) < 1e-10;\r\n        // FIX 7: Create the Range once here and reuse it for both the multi-fragment\r\n        //        check and the single-fragment size fallback, avoiding a second Range\r\n        //        allocation inside getElementSize for Text nodes.\r\n        const range = node.ownerDocument.createRange();\r\n        range.selectNodeContents(node);\r\n        const clientRects = range.getClientRects();\r\n        const viewportRoot = node.ownerDocument.documentElement ?? node.ownerDocument.body;\r\n        const viewportRootRect = viewportRoot?.getBoundingClientRect();\r\n\r\n        const convertViewportRectToRelativeQuad = (rect) => {\r\n            if (relativeTo === viewportRoot) {\r\n                return new DOMQuad(\r\n                    new DOMPoint(rect.x, rect.y),\r\n                    new DOMPoint(rect.x + rect.width, rect.y),\r\n                    new DOMPoint(rect.x + rect.width, rect.y + rect.height),\r\n                    new DOMPoint(rect.x, rect.y + rect.height),\r\n                );\r\n            }\r\n\r\n            const rectInViewportRoot = new DOMRect(\r\n                rect.x - (viewportRootRect?.x ?? 0),\r\n                rect.y - (viewportRootRect?.y ?? 0),\r\n                rect.width,\r\n                rect.height,\r\n            );\r\n            return convertRectFromNode(relativeTo, rectInViewportRoot, viewportRoot, {\r\n                iframes: options?.iframes,\r\n            });\r\n        };\r\n\r\n        if (clientRects.length > 1) {\r\n            if (canUseViewportTextRects) {\r\n                const quads = [];\r\n                for (const cr of clientRects) {\r\n                    if (cr.width < 1 && cr.height < 1) continue;\r\n                    quads.push(convertViewportRectToRelativeQuad(cr));\r\n                }\r\n                if (quads.length > 0) {\r\n                    if (boxQuadsCache) boxQuadsCache.set(key, quads);\r\n                    return quads;\r\n                }\r\n            }\r\n\r\n            // Work via the parent element so rotation is handled correctly.\r\n            // Each fragment's viewport rect (from getClientRects) is an AABB;\r\n            // its center equals the actual geometric center regardless of rotation.\r\n            // We convert that center to parent-local space, recover the fragment's\r\n            // local dimensions via the 2x2 AABB system, then apply the parent's\r\n            // accumulated matrix to build proper (rotated) quads in relativeTo-space.\r\n            const parent = getParentElementIncludingSlots(node, options?.iframes);\r\n            const M_parent = getResultingTransformationBetweenElementAndAllAncestors(parent, relativeTo, options?.iframes);\r\n            const parentCss = getElementCombinedTransform(parent, options?.iframes);\r\n            const pr = parent.getBoundingClientRect();\r\n            const pa = parentCss.a, pb = parentCss.b, pc = parentCss.c, pd = parentCss.d;\r\n            // AABB center of the transformed parent equals its geometric center.\r\n            // geometric_center_screen = screen(0,0) + L * (pw/2, ph/2)\r\n            // => screen(0,0) = AABB_center - L * (pw/2, ph/2)\r\n            //@ts-ignore\r\n            const pw = parent.offsetWidth;\r\n            //@ts-ignore\r\n            const ph = parent.offsetHeight;\r\n            const parentOriginX = (pr.x + pr.width  / 2) - (pa * pw / 2 + pc * ph / 2);\r\n            const parentOriginY = (pr.y + pr.height / 2) - (pb * pw / 2 + pd * ph / 2);\r\n            const linearDet = pa * pd - pb * pc;\r\n            const absA = Math.abs(pa), absB = Math.abs(pb);\r\n            const absDet = absA * absA - absB * absB;\r\n\r\n            const quads = [];\r\n            for (const cr of clientRects) {\r\n                if (cr.width < 1 && cr.height < 1) continue;\r\n\r\n                // Fragment AABB center -> parent-local center via inverse CSS transform\r\n                const dx = cr.x + cr.width  / 2 - parentOriginX;\r\n                const dy = cr.y + cr.height / 2 - parentOriginY;\r\n                let lcx, lcy;\r\n                if (Math.abs(linearDet) > 1e-10) {\r\n                    lcx = (pd * dx - pc * dy) / linearDet;\r\n                    lcy = (pa * dy - pb * dx) / linearDet;\r\n                } else {\r\n                    lcx = dx; lcy = dy;\r\n                }\r\n\r\n                // Fragment dimensions in parent-local via 2x2 AABB system\r\n                let tw, th;\r\n                if (Math.abs(absDet) > 1e-6) {\r\n                    tw = Math.max(0, (absA * cr.width  - absB * cr.height) / absDet);\r\n                    th = Math.max(0, (absA * cr.height - absB * cr.width)  / absDet);\r\n                } else {\r\n                    // Singular (~45 deg): use CSS line-height as th\r\n                    const cs = getCachedComputedStyle(parent);\r\n                    th = Math.max(0, parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16);\r\n                    const denom = Math.max(absA, absB);\r\n                    tw = denom > 1e-6 ? Math.max(0, (cr.width - th * absB) / denom) : cr.width;\r\n                }\r\n\r\n                // Fragment top-left in parent-local, then transform all 4 corners via M_parent\r\n                const lx = lcx - tw / 2, ly = lcy - th / 2;\r\n                const quad = new DOMQuad(\r\n                    M_parent.transformPoint(new DOMPoint(lx,      ly)),\r\n                    M_parent.transformPoint(new DOMPoint(lx + tw, ly)),\r\n                    M_parent.transformPoint(new DOMPoint(lx + tw, ly + th)),\r\n                    M_parent.transformPoint(new DOMPoint(lx,      ly + th))\r\n                );\r\n                quads.push(toViewportRelativeDocumentElementQuad(quad, node, relativeTo, options?.iframes));\r\n            }\r\n\r\n            if (quads.length > 0) {\r\n                if (boxQuadsCache) boxQuadsCache.set(key, quads);\r\n                return quads;\r\n            }\r\n        }\r\n\r\n        // FIX 7 (continued): Single-fragment text — reuse the already-fetched\r\n        // bounding rect so getElementSize doesn't create a second Range.\r\n        const textBoundingRect = range.getBoundingClientRect();\r\n        if (canUseViewportTextRects) {\r\n            const tQuad = [convertViewportRectToRelativeQuad(textBoundingRect)];\r\n            if (boxQuadsCache) boxQuadsCache.set(key, tQuad);\r\n            return tQuad;\r\n        }\r\n        const { width: tw, height: th } = _getTextNodeSize(originalElementAndAllParentsMultipliedMatrix, textBoundingRect, node);\r\n        const is2Dt = originalElementAndAllParentsMultipliedMatrix.is2D;\r\n        const tCorners = [\r\n            new DOMPoint(0,  0),\r\n            new DOMPoint(tw, 0),\r\n            new DOMPoint(tw, th),\r\n            new DOMPoint(0,  th),\r\n        ];\r\n        /** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */\r\n        //@ts-ignore\r\n        const tPoints = Array(4);\r\n        if (is2Dt) {\r\n            for (let i = 0; i < 4; i++)\r\n                tPoints[i] = tCorners[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n        } else {\r\n            for (let i = 0; i < 4; i++) {\r\n                tPoints[i] = projectPoint(tCorners[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n                tPoints[i] = as2DPoint(tPoints[i]);\r\n            }\r\n        }\r\n        const tQuad = [toViewportRelativeDocumentElementQuad(new DOMQuad(tPoints[0], tPoints[1], tPoints[2], tPoints[3]), node, relativeTo, options?.iframes)];\r\n        if (boxQuadsCache) boxQuadsCache.set(key, tQuad);\r\n        return tQuad;\r\n    }\r\n\r\n    if ((node instanceof SVGGraphicsElement || node instanceof _SVGGraphicsElement)\n        && !((node instanceof SVGSVGElement || node instanceof _SVGSVGElement))) {\n        const bbox = node.getBBox();\n        const visualBox = getSvgVisualBox(node, bbox);\n        const x0 = visualBox.x - bbox.x;\n        const y0 = visualBox.y - bbox.y;\n        const x1 = x0 + visualBox.width;\n        const y1 = y0 + visualBox.height;\n        const screenPts = [\n            new DOMPoint(visualBox.x, visualBox.y),\n            new DOMPoint(visualBox.x + visualBox.width, visualBox.y),\n            new DOMPoint(visualBox.x + visualBox.width, visualBox.y + visualBox.height),\n            new DOMPoint(visualBox.x, visualBox.y + visualBox.height),\n        ];\n        const pts = [\n            new DOMPoint(x0, y0),\n            new DOMPoint(x1, y0),\n            new DOMPoint(x1, y1),\n            new DOMPoint(x0, y1),\n        ];\n        const screenCtm = !hasTransformedHtmlAncestor(node, relativeTo, options?.iframes) ? node.getScreenCTM() : null;\n        if (screenCtm) {\n            const screenQuad = new DOMQuad(\n                screenPts[0].matrixTransform(screenCtm),\n                screenPts[1].matrixTransform(screenCtm),\n                screenPts[2].matrixTransform(screenCtm),\n                screenPts[3].matrixTransform(screenCtm),\n            );\n            const svgQuad = [convertViewportQuadToRelativeNode(screenQuad, node, relativeTo, options?.iframes)];\n            if (boxQuadsCache) boxQuadsCache.set(key, svgQuad);\n            return svgQuad;\n        }\n\n        /** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */\n        //@ts-ignore\n        const points = Array(4);\n        if (originalElementAndAllParentsMultipliedMatrix.is2D) {\n            for (let i = 0; i < 4; i++)\n                points[i] = pts[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);\n        } else {\n            for (let i = 0; i < 4; i++) {\n                points[i] = projectPoint(pts[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);\n                points[i] = as2DPoint(points[i]);\n            }\n        }\n        const svgQuad = [toViewportRelativeDocumentElementQuad(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options?.iframes)];\n        if (boxQuadsCache) boxQuadsCache.set(key, svgQuad);\n        return svgQuad;\n    }\n    if (\n        (node instanceof win.Element)\r\n        && relativeTo === node.ownerDocument.documentElement\r\n        && (!options?.box || options.box === 'border')\r\n        && originalElementAndAllParentsMultipliedMatrix.is2D\r\n        && Math.abs(originalElementAndAllParentsMultipliedMatrix.b) < 1e-10\r\n        && Math.abs(originalElementAndAllParentsMultipliedMatrix.c) < 1e-10\r\n    ) {\r\n        const rect = node.getBoundingClientRect();\r\n        const viewportQuad = [new DOMQuad(\r\n            new DOMPoint(rect.x, rect.y),\r\n            new DOMPoint(rect.x + rect.width, rect.y),\r\n            new DOMPoint(rect.x + rect.width, rect.y + rect.height),\r\n            new DOMPoint(rect.x, rect.y + rect.height),\r\n        )];\r\n        if (boxQuadsCache) boxQuadsCache.set(key, viewportQuad);\r\n        return viewportQuad;\r\n    }\r\n\r\n    let { width, height } = getElementSize(node, originalElementAndAllParentsMultipliedMatrix);\r\n\r\n    // FIX 13: cache cross-realm Element constructor.\r\n    const _Element = win.Element;\r\n    const is2D = originalElementAndAllParentsMultipliedMatrix.is2D;\r\n\r\n    // FIX 8 + FIX 9: Split the `!o` branch out of the point loop into two\r\n    // separate code paths. In the box-offset path, parse style values once\r\n    // (FIX 9) and use them directly, eliminating 4x redundant parseFloat calls.\r\n    if ((node instanceof Element || node instanceof _Element)) {\r\n        const box = options?.box;\r\n        if (box === 'margin' || box === 'padding' || box === 'content') {\r\n            const cs = getCachedComputedStyle(node);\r\n            let x0, y0, x1, y1, x2, y2, x3, y3;\r\n\r\n            if (box === 'margin') {\r\n                const mL = parseFloat(cs.marginLeft);\r\n                const mT = parseFloat(cs.marginTop);\r\n                const mR = parseFloat(cs.marginRight);\r\n                const mB = parseFloat(cs.marginBottom);\r\n                x0 = -mL;        y0 = -mT;\r\n                x1 = width + mR; y1 = -mT;\r\n                x2 = width + mR; y2 = height + mB;\r\n                x3 = -mL;        y3 = height + mB;\r\n            } else if (box === 'padding') {\r\n                const bL = parseFloat(cs.borderLeftWidth);\r\n                const bT = parseFloat(cs.borderTopWidth);\r\n                const bR = parseFloat(cs.borderRightWidth);\r\n                const bB = parseFloat(cs.borderBottomWidth);\r\n                x0 = bL;         y0 = bT;\r\n                x1 = width - bR; y1 = bT;\r\n                x2 = width - bR; y2 = height - bB;\r\n                x3 = bL;         y3 = height - bB;\r\n            } else { // content\r\n                const bL = parseFloat(cs.borderLeftWidth);\r\n                const bT = parseFloat(cs.borderTopWidth);\r\n                const bR = parseFloat(cs.borderRightWidth);\r\n                const bB = parseFloat(cs.borderBottomWidth);\r\n                const pL = parseFloat(cs.paddingLeft);\r\n                const pT = parseFloat(cs.paddingTop);\r\n                const pR = parseFloat(cs.paddingRight);\r\n                const pB = parseFloat(cs.paddingBottom);\r\n                x0 = bL + pL;            y0 = bT + pT;\r\n                x1 = width - bR - pR;    y1 = bT + pT;\r\n                x2 = width - bR - pR;    y2 = height - bB - pB;\r\n                x3 = bL + pL;            y3 = height - bB - pB;\r\n            }\r\n\r\n            const pts = [\r\n                new DOMPoint(x0, y0),\r\n                new DOMPoint(x1, y1),\r\n                new DOMPoint(x2, y2),\r\n                new DOMPoint(x3, y3),\r\n            ];\r\n            /** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */\r\n            //@ts-ignore\r\n            const points = Array(4);\r\n            if (is2D) {\r\n                for (let i = 0; i < 4; i++)\r\n                    points[i] = pts[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n            } else {\r\n                for (let i = 0; i < 4; i++) {\r\n                    points[i] = projectPoint(pts[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n                    points[i] = as2DPoint(points[i]);\r\n                }\r\n            }\r\n            const quad = [toViewportRelativeDocumentElementQuad(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options?.iframes)];\r\n            if (boxQuadsCache) boxQuadsCache.set(key, quad);\r\n            return quad;\r\n        }\r\n    }\r\n\r\n    // FIX 8: No-offset path — plain loop, no `!o` branch test per iteration.\r\n    const corners = [\r\n        new DOMPoint(0,     0),\r\n        new DOMPoint(width, 0),\r\n        new DOMPoint(width, height),\r\n        new DOMPoint(0,     height),\r\n    ];\r\n    /** @type {[DOMPoint,DOMPoint,DOMPoint,DOMPoint]} */\r\n    //@ts-ignore\r\n    const points = Array(4);\r\n    if (is2D) {\r\n        for (let i = 0; i < 4; i++)\r\n            points[i] = corners[i].matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n    } else {\r\n        for (let i = 0; i < 4; i++) {\r\n            points[i] = projectPoint(corners[i], originalElementAndAllParentsMultipliedMatrix).matrixTransform(originalElementAndAllParentsMultipliedMatrix);\r\n            points[i] = as2DPoint(points[i]);\r\n        }\r\n    }\r\n\r\n    const quad = [toViewportRelativeDocumentElementQuad(new DOMQuad(points[0], points[1], points[2], points[3]), node, relativeTo, options?.iframes)];\r\n    if (boxQuadsCache)\r\n        boxQuadsCache.set(key, quad);\r\n    return quad;\n}\n\nfunction convertViewportQuadToRelativeNode(quad, node, relativeTo, iframes) {\n    const viewportRoot = node.ownerDocument.documentElement ?? node.ownerDocument.body;\n    if (relativeTo === viewportRoot) {\n        return quad;\n    }\n\n    const win = node.ownerDocument.defaultView ?? window;\n    if (relativeTo instanceof win.SVGElement) {\n        const relativeScreenCtm = relativeTo.getScreenCTM();\n        if (relativeScreenCtm) {\n            const inverse = relativeScreenCtm.inverse();\n            return new DOMQuad(\n                quad.p1.matrixTransform(inverse),\n                quad.p2.matrixTransform(inverse),\n                quad.p3.matrixTransform(inverse),\n                quad.p4.matrixTransform(inverse),\n            );\n        }\n    }\n\n    if (relativeTo.ownerDocument === node.ownerDocument && relativeTo instanceof win.HTMLElement) {\n        const htmlQuad = convertViewportQuadToHtmlElement(quad, relativeTo, iframes);\n        if (htmlQuad) {\n            return htmlQuad;\n        }\n    }\n\n    if (relativeTo === node.ownerDocument.body) {\n        const relativeRect = relativeTo.getBoundingClientRect();\n        const scrollLeft = relativeTo.scrollLeft || 0;\n        const scrollTop = relativeTo.scrollTop || 0;\n        return new DOMQuad(\n            new DOMPoint(quad.p1.x - relativeRect.x + scrollLeft, quad.p1.y - relativeRect.y + scrollTop),\n            new DOMPoint(quad.p2.x - relativeRect.x + scrollLeft, quad.p2.y - relativeRect.y + scrollTop),\n            new DOMPoint(quad.p3.x - relativeRect.x + scrollLeft, quad.p3.y - relativeRect.y + scrollTop),\n            new DOMPoint(quad.p4.x - relativeRect.x + scrollLeft, quad.p4.y - relativeRect.y + scrollTop),\n        );\n    }\n\n    return convertQuadFromNode(relativeTo, quad, viewportRoot, { iframes });\n}\n\nfunction convertViewportQuadToHtmlElement(quad, relativeTo, iframes) {\n    const scale = getPositiveAxisAlignedViewportScale(relativeTo, iframes);\n    if (!scale) {\n        return null;\n    }\n\n    const relativeRect = relativeTo.getBoundingClientRect();\n    const scaleX = scale.x;\n    const scaleY = scale.y;\n    if (!Number.isFinite(scaleX) || !Number.isFinite(scaleY) || Math.abs(scaleX) < 1e-10 || Math.abs(scaleY) < 1e-10) {\n        return null;\n    }\n\n    const scrollLeft = relativeTo.scrollLeft || 0;\n    const scrollTop = relativeTo.scrollTop || 0;\n    const convertPoint = (point) => new DOMPoint(\n        (point.x - relativeRect.x) / scaleX + scrollLeft,\n        (point.y - relativeRect.y) / scaleY + scrollTop,\n    );\n\n    return new DOMQuad(\n        convertPoint(quad.p1),\n        convertPoint(quad.p2),\n        convertPoint(quad.p3),\n        convertPoint(quad.p4),\n    );\n}\n\nfunction getPositiveAxisAlignedViewportScale(element, iframes) {\n    const win = element.ownerDocument.defaultView ?? window;\n    const scale = { x: 1, y: 1 };\n    let current = element;\n    while (current && current !== element.ownerDocument.documentElement) {\n        if (current instanceof win.Element) {\n            const style = getCachedComputedStyle(current);\n            const transformScale = getPositiveAxisAlignedTransformScale(style);\n            if (!transformScale) {\n                return null;\n            }\n            const zoom = getElementZoom(current);\n            scale.x *= transformScale.x * zoom;\n            scale.y *= transformScale.y * zoom;\n        }\n        current = getParentElementIncludingSlots(current, iframes);\n    }\n    return scale;\n}\n\nfunction getPositiveAxisAlignedTransformScale(style) {\n    if (style.perspective && style.perspective !== 'none') {\n        return null;\n    }\n    if (style.rotate && style.rotate !== 'none' && !isZeroAngleValue(style.rotate)) {\n        return null;\n    }\n    const individualScale = parsePositiveScaleValue(style.scale);\n    if (!individualScale) {\n        return null;\n    }\n\n    const transform = style.transform;\n    if (!transform || transform === 'none') {\n        return individualScale;\n    }\n\n    if (transform.startsWith('matrix3d(')) {\n        const values = parseCssMatrixValues(transform);\n        if (values?.length === 16\n            && values[0] > 0\n            && values[5] > 0\n            && Math.abs(values[1]) < 1e-10\n            && Math.abs(values[4]) < 1e-10\n            && Math.abs(values[3]) < 1e-10\n            && Math.abs(values[7]) < 1e-10\n            && Math.abs(values[11]) < 1e-10) {\n            return { x: individualScale.x * values[0], y: individualScale.y * values[5] };\n        }\n        return null;\n    }\n\n    if (transform.startsWith('matrix(')) {\n        const values = parseCssMatrixValues(transform);\n        if (values?.length === 6\n            && values[0] > 0\n            && values[3] > 0\n            && Math.abs(values[1]) < 1e-10\n            && Math.abs(values[2]) < 1e-10) {\n            return { x: individualScale.x * values[0], y: individualScale.y * values[3] };\n        }\n        return null;\n    }\n\n    return null;\n}\n\nfunction parsePositiveScaleValue(value) {\n    if (!value || value === 'none') {\n        return { x: 1, y: 1 };\n    }\n    const parts = value.split(/\\s+/).map(part => parseFloat(part)).filter(Number.isFinite);\n    if (parts.length === 0 || parts[0] <= 0 || (parts[1] != null && parts[1] <= 0)) {\n        return null;\n    }\n    return { x: parts[0], y: parts[1] ?? parts[0] };\n}\n\nfunction parseCssMatrixValues(transform) {\n    const start = transform.indexOf('(');\n    const end = transform.lastIndexOf(')');\n    if (start < 0 || end <= start) {\n        return null;\n    }\n    const values = transform.slice(start + 1, end).split(',').map(value => parseFloat(value.trim()));\n    return values.every(Number.isFinite) ? values : null;\n}\n\nfunction isZeroAngleValue(value) {\n    const matches = value.match(/-?\\d*\\.?\\d+(?:e[-+]?\\d+)?/gi);\n    if (!matches?.length) {\n        return false;\n    }\n    const angle = parseFloat(matches[matches.length - 1]);\n    return Number.isFinite(angle) && Math.abs(angle) < 1e-10;\n}\n\nfunction hasTransformedHtmlAncestor(node, relativeTo, iframes) {\n    const win = node.ownerDocument.defaultView ?? window;\n    let element = getParentElementIncludingSlots(node, iframes);\n    while (element && element !== relativeTo && element !== node.ownerDocument.documentElement) {\n        if (element instanceof win.HTMLElement) {\n            const css = getCachedComputedStyle(element);\n            if (transformProperties.some((value) => css[value] ? css[value] !== 'none' : false)) {\n                return true;\n            }\n        }\n        element = getParentElementIncludingSlots(element, iframes);\n    }\n    return false;\n}\n\nfunction toViewportRelativeDocumentElementQuad(quad, node, relativeTo, iframes) {\n    if (relativeTo !== node.ownerDocument.documentElement) {\r\n        return quad;\r\n    }\r\n    if (isViewportFixedAnchoredNode(node, iframes)) {\r\n        return quad;\r\n    }\r\n\r\n    const win = node.ownerDocument.defaultView ?? window;\r\n    const scrollX = win.scrollX ?? node.ownerDocument.documentElement.scrollLeft ?? 0;\r\n    const scrollY = win.scrollY ?? node.ownerDocument.documentElement.scrollTop ?? 0;\r\n    if (scrollX === 0 && scrollY === 0) {\r\n        return quad;\r\n    }\r\n\r\n    return new DOMQuad(\r\n        new DOMPoint(quad.p1.x - scrollX, quad.p1.y - scrollY),\r\n        new DOMPoint(quad.p2.x - scrollX, quad.p2.y - scrollY),\r\n        new DOMPoint(quad.p3.x - scrollX, quad.p3.y - scrollY),\r\n        new DOMPoint(quad.p4.x - scrollX, quad.p4.y - scrollY),\r\n    );\r\n}\r\n\r\nfunction isViewportFixedAnchoredNode(node, iframes) {\r\n    const win = node.ownerDocument.defaultView ?? window;\r\n    let element = null;\r\n    if (node instanceof win.Text) {\r\n        element = getParentElementIncludingSlots(node, iframes);\r\n    } else if (node instanceof win.Element) {\r\n        element = node;\r\n    }\r\n\r\n    while (element && element !== node.ownerDocument.documentElement) {\r\n        const cs = getCachedComputedStyle(element);\r\n        if (cs.position === 'fixed') {\r\n            return getNearestFixedContainingBlock(element, iframes) == null;\r\n        }\r\n        element = getParentElementIncludingSlots(element, iframes);\r\n    }\r\n\r\n    return false;\r\n}\n/**\n * Compute width/height for a Text node given an already-fetched bounding rect.\r\n * Extracted from getElementSize so getBoxQuads (FIX 7) can reuse the Range it\r\n * already created, avoiding a second Range allocation.\r\n * @param {DOMMatrix} matrix\r\n * @param {DOMRect} targetRect\r\n * @param {Text} node\r\n */\r\nfunction _getTextNodeSize(matrix, targetRect, node) {\r\n    const absA = Math.abs(matrix?.a ?? 1);\r\n    const absB = Math.abs(matrix?.b ?? 0);\r\n    const det = absA * absA - absB * absB; // cos(2*angle)*scale^2\r\n    let width, height;\r\n    if (Math.abs(det) > 1e-6) {\r\n        width  = Math.max(0, (absA * targetRect.width  - absB * targetRect.height) / det);\r\n        height = Math.max(0, (absA * targetRect.height - absB * targetRect.width)  / det);\r\n    } else {\r\n        // Singular (~45 deg rotation): use CSS line-height as the known height\r\n        const parentEl = node.parentElement;\r\n        let lineH = 16;\r\n        if (parentEl) {\r\n            const cs = getCachedComputedStyle(parentEl);\r\n            lineH = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16;\r\n        }\r\n        height = Math.max(0, lineH);\r\n        const denom = Math.max(absA, absB);\r\n        width = denom > 1e-6 ? Math.max(0, (targetRect.width - height * absB) / denom) : targetRect.width;\r\n    }\r\n    return { width, height };\r\n}\r\n\r\n//todo: https://drafts.csswg.org/css-transforms-2/#accumulated-3d-transformation-matrix-computation\r\n// also good for writing a spec\r\n\r\n// Find a value for z that will transform to 0. (from firefox matrix.h)\r\n// or chromium https://github.com/chromium/chromium/blob/main/ui/gfx/geometry/transform.cc#L849\r\n/**\r\n* @param {DOMPoint} point\r\n*/\r\nfunction projectPoint(point, m) {\r\n    const z = -(point.x * m.m13 + point.y * m.m23 + m.m43) / m.m33;\r\n    return new DOMPoint(point.x, point.y, z, 1);\r\n}\r\n/**\r\n* convert a DOM-Point to 2D\r\n* @param {DOMPoint} point\r\n*/\r\nfunction as2DPoint(point) {\r\n    return new DOMPoint(\r\n        point.x / point.w,\r\n        point.y / point.w\r\n    );\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {DOMMatrix=} matrix\r\n*/\r\nexport function getElementSize(node, matrix) {\r\n    let width = 0;\r\n    let height = 0;\r\n    // FIX 13: Cache cross-realm constructors once.\r\n    const win = node.ownerDocument.defaultView ?? window;\r\n    if ((node instanceof HTMLElement || node instanceof win.HTMLElement)) {\r\n        width = node.offsetWidth;\r\n        height = node.offsetHeight;\r\n    } else if ((node instanceof SVGSVGElement || node instanceof win.SVGSVGElement)) {\r\n        width = node.width.baseVal.value\r\n        height = node.height.baseVal.value\r\n    } else if ((node instanceof SVGGraphicsElement || node instanceof win.SVGGraphicsElement)) {\r\n        const bbox = node.getBBox()\r\n        width = bbox.width;\r\n        height = bbox.height;\r\n    } else if ((node instanceof MathMLElement || node instanceof win.MathMLElement)) {\r\n        const bbox = node.getBoundingClientRect()\r\n        width = bbox.width / (matrix?.a ?? 1);\r\n        height = bbox.height / (matrix?.d ?? 1);\r\n    } else if ((node instanceof Text || node instanceof win.Text)) {\r\n        // Note: getBoxQuads passes an already-fetched rect via _getTextNodeSize to\r\n        // avoid this Range creation. This path serves external callers of getElementSize.\r\n        const range = node.ownerDocument.createRange();\r\n        range.selectNodeContents(node);\r\n        const targetRect = range.getBoundingClientRect();\r\n        const result = _getTextNodeSize(matrix, targetRect, node);\r\n        width  = result.width;\r\n        height = result.height;\r\n    }\r\n    return { width, height }\r\n}\r\n\r\nfunction getSvgVisualBox(node, bbox) {\n    const svgStyle = getCachedComputedStyle(node);\n    const strokeWidth = svgStyle.stroke !== 'none' ? parseFloat(svgStyle.strokeWidth) || 0 : 0;\n    if (strokeWidth <= 0) {\n        return bbox;\n    }\n\n    const strokeInflation = getSvgStrokeInflation(node, bbox, strokeWidth);\n    return new DOMRect(\n        bbox.x - strokeInflation.left,\n        bbox.y - strokeInflation.top,\n        bbox.width + strokeInflation.left + strokeInflation.right,\n        bbox.height + strokeInflation.top + strokeInflation.bottom,\n    );\n}\n\nfunction getSvgStrokeInflation(node, bbox, strokeWidth) {\n    const halfStrokeWidth = strokeWidth / 2;\n    if ((node instanceof SVGLineElement || node instanceof (node.ownerDocument.defaultView ?? window).SVGLineElement)) {\n        const x1 = node.x1.baseVal.value;\n        const y1 = node.y1.baseVal.value;\n        const x2 = node.x2.baseVal.value;\n        const y2 = node.y2.baseVal.value;\n        const dx = x2 - x1;\n        const dy = y2 - y1;\n        const length = Math.hypot(dx, dy);\n\n        if (length > 1e-10) {\n            let inflateX = halfStrokeWidth * Math.abs(dy) / length;\n            let inflateY = halfStrokeWidth * Math.abs(dx) / length;\n            const lineCap = getCachedComputedStyle(node).strokeLinecap;\n\n            if (lineCap === 'round' || lineCap === 'square') {\n                inflateX += halfStrokeWidth * Math.abs(dx) / length;\n                inflateY += halfStrokeWidth * Math.abs(dy) / length;\n            }\n\n            return { left: inflateX, right: inflateX, top: inflateY, bottom: inflateY };\n        }\n    }\n\n    const genericInflation = strokeWidth * 2;\n    return { left: genericInflation, right: genericInflation, top: genericInflation, bottom: genericInflation };\n}\n/**\r\n* @param {Node} node\r\n* @param {boolean} includeScroll\r\n* @param {HTMLIFrameElement[]} iframes\r\n*/\r\nfunction getElementOffsetsInContainer(node, includeScroll, iframes) {\r\n    if ((node instanceof HTMLElement || node instanceof (node.ownerDocument.defaultView ?? window).HTMLElement)) {\r\n        // When <html> appears inside a shadow DOM canvas the browser reflects\r\n        // body's top margin into html.offsetTop (but not offsetLeft), causing\r\n        // a Y-only shift in the transform walk.  The html element has no real\r\n        // layout offset relative to its shadow-host container, so return 0,0.\r\n        if ((node instanceof HTMLHtmlElement || node instanceof (node.ownerDocument.defaultView ?? window).HTMLHtmlElement)) {\r\n            return new DOMPoint(0, 0);\r\n        }\r\n        const cs = getCachedComputedStyle(node);\r\n        if (cs.position === 'fixed') {\r\n            const fixedContainer = getNearestFixedContainingBlock(node, iframes);\r\n            if (!fixedContainer) {\r\n                const left = cs.left && cs.left !== 'auto' ? parseFloat(cs.left) : node.offsetLeft;\r\n                const top = cs.top && cs.top !== 'auto' ? parseFloat(cs.top) : node.offsetTop;\r\n                return new DOMPoint(left, top);\r\n            }\r\n\r\n            const m = getResultingTransformationBetweenElementAndAllAncestors(fixedContainer, node.ownerDocument.body, iframes, true).inverse();\r\n            const r1 = node.getBoundingClientRect();\r\n            const r1t = m.transformPoint(r1);\r\n            const r2 = fixedContainer.getBoundingClientRect();\r\n            const r2t = m.transformPoint(r2);\r\n\r\n            return new DOMPoint(r1t.x - r2t.x, r1t.y - r2t.y);\r\n        }\r\n\r\n        // FIX 4: Only call getCachedComputedStyle when includeScroll is true —\r\n        //        cs is unused in the plain offsetLeft/offsetTop path.\r\n        if (includeScroll) {\r\n            return new DOMPoint(node.offsetLeft - (node.scrollLeft - parseFloat(cs.borderLeftWidth)), node.offsetTop - (node.scrollTop - parseFloat(cs.borderTopWidth)));\r\n        } else {\r\n            return new DOMPoint(node.offsetLeft, node.offsetTop);\r\n        }\r\n    } else if ((node instanceof Text || node instanceof (node.ownerDocument.defaultView ?? window).Text)) {\r\n        const range = node.ownerDocument.createRange();\r\n        range.selectNodeContents(node);\r\n        const r1 = range.getBoundingClientRect();\r\n        /** @type {HTMLElement} */\r\n        //@ts-ignore\r\n        const parent = getParentElementIncludingSlots(node, iframes);\r\n        const r2 = parent.getBoundingClientRect();\r\n\r\n        // Get the parent's CSS transform so we can work in local space even when rotated.\r\n        const pt = getElementCombinedTransform(parent, iframes);\r\n        const pa = pt.a, pb = pt.b, pc = pt.c, pd = pt.d;\r\n\r\n        // AABB center of the transformed parent equals its geometric center.\r\n        // geometric_center_screen = screen(0,0) + L * (pw/2, ph/2)\r\n        // => screen(0,0) = AABB_center - L * (pw/2, ph/2)\r\n        const pw = parent.offsetWidth;\r\n        const ph = parent.offsetHeight;\r\n        const parentOriginX = (r2.x + r2.width  / 2) - (pa * pw / 2 + pc * ph / 2);\r\n        const parentOriginY = (r2.y + r2.height / 2) - (pb * pw / 2 + pd * ph / 2);\r\n\r\n        // Delta from parent origin to text AABB center in screen space\r\n        const dx = (r1.x + r1.width  / 2) - parentOriginX;\r\n        const dy = (r1.y + r1.height / 2) - parentOriginY;\r\n\r\n        // Apply inverse of CSS transform linear part: local_center = L^-1 * screen_delta\r\n        const transformDet = pa * pd - pb * pc;\r\n        let localCenterX, localCenterY;\r\n        if (Math.abs(transformDet) > 1e-10) {\r\n            localCenterX = (pd * dx - pc * dy) / transformDet;\r\n            localCenterY = (pa * dy - pb * dx) / transformDet;\r\n        } else {\r\n            localCenterX = dx;\r\n            localCenterY = dy;\r\n        }\r\n\r\n        // Recover tw and th in local space from the AABB using the 2x2 system:\r\n        //   aabb_w = tw*|cos| + th*|sin|,  aabb_h = tw*|sin| + th*|cos|\r\n        const absA = Math.abs(pa), absB = Math.abs(pb);\r\n        const absDet = absA * absA - absB * absB;\r\n        let tw, th;\r\n        if (Math.abs(absDet) > 1e-6) {\r\n            tw = Math.max(0, (absA * r1.width  - absB * r1.height) / absDet);\r\n            th = Math.max(0, (absA * r1.height - absB * r1.width)  / absDet);\r\n        } else {\r\n            // Singular (~45 deg): use CSS line-height as th\r\n            const cs = getCachedComputedStyle(parent);\r\n            th = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2 || 16;\r\n            th = Math.max(0, th);\r\n            const denom = Math.max(absA, absB);\r\n            tw = denom > 1e-6 ? Math.max(0, (r1.width - th * absB) / denom) : r1.width;\r\n        }\r\n\r\n        // local origin = center minus half-dimensions\r\n        return new DOMPoint(localCenterX - tw / 2, localCenterY - th / 2);\r\n    } else if ((node instanceof Element || node instanceof (node.ownerDocument.defaultView ?? window).Element)) {\r\n        if ((node instanceof SVGGraphicsElement || node instanceof (node.ownerDocument.defaultView ?? window).SVGGraphicsElement) && !((node instanceof SVGSVGElement || node instanceof (node.ownerDocument.defaultView ?? window).SVGSVGElement))) {\r\n            const bb = node.getBBox();\r\n            return new DOMPoint(bb.x, bb.y);\r\n        }\r\n        const cs = getCachedComputedStyle(node);\r\n        if (cs.position === 'absolute') {\r\n            return new DOMPoint(parseFloat(cs.left), parseFloat(cs.top));\r\n        }\r\n\r\n        const par = getParentElementIncludingSlots(node, iframes);\r\n        const m = getResultingTransformationBetweenElementAndAllAncestors(par, document.body, iframes, true).inverse();\r\n        const r1 = node.getBoundingClientRect();\r\n        const r1t = m.transformPoint(r1);\r\n        const r2 = par.getBoundingClientRect();\r\n        const r2t = m.transformPoint(r2);\r\n\r\n        return new DOMPoint(r1t.x - r2t.x, r1t.y - r2t.y);\r\n    }\r\n}\r\n/**\r\n* @param {Node} node\r\n* @param {Element} ancestor\r\n* @param {HTMLIFrameElement[]} iframes\r\n* @param {boolean=} excludeSelfZoom\r\n*/\r\nexport function getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, iframes, excludeSelfZoom = false) {\r\n    let key;\r\n    if (transformCache) {\r\n        let i1 = hash.get(node);\r\n        if (i1 === undefined)\r\n            hash.set(node, i1 = hashId++);\r\n        let i2 = hash.get(ancestor);\r\n        if (i2 === undefined)\r\n            hash.set(ancestor, i2 = hashId++);\r\n        key = i1 + '_' + i2 + '_' + (excludeSelfZoom ? 'no-self-zoom' : 'full');\r\n        const q = transformCache.get(key);\r\n        if (q)\r\n            return q;\r\n    }\r\n\r\n    /** @type {Element } */\r\n    //@ts-ignore\r\n    let actualElement = node;\r\n    /** @type {DOMMatrix } */\r\n    let parentElementMatrix;\r\n\r\n    // FIX 12: Compute self-transform once; we'll carry parent transforms forward\r\n    //         each iteration instead of recomputing them.\r\n    const useOwnSvgCtm =\r\n        (actualElement instanceof SVGGraphicsElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).SVGGraphicsElement) &&\r\n        !((actualElement instanceof SVGSVGElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).SVGSVGElement));\r\n    // SVGGraphicsElement.getCTM() already includes the element's local SVG/CSS transform.\r\n    // Starting with getElementTransformWithZoom here would double-apply self rotate/scale.\r\n    let currentElementTransform = useOwnSvgCtm\r\n        ? new DOMMatrix()\r\n        : getElementTransformWithZoom(actualElement, iframes, !excludeSelfZoom);\r\n\r\n    /** @type {DOMMatrix } */\r\n    // FIX 2: Only use a non-identity starting matrix when the element itself has\r\n    //        a CSS transform. Most plain elements have identity, avoiding a multiply.\r\n    let originalElementAndAllParentsMultipliedMatrix = currentElementTransform.isIdentity\r\n        ? new DOMMatrix()\r\n        : currentElementTransform;\r\n\r\n    let perspectiveParentElement = getParentElementIncludingSlots(actualElement, iframes);\r\n    if (perspectiveParentElement) {\r\n        // FIX 5: Guard transformStyle read behind is2D — on a standard 2D page\r\n        //        the matrix is always 2D here so we skip the style read entirely.\r\n        if (!originalElementAndAllParentsMultipliedMatrix.is2D) {\r\n            const s = getCachedComputedStyle(perspectiveParentElement);\r\n            if (s.transformStyle !== 'preserve-3d') {\r\n                projectTo2D(originalElementAndAllParentsMultipliedMatrix);\r\n            }\r\n        }\r\n    }\r\n\r\n    let lastOffsetParent = null;\r\n    while (actualElement != ancestor && actualElement != null) {\r\n        let parentElement = getParentElementIncludingSlots(actualElement, iframes);\r\n\r\n        if ((actualElement instanceof HTMLElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).HTMLElement)) {\r\n            const fixedStyle = getCachedComputedStyle(actualElement);\r\n            if (fixedStyle.position === 'fixed') {\r\n                const fixedContainer = getNearestFixedContainingBlock(actualElement, iframes);\r\n                parentElement = fixedContainer ?? ancestor;\r\n            }\r\n        }\r\n\r\n        if (actualElement.assignedSlot != null) {\r\n            if (actualElement.nodeType === Node.ELEMENT_NODE) {\r\n                const slotOffsetParent = offsetParentPolyfill(actualElement);\r\n                const shouldApplySlottedOffset = lastOffsetParent !== slotOffsetParent\r\n                    && (lastOffsetParent === null || actualElement === lastOffsetParent || !isFlatTreeInclusiveAncestor(lastOffsetParent, actualElement));\r\n                if (shouldApplySlottedOffset) {\r\n                    const l = offsetTopLeftPolyfill(actualElement, 'offsetLeft');\r\n                    const t = offsetTopLeftPolyfill(actualElement, 'offsetTop');\r\n                    // FIX 3: Skip zero-translation matrix allocations.\r\n                    if (l !== 0 || t !== 0) {\r\n                        const mvMat = new DOMMatrix().translateSelf(l, t);\r\n                        originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                    }\r\n                    lastOffsetParent = slotOffsetParent;\r\n                }\r\n                if (lastOffsetParent === null)\r\n                    lastOffsetParent = slotOffsetParent;\r\n            } else if (actualElement.nodeType === Node.TEXT_NODE) {\r\n                const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);\r\n                // FIX 3\r\n                if (offsets.x !== 0 || offsets.y !== 0) {\r\n                    const mvMat = new DOMMatrix().translateSelf(offsets.x, offsets.y);\r\n                    originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                }\r\n            }\r\n            /*\r\n            following code should be used instead of above to fix:\r\n            but it does not work with:\r\n                <div>\r\n                    <visu-tag-root-canvas id=\"outer-tag-root-canvas\" tag-root=\"SRM.RBG01\">\r\n                        <template shadowrootmode=\"open\">\r\n                            <div id=\"rootObj\" style=\"height:100%;width:100%;position:absolute;top:400px;\">\r\n                                <slot></slot>\r\n                            </div>\r\n                        </template>\r\n                        <div class=\"wrapper\" id=\"aaaaabb\" style=\"height:50px;width:50px;\"></div>\r\n                    </visu-tag-root-canvas>\r\n                </div>\r\n            */\r\n            /*\r\n            const l = offsetTopLeftPolyfill(actualElement, 'offsetLeft');\r\n            const t = offsetTopLeftPolyfill(actualElement, 'offsetTop');\r\n            const mvMat = new DOMMatrix().translateSelf(l, t);\r\n            originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n            */\r\n        } else {\r\n            if (!(actualElement instanceof SVGSVGElement) && !(actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).SVGSVGElement) &&\r\n                (actualElement instanceof SVGGraphicsElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).SVGGraphicsElement)) {\r\n                const ctm = actualElement.getCTM();\r\n                const bb = actualElement.getBBox();\r\n                // FIX 3\r\n                if (bb.x !== 0 || bb.y !== 0) {\r\n                    const mvMat = new DOMMatrix().translateSelf(bb.x, bb.y);\r\n                    originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                }\r\n                originalElementAndAllParentsMultipliedMatrix = new DOMMatrix([ctm.a, ctm.b, ctm.c, ctm.d, ctm.e, ctm.f]).multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                parentElement = actualElement.ownerSVGElement;\r\n            } else if ((actualElement instanceof HTMLElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).HTMLElement)) {\r\n                const actualStyle = getCachedComputedStyle(actualElement);\r\n                const isFixedSelf = actualStyle.position === 'fixed' && actualElement === node;\r\n                if ((isFixedSelf || lastOffsetParent !== actualElement.offsetParent) && !((actualElement instanceof HTMLSlotElement || actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).HTMLSlotElement))\r\n                    && (lastOffsetParent === null || actualElement === lastOffsetParent || !isFlatTreeInclusiveAncestor(lastOffsetParent, actualElement))) {\r\n                    const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);\r\n                    const zoom = getElementZoom(actualElement);\r\n                    lastOffsetParent = actualElement.offsetParent;\r\n                    // FIX 3\r\n                    if (offsets.x !== 0 || offsets.y !== 0) {\r\n                        const mvMat = new DOMMatrix().translateSelf(offsets.x * zoom, offsets.y * zoom);\r\n                        originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                    }\r\n                }\r\n            } else {\r\n                const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);\r\n                lastOffsetParent = null;\r\n                // FIX 3\r\n                if (offsets.x !== 0 || offsets.y !== 0) {\r\n                    const mvMat = new DOMMatrix().translateSelf(offsets.x, offsets.y);\r\n                    originalElementAndAllParentsMultipliedMatrix = mvMat.multiplySelf(originalElementAndAllParentsMultipliedMatrix);\r\n                }\r\n            }\r\n        }\r\n\r\n        if (parentElement) {\r\n            if (parentElement === ancestor) {\r\n                // The ancestor's own transform is excluded from the returned matrix,\r\n                // so avoid computing it on the hot return path.\r\n                if (lastOffsetParent !== null &&\r\n                    (parentElement instanceof HTMLElement || parentElement instanceof (parentElement.ownerDocument.defaultView ?? window).HTMLElement) &&\r\n                    parentElement.offsetParent === lastOffsetParent) {\r\n                    const ancOff = getElementOffsetsInContainer(parentElement, false, iframes);\r\n                    // FIX 3\r\n                    if (ancOff.x !== 0 || ancOff.y !== 0) {\r\n                        originalElementAndAllParentsMultipliedMatrix = new DOMMatrix().translate(-ancOff.x, -ancOff.y).multiply(originalElementAndAllParentsMultipliedMatrix);\r\n                    }\r\n                }\r\n                // FIX 15: Do NOT subtract the scroll of documentElement (the viewport/window\r\n                //         scroll). The offsetLeft/offsetTop walk already yields document-absolute\r\n                //         coordinates; subtracting documentElement.scrollTop would wrongly\r\n                //         shift positions to viewport-space when the page is scrolled.\r\n                //         Only subtract scroll for a non-root ancestor that is itself a\r\n                //         scroll container (e.g. an overflow:scroll div used as relativeTo).\r\n                const isViewportScrollContainer = parentElement === parentElement.ownerDocument.documentElement;\r\n                if (!isViewportScrollContainer && (parentElement.scrollTop || parentElement.scrollLeft))\r\n                    originalElementAndAllParentsMultipliedMatrix = new DOMMatrix().translate(-parentElement.scrollLeft, -parentElement.scrollTop).multiply(originalElementAndAllParentsMultipliedMatrix);\r\n\r\n                const ancestorZoom = getElementZoomScaleTransform(parentElement);\r\n                if (!ancestorZoom.isIdentity)\r\n                    originalElementAndAllParentsMultipliedMatrix = ancestorZoom.multiply(originalElementAndAllParentsMultipliedMatrix);\r\n\r\n                // FIX 6: Cache result on the early-return path. Originally, the\r\n                //        cache.set() only ran after the while-loop (the null/root\r\n                //        fallthrough), so the most common case — element IS a\r\n                //        descendant of ancestor — was NEVER cached.\r\n                if (transformCache)\r\n                    transformCache.set(key, originalElementAndAllParentsMultipliedMatrix);\r\n\r\n                return originalElementAndAllParentsMultipliedMatrix;\r\n            }\r\n\r\n            // FIX 12: parentElementMatrix computed here; in the next iteration this\r\n            //         becomes the element's own transform, so we can reuse it without\r\n            //         calling getElementCombinedTransform again.\r\n            parentElementMatrix = getElementTransformWithZoom(parentElement, iframes);\r\n\r\n            if (!parentElementMatrix.isIdentity)\r\n                originalElementAndAllParentsMultipliedMatrix = parentElementMatrix.multiply(originalElementAndAllParentsMultipliedMatrix);\r\n\r\n            perspectiveParentElement = getParentElementIncludingSlots(parentElement, iframes);\r\n            if (perspectiveParentElement) {\r\n                // FIX 5: Skip transformStyle read when matrix is already 2D.\r\n                if (!originalElementAndAllParentsMultipliedMatrix.is2D) {\r\n                    const s = getCachedComputedStyle(perspectiveParentElement);\r\n                    if (s.transformStyle !== 'preserve-3d') {\r\n                        projectTo2D(originalElementAndAllParentsMultipliedMatrix);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        actualElement = parentElement;\r\n    }\r\n\r\n    if (transformCache) {\r\n        transformCache.set(key, originalElementAndAllParentsMultipliedMatrix);\r\n    }\r\n    return originalElementAndAllParentsMultipliedMatrix;\r\n}\r\n\r\n/*\r\ngetResultingTransformationBetweenElementAndAllAncestors -> but with extra layout matrix (does not work yet....)\r\n\r\nexport function getResultingTransformationBetweenElementAndAllAncestors(node, ancestor, iframes) {\r\n    let key;\r\n    if (transformCache) {\r\n        let i1 = hash.get(node);\r\n        if (i1 === undefined)\r\n            hash.set(node, i1 = hashId++);\r\n        let i2 = hash.get(ancestor);\r\n        if (i2 === undefined)\r\n            hash.set(ancestor, i2 = hashId++);\r\n        key = i1 + '_' + i2;\r\n        const q = transformCache.get(key);\r\n        if (q)\r\n            return q;\r\n    }\r\n\r\n    // NEW - two matrices instead of one\r\n    let layoutMatrix = new DOMMatrix();\r\n\r\n    let actualElement = node;\r\n\r\n    let transformMatrix = getElementCombinedTransform(actualElement, iframes); //.multiplySelf(transformMatrix);\r\n\r\n\r\n    const perspectiveParent = getParentElementIncludingSlots(actualElement, iframes);\r\n    if (perspectiveParent) {\r\n        const s = getCachedComputedStyle(perspectiveParent);\r\n        if (s.transformStyle !== \"preserve-3d\")\r\n            projectTo2D(transformMatrix);\r\n    }\r\n\r\n\r\n    let lastOffsetParent = null;\r\n\r\n    while (actualElement !== ancestor && actualElement != null) {\r\n\r\n        const parentElement = getParentElementIncludingSlots(actualElement, iframes);\r\n\r\n        // ------------------------\r\n        //  LAYOUT MATRIX (offsets)\r\n        // ------------------------\r\n\r\n        if (actualElement.assignedSlot != null) {\r\n\r\n            const l = offsetTopLeftPolyfill(actualElement, \"offsetLeft\");\r\n            const t = offsetTopLeftPolyfill(actualElement, \"offsetTop\");\r\n            layoutMatrix = new DOMMatrix().translateSelf(l, t).multiplySelf(layoutMatrix);\r\n\r\n        } else {\r\n\r\n            if (actualElement instanceof HTMLElement ||\r\n                actualElement instanceof (actualElement.ownerDocument.defaultView ?? window).HTMLElement) {\r\n\r\n                if (lastOffsetParent !== actualElement.offsetParent &&\r\n                    !(actualElement instanceof HTMLSlotElement)) {\r\n\r\n                    const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);\r\n                    lastOffsetParent = actualElement.offsetParent;\r\n\r\n                    layoutMatrix = new DOMMatrix().translateSelf(offsets.x, offsets.y).multiplySelf(layoutMatrix);\r\n                }\r\n\r\n            } else {\r\n\r\n                const offsets = getElementOffsetsInContainer(actualElement, actualElement !== node, iframes);\r\n                lastOffsetParent = null;\r\n\r\n                layoutMatrix = new DOMMatrix().translateSelf(offsets.x, offsets.y).multiplySelf(layoutMatrix);\r\n            }\r\n        }\r\n\r\n        // ------------------------\r\n        //  TRANSFORM MATRIX (CSS)\r\n        // ------------------------\r\n\r\n        if (parentElement) {\r\n\r\n            // NEW - only affects transform pipeline\r\n            const parentTransform = getElementCombinedTransform(parentElement, iframes);\r\n            transformMatrix = parentTransform.multiply(transformMatrix);\r\n\r\n            // flattening boundary\r\n            const perspectiveParent = getParentElementIncludingSlots(parentElement, iframes);\r\n            if (perspectiveParent) {\r\n                const s = getCachedComputedStyle(perspectiveParent);\r\n                if (s.transformStyle !== \"preserve-3d\")\r\n                    projectTo2D(transformMatrix);\r\n            }\r\n\r\n            // ------------------------\r\n            //  EXIT CONDITION\r\n            // ------------------------\r\n\r\n            if (parentElement === ancestor) {\r\n\r\n                // NEW - scroll offsets belong to layout\r\n                if (parentElement.scrollTop || parentElement.scrollLeft) {\r\n                    layoutMatrix = new DOMMatrix()\r\n                        .translate(-parentElement.scrollLeft, -parentElement.scrollTop)\r\n                        .multiply(layoutMatrix);\r\n                }\r\n\r\n                const result = layoutMatrix.multiply(transformMatrix);\r\n\r\n                if (transformCache)\r\n                    transformCache.set(key, result);\r\n\r\n                return result;\r\n            }\r\n        }\r\n\r\n        actualElement = parentElement;\r\n    }\r\n\r\n    const result = layoutMatrix.multiply(transformMatrix);\r\n\r\n    if (transformCache)\r\n        transformCache.set(key, result);\r\n\r\n    return result;\r\n}\r\n*/\r\n\r\n/**\r\n* @param {Node} node\r\n* @param {HTMLIFrameElement[]} iframes\r\n* @returns {Element}\r\n*/\r\nfunction getParentElementIncludingSlots(node, iframes) {\r\n    if ((node instanceof Element || node instanceof (node.ownerDocument.defaultView ?? window).Element) && node.assignedSlot)\r\n        return node.assignedSlot;\r\n    if (node.parentElement == null) {\r\n        if ((node.parentNode instanceof ShadowRoot || node.parentNode instanceof (node.ownerDocument.defaultView ?? window).ShadowRoot)) {\r\n            return node.parentNode.host;\r\n        }\r\n    }\r\n    if ((node instanceof HTMLHtmlElement || node instanceof (node.ownerDocument.defaultView ?? window).HTMLHtmlElement)) {\r\n        if (iframes) {\r\n            for (const f of iframes)\r\n                if (f?.contentDocument == node.ownerDocument)\r\n                    return f;\r\n        }\r\n    }\r\n    return node.parentElement;\r\n}\r\n/**\r\n * @param {Node} element\r\n* @param {HTMLIFrameElement[]=} iframes\r\n*/\r\nexport function getElementCombinedTransform(element, iframes) {\r\n    if ((element instanceof Text || element instanceof (element.ownerDocument.defaultView ?? window).Text))\r\n        return new DOMMatrix;\r\n\r\n    /** @type {Element} */\r\n    // @ts-ignore\r\n    const actualElement = element;\r\n\r\n    //https://www.w3.org/TR/css-transforms-2/#ctm\r\n    let s = getCachedComputedStyle(actualElement);\r\n\r\n    // FIX 10: Check hasTransform first — it's the most common non-identity case.\r\n    //         Reordering so the most frequent hit is evaluated first.\r\n    const hasTransform  = s.transform !== 'none' && !!s.transform;\r\n    const hasTranslate  = s.translate !== 'none' && !!s.translate;\r\n    const hasRotate     = s.rotate    !== 'none' && !!s.rotate;\r\n    const hasScale      = s.scale     !== 'none' && !!s.scale;\r\n    const hasOffsetPath = !!s.offsetPath && s.offsetPath !== 'none';\r\n\r\n    if (!hasTransform && !hasTranslate && !hasRotate && !hasScale && !hasOffsetPath) {\r\n        // FIX 1: Check parent perspective right here in the fast-path, so identity\r\n        //        elements on non-3D pages return a new DOMMatrix() immediately\r\n        //        without calling getElementPerspectiveTransform at all.\r\n        const parent = getParentElementIncludingSlots(actualElement, iframes);\r\n        if (!parent) return new DOMMatrix();\r\n        const ps = getCachedComputedStyle(parent);\r\n        if (!ps.perspective || ps.perspective === 'none') return new DOMMatrix();\r\n        // Parent has a perspective — fall through to compute it properly.\r\n        //@ts-ignore\r\n        const pt = getElementPerspectiveTransform(actualElement, iframes);\r\n        return pt != null ? pt : new DOMMatrix();\r\n    }\r\n\r\n    let m = new DOMMatrix();\r\n    const origin = s.transformOrigin.split(' ');\r\n    const originX = parseFloat(origin[0]);\r\n    const originY = parseFloat(origin[1]);\r\n    const originZ = origin[2] ? parseFloat(origin[2]) : 0;\r\n\r\n    // FIX 11: Skip the origin wrap/unwrap entirely when origin is (0,0,0).\r\n    //         Saves two DOMMatrix allocations and two multiply calls per element.\r\n    const hasNonZeroOrigin = (originX !== 0 || originY !== 0 || originZ !== 0);\r\n    const mOri = hasNonZeroOrigin ? new DOMMatrix().translateSelf(originX, originY, originZ) : null;\r\n\r\n    if (hasTranslate) {\r\n        let tr = s.translate;\r\n        if (tr.includes('%')) {\r\n            const v = tr.split(' ');\r\n            const r = actualElement.getBoundingClientRect();\r\n            if (v[0].endsWith('%'))\r\n                v[0] = (parseFloat(v[0]) * r.width / 100) + 'px';\r\n            if (v[1]?.endsWith('%'))\r\n                v[1] = (parseFloat(v[1]) * r.height / 100) + 'px';\r\n            tr = v.join(',');\r\n        }\r\n        m.multiplySelf(new DOMMatrix('translate(' + tr.replaceAll(' ', ',') + ')'));\r\n    }\r\n    if (hasRotate) {\r\n        m.multiplySelf(new DOMMatrix('rotate(' + s.rotate.replaceAll(' ', ',') + ')'));\r\n    }\r\n    if (hasScale) {\r\n        m.multiplySelf(new DOMMatrix('scale(' + s.scale.replaceAll(' ', ',') + ')'));\r\n    }\r\n    if (hasOffsetPath) {\r\n        m.multiplySelf(computeOffsetTransformMatrix(element));\r\n    }\r\n    if (hasTransform) {\r\n        m.multiplySelf(new DOMMatrix(s.transform));\r\n    }\r\n\r\n    // FIX 11 (continued): Only wrap with origin if non-zero.\r\n    if (hasNonZeroOrigin) {\r\n        m = mOri.multiply(m.multiplySelf(mOri.inverse()));\r\n    }\r\n\r\n    //@ts-ignore\r\n    const pt = getElementPerspectiveTransform(element, iframes);\r\n    if (pt != null) {\r\n        m = pt.multiplySelf(m);\r\n    }\r\n    return m;\r\n}\r\n/**\r\n* project a DOM-Matrix to 2D (from firefox matrix.h)\r\n* @param {DOMMatrix} m\r\n*/\r\nfunction projectTo2D(m) {\r\n    m.m31 = 0.0;\r\n    m.m32 = 0.0;\r\n    m.m13 = 0.0;\r\n    m.m23 = 0.0;\r\n    m.m33 = 1.0;\r\n    m.m43 = 0.0;\r\n    m.m34 = 0.0;\r\n    // Some matrices, such as those derived from perspective transforms,\r\n    // can modify _44 from 1, while leaving the rest of the fourth column\r\n    // (_14, _24) at 0. In this case, after resetting the third row and\r\n    // third column above, the value of _44 functions only to scale the\r\n    // coordinate transform divide by W. The matrix can be converted to\r\n    // a true 2D matrix by normalizing out the scaling effect of _44 on\r\n    // the remaining components ahead of time.\r\n    if (m.m14 == 0.0 && m.m24 == 0.0 && m.m44 != 1.0 && m.m44 != 0.0) {\r\n        const scale = 1.0 / m.m44;\r\n        m.m11 *= scale;\r\n        m.m12 *= scale;\r\n        m.m21 *= scale;\r\n        m.m22 *= scale;\r\n        m.m41 *= scale;\r\n        m.m42 *= scale;\r\n        m.m44 = 1.0;\r\n    }\r\n}\r\n/**\r\n* @param {HTMLElement} element\r\n* @param {HTMLIFrameElement[]} iframes\r\n*/\r\nfunction getElementPerspectiveTransform(element, iframes) {\r\n    /** @type { Element } */\r\n    //@ts-ignore\r\n    const perspectiveNode = getParentElementIncludingSlots(element, iframes);\r\n    if (perspectiveNode) {\r\n        //https://drafts.csswg.org/css-transforms-2/#perspective-matrix-computation\r\n        let s = getCachedComputedStyle(perspectiveNode);\r\n        if (s.perspective !== 'none') {\r\n            let m = new DOMMatrix();\r\n            let p = parseFloat(s.perspective);\r\n            m.m34 = -1.0 / p;\r\n            //https://drafts.csswg.org/css-transforms-2/#PerspectiveDefined\r\n            if (s.perspectiveOrigin) {\r\n                const origin = s.perspectiveOrigin.split(' ');\r\n                const originX = parseFloat(origin[0]) - element.offsetLeft;\r\n                const originY = parseFloat(origin[1]) - element.offsetTop;\r\n\r\n                const mOri = new DOMMatrix().translateSelf(originX, originY);\r\n                const mOriInv = new DOMMatrix().translateSelf(-originX, -originY);\r\n\r\n                return mOri.multiplySelf(m.multiplySelf(mOriInv));\r\n            }\r\n        }\r\n    }\r\n    return null;\r\n}\r\n\r\nfunction computeOffsetTransformMatrix(elem) {\r\n    const cs = getCachedComputedStyle(elem);\r\n\r\n    const offsetPath     = cs.offsetPath;      // e.g. \"path('M0,0 ...')\"\r\n    const offsetDistance = cs.offsetDistance;  // e.g. \"50%\"\r\n    const offsetRotate   = cs.offsetRotate;    // e.g. \"auto\", \"45deg\", \"auto 30deg\"\r\n    const offsetAnchor   = cs.offsetAnchor;\r\n    const transformOrigin = cs.transformOrigin;\r\n\r\n    // Parse offset-distance (px or %)\r\n    let distance = parseOffsetDistance(offsetDistance);\r\n\r\n    // Compute position & tangent on path (in containing block coordinates)\r\n    let { x, y, angle } = computeOffsetPathPoint(elem, offsetPath, distance);\r\n\r\n    // Subtract the element's flow position within its containing block.\r\n    // The offset-path positions the element absolutely within the containing block,\r\n    // but the walk already adds offsetLeft/offsetTop (flow position). To avoid\r\n    // double-counting, make the offset relative to the flow position.\r\n    const parent = elem.parentElement;\r\n    if (parent instanceof HTMLElement || parent instanceof (parent.ownerDocument.defaultView ?? window).HTMLElement) {\r\n        if (elem.offsetParent === parent) {\r\n            // Containing block = parent = offsetParent\r\n            x -= elem.offsetLeft;\r\n            y -= elem.offsetTop;\r\n        } else if (elem.offsetParent === parent.offsetParent) {\r\n            // Both share the same offsetParent\r\n            x -= (elem.offsetLeft - parent.offsetLeft);\r\n            y -= (elem.offsetTop - parent.offsetTop);\r\n        }\r\n    }\r\n\r\n    // Handle offset-rotate\r\n    let rotateFinal = 0;\r\n    if (offsetRotate.startsWith(\"auto\")) {\r\n        let parts = offsetRotate.split(/\\s+/);\r\n        let extra = parts.length === 2 ? parseFloat(parts[1]) : 0;\r\n        rotateFinal = angle + extra;\r\n    } else {\r\n        rotateFinal = parseFloat(offsetRotate);\r\n    }\r\n\r\n    const anchor = parseOffsetAnchor(offsetAnchor, transformOrigin, elem);\r\n\r\n    const anchorMatrix = new DOMMatrix().translateSelf(-anchor.x, -anchor.y);\r\n\r\n    let m = anchorMatrix.translate(x, y);\r\n    m.multiplySelf(anchorMatrix.invertSelf());\r\n    m.rotateSelf(rotateFinal);\r\n    m.translateSelf(-anchor.x, -anchor.y);\r\n\r\n    return m;\r\n}\r\n\r\nfunction parseOffsetAnchor(str, transformOrigin, elem) {\r\n    const width = elem.offsetWidth;\r\n    const height = elem.offsetHeight;\r\n\r\n    if (!str || str === \"auto\") {\r\n        str = transformOrigin;\r\n    }\r\n\r\n    const parts = str.split(/\\s+/);\r\n    if (parts.length === 1) {\r\n        // 1-value syntax = x only, y = center\r\n        const x = parsePosition(parts[0], width);\r\n        return { x, y: height / 2 };\r\n    }\r\n\r\n    const x = parsePosition(parts[0], width);\r\n    const y = parsePosition(parts[1], height);\r\n    return { x, y };\r\n}\r\n\r\nfunction parsePosition(part, size) {\r\n    part = part.trim();\r\n    if (part.endsWith(\"%\")) {\r\n        return parseFloat(part) / 100 * size;\r\n    }\r\n    if (part.endsWith(\"px\")) {\r\n        return parseFloat(part);\r\n    }\r\n    // keywords\r\n    switch (part) {\r\n        case \"left\":   return 0;\r\n        case \"top\":    return 0;\r\n        case \"center\": return size / 2;\r\n        case \"right\":  return size;\r\n        case \"bottom\": return size;\r\n    }\r\n    return parseFloat(part);\r\n}\r\n\r\nfunction parseOffsetDistance(str) {\r\n    str = str.trim();\r\n    if (str.endsWith(\"%\")) {\r\n        return parseFloat(str) / 100; // normalized (0..1)\r\n    }\r\n    return parseFloat(str); // px value if pathLength = 1\r\n}\r\n\r\nfunction parseAngle(str) {\r\n    if (!str) return 0;\r\n    str = str.trim();\r\n    if (str.endsWith(\"deg\"))  return parseFloat(str);\r\n    if (str.endsWith(\"rad\"))  return parseFloat(str) * (180 / Math.PI);\r\n    if (str.endsWith(\"grad\")) return parseFloat(str) * 0.9;\r\n    return parseFloat(str);\r\n}\r\n\r\nfunction computeOffsetPathPoint(elem, offsetPath, distNorm) {\r\n    if (!offsetPath || offsetPath === \"none\") {\r\n        return { x: 0, y: 0, angle: 0 };\r\n    }\r\n\r\n    const value = offsetPath.trim();\r\n\r\n    let m = value.match(/path\\([\"'](.+)[\"']\\)/);\r\n    if (m) return computePathType(m[1], distNorm);\r\n\r\n    if (value.startsWith(\"circle(\"))  return computeCircle(value, distNorm);\r\n    if (value.startsWith(\"ellipse(\")) return computeEllipse(value, distNorm);\r\n    if (value.startsWith(\"inset(\"))   return computeInset(value, elem, distNorm);\r\n    if (value.startsWith(\"rect(\"))    return computeRect(value, distNorm);\r\n    if (value.startsWith(\"xywh(\"))    return computeXYWH(value, distNorm);\r\n    if (value.startsWith(\"ray(\"))     return computeRay(value, distNorm);\r\n    if (value.startsWith(\"polygon(\")) return computePolygon(value, distNorm);\r\n\r\n    console.warn(\"Unsupported offset-path:\", offsetPath);\r\n    return { x: 0, y: 0, angle: 0 };\r\n}\r\n\r\n\r\nfunction computePathType(pathData, distNorm) {\r\n    let svgPath = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n    svgPath.setAttribute(\"d\", pathData);\r\n\r\n    const total = svgPath.getTotalLength();\r\n    const dist = distNorm <= 1 ? distNorm * total : distNorm;\r\n\r\n    const p1 = svgPath.getPointAtLength(dist);\r\n    const p2 = svgPath.getPointAtLength(Math.min(total, dist + 0.01));\r\n\r\n    let angle = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;\r\n\r\n    return { x: p1.x, y: p1.y, angle };\r\n}\r\n\r\nfunction computeRay(str, t) {\r\n    let m = str.match(/ray\\(([^)]+)\\)/);\r\n    let inside = m[1].trim();\r\n\r\n    // Split on \"at\" (optional)\r\n    let [beforeAt, atPart] = inside.split(\"at\").map(s => s && s.trim());\r\n\r\n    // angle\r\n    let parts = beforeAt.split(/\\s+/);\r\n    let angleDeg = parseAngle(parts[0]);\r\n    let angleRad = angleDeg * Math.PI / 180;\r\n\r\n    // point of origin\r\n    let ox = 0, oy = 0;\r\n    if (atPart) {\r\n        const pos = atPart.split(/\\s+/);\r\n        ox = parseFloat(pos[0]);\r\n        oy = parseFloat(pos[1]);\r\n    }\r\n\r\n    // Ray: infinite line; offset-distance is distance along ray\r\n    let dist = (t <= 1 ? t : t); // percentage normalized already\r\n\r\n    let x = ox + Math.cos(angleRad) * dist;\r\n    let y = oy + Math.sin(angleRad) * dist;\r\n\r\n    // tangent is ray direction\r\n    return { x, y, angle: angleDeg };\r\n}\r\n\r\nfunction computeCircle(str, t) {\r\n    let m = str.match(/circle\\(([^)]+)\\)/);\r\n    let inner = m[1];\r\n\r\n    let [radiusPart, atPart] = inner.split(\"at\").map(s => s.trim());\r\n    let r = parseFloat(radiusPart);\r\n    let [cx, cy] = atPart.split(/\\s+/).map(parseFloat);\r\n\r\n    let angleRad = t * 2 * Math.PI;\r\n    let x = cx + Math.cos(angleRad) * r;\r\n    let y = cy + Math.sin(angleRad) * r;\r\n\r\n    let tangentAngleDeg = angleRad * 180 / Math.PI + 90;\r\n\r\n    return { x, y, angle: tangentAngleDeg };\r\n}\r\n\r\nfunction computeEllipse(str, t) {\r\n    let m = str.match(/ellipse\\(([^)]+)\\)/);\r\n    let parts = m[1].split(\"at\");\r\n    let radii = parts[0].trim().split(/\\s+/).map(parseFloat);\r\n    let center = parts[1].trim().split(/\\s+/).map(parseFloat);\r\n\r\n    let rx = radii[0];\r\n    let ry = radii[1];\r\n    let cx = center[0];\r\n    let cy = center[1];\r\n\r\n    let angleRad = t * 2 * Math.PI;\r\n\r\n    let x = cx + Math.cos(angleRad) * rx;\r\n    let y = cy + Math.sin(angleRad) * ry;\r\n\r\n    // tangent direction derivative\r\n    let dx = -Math.sin(angleRad) * rx;\r\n    let dy = Math.cos(angleRad) * ry;\r\n    let tangentAngleDeg = Math.atan2(dy, dx) * 180 / Math.PI;\r\n\r\n    return { x, y, angle: tangentAngleDeg };\r\n}\r\n\r\nfunction computeRect(str, t) {\r\n    let m = str.match(/rect\\(([^)]+)\\)/);\r\n    let nums = m[1].split(/\\s+/).map(s => parseFloat(s));\r\n    let top = nums[0], right = nums[1], bottom = nums[2], left = nums[3];\r\n    return rectPath(top, left, right, bottom, t);\r\n}\r\n\r\nfunction computeXYWH(str, t) {\r\n    let m = str.match(/xywh\\(([^)]+)\\)/);\r\n    let nums = m[1].split(/\\s+/).map(parseFloat);\r\n\r\n    let left   = nums[0];\r\n    let top    = nums[1];\r\n    let width  = nums[2];\r\n    let height = nums[3];\r\n\r\n    return rectPath(top, left, left + width, top + height, t);\r\n}\r\n\r\nfunction computePolygon(str, t) {\r\n    let m = str.match(/polygon\\(([^)]+)\\)/);\r\n    let pairs = m[1].split(\",\").map(p => p.trim().split(/\\s+/).map(parseFloat));\r\n\r\n    // Build cumulative lengths\r\n    let pts = pairs;\r\n    let lengths = [0];\r\n\r\n    for (let i = 1; i < pts.length; i++) {\r\n        let dx = pts[i][0] - pts[i - 1][0];\r\n        let dy = pts[i][1] - pts[i - 1][1];\r\n        lengths.push(Math.hypot(dx, dy) + lengths[i - 1]);\r\n    }\r\n\r\n    // close polygon\r\n    let dx = pts[0][0] - pts[pts.length - 1][0];\r\n    let dy = pts[0][1] - pts[pts.length - 1][1];\r\n    lengths.push(Math.hypot(dx, dy) + lengths[lengths.length - 1]);\r\n\r\n    let total = lengths[lengths.length - 1];\r\n    let target = t * total;\r\n\r\n    // find segment\r\n    let i = lengths.findIndex(len => len >= target);\r\n    if (i <= 0) i = 1;\r\n\r\n    let prevLen = lengths[i - 1];\r\n    let nextLen = lengths[i];\r\n    let segT = (target - prevLen) / (nextLen - prevLen);\r\n\r\n    // segment points\r\n    let a = pts[(i - 1) % pts.length];\r\n    let b = pts[i % pts.length];\r\n\r\n    let x = a[0] + (b[0] - a[0]) * segT;\r\n    let y = a[1] + (b[1] - a[1]) * segT;\r\n\r\n    let angle = Math.atan2(b[1] - a[1], b[0] - a[0]) * 180 / Math.PI;\r\n\r\n    return { x, y, angle };\r\n}\r\n\r\nfunction rectPath(top, left, right, bottom, t) {\r\n    let w = right - left;\r\n    let h = bottom - top;\r\n\r\n    let perimeter = 2 * (w + h);\r\n    let dist = t * perimeter;\r\n\r\n    // go around edges\r\n    if (dist < w) {\r\n        // top edge\r\n        let x = left + dist;\r\n        return { x, y: top, angle: 0 };\r\n    }\r\n    dist -= w;\r\n\r\n    if (dist < h) {\r\n        // right edge\r\n        let y = top + dist;\r\n        return { x: right, y, angle: 90 };\r\n    }\r\n    dist -= h;\r\n\r\n    if (dist < w) {\r\n        // bottom edge\r\n        let x = right - dist;\r\n        return { x, y: bottom, angle: 180 };\r\n    }\r\n    dist -= w;\r\n\r\n    // left edge\r\n    let y = bottom - dist;\r\n    return { x: left, y, angle: 270 };\r\n}\r\n\r\n// normalized inset uses calc\r\nfunction tokenizeCalc(input) {\r\n    let tokens = [];\r\n    let i = 0;\r\n\r\n    while (i < input.length) {\r\n        let ch = input[i];\r\n\r\n        if (/\\s/.test(ch)) {\r\n            i++;\r\n            continue;\r\n        }\r\n\r\n        // operators & parentheses\r\n        if (\"+-*/()\".includes(ch)) {\r\n            tokens.push({ type: ch, value: ch });\r\n            i++;\r\n            continue;\r\n        }\r\n\r\n        // numbers or dimensions or %\r\n        if (/[0-9.]/.test(ch)) {\r\n            let start = i;\r\n            while (/[0-9.]/.test(input[i])) i++;\r\n            let num = input.slice(start, i);\r\n\r\n            if (input[i] === \"%\") {\r\n                i++;\r\n                tokens.push({ type: \"percentage\", value: parseFloat(num) });\r\n                continue;\r\n            }\r\n\r\n            // only px supported\r\n            if (input.slice(i, i+2) === \"px\") {\r\n                i += 2;\r\n                tokens.push({ type: \"dimension\", value: parseFloat(num), unit: \"px\" });\r\n                continue;\r\n            }\r\n\r\n            // plain number\r\n            tokens.push({ type: \"number\", value: parseFloat(num) });\r\n            continue;\r\n        }\r\n\r\n        // function name (calc)\r\n        if (/[a-zA-Z]/.test(ch)) {\r\n            let start = i;\r\n            while (/[a-zA-Z]/.test(input[i])) i++;\r\n            let name = input.slice(start, i);\r\n\r\n            if (name === \"calc\" && input[i] === \"(\") {\r\n                tokens.push({ type: \"func\", value: \"calc\" });\r\n                continue;\r\n            }\r\n\r\n            throw new Error(\"Unsupported function: \" + name);\r\n        }\r\n\r\n        throw new Error(\"Unexpected character in calc(): \" + ch);\r\n    }\r\n\r\n    return tokens;\r\n}\r\n\r\n// normalized inset uses calc\r\nfunction parseCalc(tokens) {\r\n    let i = 0;\r\n\r\n    function peek() { return tokens[i]; }\r\n    function consume() { return tokens[i++]; }\r\n\r\n    function parseExpression() {\r\n        let node = parseTerm();\r\n        while (peek() && (peek().type === \"+\" || peek().type === \"-\")) {\r\n            let op = consume().type;\r\n            let right = parseTerm();\r\n            node = { type: \"binary\", op, left: node, right };\r\n        }\r\n        return node;\r\n    }\r\n\r\n    function parseTerm() {\r\n        let node = parseFactor();\r\n        while (peek() && (peek().type === \"*\" || peek().type === \"/\")) {\r\n            let op = consume().type;\r\n            let right = parseFactor();\r\n            node = { type: \"binary\", op, left: node, right };\r\n        }\r\n        return node;\r\n    }\r\n\r\n    function parseFactor() {\r\n        let t = peek();\r\n        if (!t) throw \"Unexpected end in calc()\";\r\n\r\n        if (t.type === \"number\") {\r\n            consume();\r\n            return { type: \"number\", value: t.value };\r\n        }\r\n\r\n        if (t.type === \"dimension\") {\r\n            consume();\r\n            return { type: \"dimension\", value: t.value, unit: t.unit };\r\n        }\r\n\r\n        if (t.type === \"percentage\") {\r\n            consume();\r\n            return { type: \"percentage\", value: t.value };\r\n        }\r\n\r\n        if (t.type === \"func\") {\r\n            consume(); // \"calc\"\r\n            if (peek().type !== \"(\") throw \"Expected '(' after calc\";\r\n            consume();\r\n            let node = parseExpression();\r\n            if (!peek() || peek().type !== \")\") throw \"Expected ')'\";\r\n            consume();\r\n            return node;\r\n        }\r\n\r\n        if (t.type === \"(\") {\r\n            consume();\r\n            let node = parseExpression();\r\n            if (!peek() || peek().type !== \")\") throw \"Expected ')'\";\r\n            consume();\r\n            return node;\r\n        }\r\n\r\n        throw new Error(\"Unexpected calc token \" + JSON.stringify(t));\r\n    }\r\n\r\n    let ast = parseExpression();\r\n    if (i !== tokens.length) throw \"Extra tokens after calc\";\r\n    return ast;\r\n}\r\n\r\n// normalized inset uses calc\r\nfunction evalCalc(ast, env) {\r\n    switch (ast.type) {\r\n        case \"number\":\r\n            return ast.value;\r\n\r\n        case \"dimension\":\r\n            return ast.value;   // px only -> already a number\r\n\r\n        case \"percentage\":\r\n            return env.percentBase * (ast.value / 100);\r\n\r\n        case \"binary\": {\r\n            let l = evalCalc(ast.left, env);\r\n            let r = evalCalc(ast.right, env);\r\n\r\n            switch (ast.op) {\r\n                case \"+\": return l + r;\r\n                case \"-\": return l - r;\r\n                case \"*\": return l * r;\r\n                case \"/\": return l / r;\r\n            }\r\n        }\r\n    }\r\n    throw \"Invalid AST node \" + ast.type;\r\n}\r\n\r\nfunction resolveLength(expr, element, useHeight = false) {\r\n    expr = expr.trim();\r\n\r\n    // Fast path: pure px\r\n    if (/^[0-9.]+px$/.test(expr))\r\n        return parseFloat(expr);\r\n\r\n    let base = useHeight ? element.offsetHeight : element.offsetWidth;\r\n\r\n    // Pure %\r\n    if (/^[0-9.]+%$/.test(expr)) {\r\n        let p = parseFloat(expr);\r\n        return base * (p / 100);\r\n    }\r\n\r\n    // calc(...) or mixed values\r\n    const ast = parseCalc(tokenizeCalc(expr));\r\n    return evalCalc(ast, {\r\n        percentBase: base\r\n    });\r\n}\r\n\r\nfunction parseInsetArgs(str) {\r\n    let inside = str.trim()\r\n        .replace(/^inset\\s*\\(/, \"\")\r\n        .replace(/\\)\\s*$/, \"\");\r\n\r\n    let args = [];\r\n    let current = \"\";\r\n    let depth = 0;\r\n\r\n    for (let i = 0; i < inside.length; i++) {\r\n        let ch = inside[i];\r\n\r\n        if (ch === \"(\") {\r\n            depth++;\r\n            current += ch;\r\n        } else if (ch === \")\") {\r\n            depth--;\r\n            current += ch;\r\n        } else if (/\\s/.test(ch) && depth === 0) {\r\n            if (current.trim() !== \"\") {\r\n                args.push(current.trim());\r\n                current = \"\";\r\n            }\r\n        } else {\r\n            current += ch;\r\n        }\r\n    }\r\n\r\n    if (current.trim() !== \"\") {\r\n        args.push(current.trim());\r\n    }\r\n\r\n    return args;\r\n}\r\n/**\r\n *\r\n * @param {string} str\r\n * @param {HTMLElement} element\r\n * @param {number} progress\r\n * @returns\r\n */\r\nfunction computeInset(str, element, progress) {\r\n    const args = parseInsetArgs(str);\r\n    if (args.length !== 4)\r\n        throw new Error(\"inset() must have 4 arguments\");\r\n\r\n    const topPx    = resolveLength(args[0], element, true);\r\n    const rightPx  = resolveLength(args[1], element, false);\r\n    const bottomPx = resolveLength(args[2], element, true);\r\n    const leftPx   = resolveLength(args[3], element, false);\r\n\r\n    const w = element.offsetWidth;\r\n    const h = element.offsetHeight;\r\n\r\n    // Actual rectangle coordinates\r\n    const x1 = leftPx;\r\n    const y1 = topPx;\r\n    const x2 = w - rightPx;\r\n    const y2 = h - bottomPx;\r\n\r\n    // Rectangle perimeter\r\n    const P = 2 * ((x2 - x1) + (y2 - y1));\r\n\r\n    let d = P * progress;\r\n\r\n    // Walk the rectangle clockwise, return point\r\n    // Top edge: (x1 -> x2, y1)\r\n    let len = x2 - x1;\r\n    if (d <= len) return { x: x1 + d, y: y1, angle: 0 };\r\n    d -= len;\r\n\r\n    // Right edge: (x2, y1 -> y2)\r\n    len = y2 - y1;\r\n    if (d <= len) return { x: x2, y: y1 + d, angle: 90 };\r\n    d -= len;\r\n\r\n    // Bottom edge: (x2 -> x1, y2)\r\n    len = x2 - x1;\r\n    if (d <= len) return { x: x2 - d, y: y2, angle: 180 };\r\n    d -= len;\r\n\r\n    // Left edge: (x1, y2 -> y1)\r\n    return { x: x1, y: y2 - d, angle: 270 };\r\n}\r\n\r\n\r\n//Code from: https://github.com/floating-ui/floating-ui/blob/master/packages/utils/src/dom.ts\r\n\r\nconst transformProperties = ['transform', 'translate', 'scale', 'rotate', 'perspective'];\r\nconst willChangeValues = ['transform', 'translate', 'scale', 'rotate', 'perspective', 'filter'];\r\nconst containValues = ['paint', 'layout', 'strict', 'content'];\r\n\r\nfunction isElement(value) {\r\n    const elType = value?.ownerDocument?.defaultView?.Element;\r\n    return value instanceof Element || (elType != null && value instanceof elType);\r\n}\r\n/**\r\n *\r\n * @param {CSSStyleDeclaration} css\r\n * @returns {boolean}\r\n */\r\nfunction isContainingBlock(css) {\r\n    // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\r\n    // https://drafts.csswg.org/css-transforms-2/#individual-transforms\r\n    return (\r\n        transformProperties.some((value) => css[value] ? css[value] !== 'none' : false) ||\r\n        (css.containerType ? css.containerType !== 'normal' : false) ||\r\n        (css.backdropFilter ? css.backdropFilter !== 'none' : false) ||\r\n        (css.filter ? css.filter !== 'none' : false) ||\r\n        willChangeValues.some((value) => (css.willChange || '').includes(value)) ||\r\n        containValues.some((value) => (css.contain || '').includes(value))\r\n    );\r\n}\r\n\r\n//Code from: https://github.com/jcfranco/composed-offset-position/blob/main/src/index.ts\r\nfunction flatTreeParent(element) {\r\n    if (element.assignedSlot)\r\n        return element.assignedSlot;\r\n    if (element.parentNode instanceof ShadowRoot)\r\n        return element.parentNode.host;\r\n    return element.parentNode;\r\n}\r\n\r\nfunction isFlatTreeInclusiveAncestor(ancestor, node) {\r\n    for (let current = node; current; current = flatTreeParent(current)) {\r\n        if (current === ancestor)\r\n            return true;\r\n    }\r\n    return false;\r\n}\r\n\r\nfunction ancestorTreeScopes(element) {\r\n    const scopes = new Set();\r\n    let currentScope = element.getRootNode();\r\n    const shadowRootCtor = element.ownerDocument?.defaultView?.ShadowRoot ?? ShadowRoot;\r\n    while (currentScope) {\r\n        scopes.add(currentScope);\r\n        if (currentScope instanceof shadowRootCtor) {\r\n            currentScope = currentScope.host?.getRootNode() ?? null;\r\n        } else {\r\n            currentScope = currentScope.parentNode\r\n                ? currentScope.parentNode.getRootNode()\r\n                : null;\r\n        }\r\n    }\r\n\r\n    return scopes;\r\n}\r\n\r\nfunction offsetParentPolyfill(element) {\r\n    // Do an initial walk to check for display:none ancestors.\r\n    for (let ancestor = element; ancestor; ancestor = flatTreeParent(ancestor)) {\r\n        if (!(ancestor instanceof Element))\r\n            continue;\r\n        if (getCachedComputedStyle(ancestor).display === 'none')\r\n            return null;\r\n    }\r\n\r\n    for (let ancestor = flatTreeParent(element); ancestor; ancestor = flatTreeParent(ancestor)) {\r\n        if (!(ancestor instanceof Element))\r\n            continue;\r\n        const style = getCachedComputedStyle(ancestor);\r\n        if (style.display === 'contents')\r\n            continue;\r\n        if (style.position !== 'static' || isContainingBlock(style))\r\n            return ancestor;\r\n        if (ancestor.tagName === 'BODY')\r\n            return ancestor;\r\n    }\r\n    return null;\r\n}\r\n/**\r\n *\r\n * @param {*} element\r\n * @param {'offsetTop' | 'offsetLeft'} offsetTopOrLeft\r\n * @returns\r\n */\r\nfunction offsetTopLeftPolyfill(element, offsetTopOrLeft) {\r\n    let value = element[offsetTopOrLeft];\r\n    let nextOffsetParent = offsetParentPolyfill(element);\r\n    const scopes = ancestorTreeScopes(element);\r\n\r\n    while (nextOffsetParent && !scopes.has(nextOffsetParent.getRootNode())) {\r\n        value -= nextOffsetParent[offsetTopOrLeft];\r\n        nextOffsetParent = offsetParentPolyfill(nextOffsetParent);\r\n    }\r\n\r\n    return value;\r\n}\r\n/**\r\n* @param {Element} element\r\n* @returns {boolean}\r\n*/\r\nfunction createsFixedContainingBlock(element) {\r\n    const cs = getCachedComputedStyle(element);\r\n    if ((cs.transform && cs.transform !== 'none') ||\r\n        (cs.perspective && cs.perspective !== 'none') ||\r\n        (cs.filter && cs.filter !== 'none') ||\r\n        (cs.backdropFilter && cs.backdropFilter !== 'none')) {\r\n        return true;\r\n    }\r\n\r\n    const contain = cs.contain || '';\r\n    if (contain.includes('paint') || contain.includes('layout') || contain.includes('strict') || contain.includes('content')) {\r\n        return true;\r\n    }\r\n\r\n    const willChange = cs.willChange || '';\r\n    return /transform|perspective|filter|backdrop-filter/.test(willChange);\r\n}\r\n/**\r\n* @param {HTMLElement} element\r\n* @param {HTMLIFrameElement[]=} iframes\r\n* @returns {Element | null}\r\n*/\r\nfunction getNearestFixedContainingBlock(element, iframes) {\r\n    let parent = getParentElementIncludingSlots(element, iframes);\r\n    while (parent) {\r\n        if (createsFixedContainingBlock(parent)) return parent;\r\n        parent = getParentElementIncludingSlots(parent, iframes);\r\n    }\r\n    return null;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/helper/w3color.ts",
    "content": "/* w3color.js ver.1.18 by w3schools.com (Do not remove this line)*/\r\n\r\nexport class w3color {\r\n  red: number = 0;\r\n  green: number = 0;\r\n  blue: number = 0;\r\n  hue: number = 0;\r\n  sat: number = 0;\r\n  lightness: number = 0;\r\n  whiteness: number = 0;\r\n  blackness: number = 0;\r\n  cyan: number = 0;\r\n  magenta: number = 0;\r\n  yellow: number = 0;\r\n  black: number = 0;\r\n  ncol: string = 'R';\r\n  opacity: number = 1;\r\n  valid: boolean = false;\r\n\r\n  toRgbString() {\r\n    return \"rgb(\" + this.red + \", \" + this.green + \", \" + this.blue + \")\";\r\n  }\r\n\r\n  toRgbaString() {\r\n    return \"rgba(\" + this.red + \", \" + this.green + \", \" + this.blue + \", \" + this.opacity + \")\";\r\n  }\r\n\r\n  toHwbString() {\r\n    return \"hwb(\" + this.hue + \", \" + Math.round(this.whiteness * 100) + \"%, \" + Math.round(this.blackness * 100) + \"%)\";\r\n  }\r\n\r\n  toHwbStringDecimal() {\r\n    return \"hwb(\" + this.hue + \", \" + this.whiteness + \", \" + this.blackness + \")\";\r\n  }\r\n\r\n  toHwbaString() {\r\n    return \"hwba(\" + this.hue + \", \" + Math.round(this.whiteness * 100) + \"%, \" + Math.round(this.blackness * 100) + \"%, \" + this.opacity + \")\";\r\n  }\r\n\r\n  toHslString() {\r\n    return \"hsl(\" + this.hue + \", \" + Math.round(this.sat * 100) + \"%, \" + Math.round(this.lightness * 100) + \"%)\";\r\n  }\r\n\r\n  toHslStringDecimal() {\r\n    return \"hsl(\" + this.hue + \", \" + this.sat + \", \" + this.lightness + \")\";\r\n  }\r\n\r\n  toHslaString() {\r\n    return \"hsla(\" + this.hue + \", \" + Math.round(this.sat * 100) + \"%, \" + Math.round(this.lightness * 100) + \"%, \" + this.opacity + \")\";\r\n  }\r\n\r\n  toCmykString() {\r\n    return \"cmyk(\" + Math.round(this.cyan * 100) + \"%, \" + Math.round(this.magenta * 100) + \"%, \" + Math.round(this.yellow * 100) + \"%, \" + Math.round(this.black * 100) + \"%)\";\r\n  }\r\n\r\n  toCmykStringDecimal() {\r\n    return \"cmyk(\" + this.cyan + \", \" + this.magenta + \", \" + this.yellow + \", \" + this.black + \")\";\r\n  }\r\n\r\n  toNcolString() {\r\n    return this.ncol + \", \" + Math.round(this.whiteness * 100) + \"%, \" + Math.round(this.blackness * 100) + \"%\";\r\n  }\r\n\r\n  toNcolStringDecimal() {\r\n    return this.ncol + \", \" + this.whiteness + \", \" + this.blackness;\r\n  }\r\n\r\n  toNcolaString() {\r\n    return this.ncol + \", \" + Math.round(this.whiteness * 100) + \"%, \" + Math.round(this.blackness * 100) + \"%, \" + this.opacity;\r\n  }\r\n\r\n  toName() {\r\n    let r, g, b, colorhexs = w3color.getColorArr('hexs');\r\n    for (let i = 0; i < colorhexs.length; i++) {\r\n      r = parseInt(colorhexs[i].substr(0, 2), 16);\r\n      g = parseInt(colorhexs[i].substr(2, 2), 16);\r\n      b = parseInt(colorhexs[i].substr(4, 2), 16);\r\n      if (this.red == r && this.green == g && this.blue == b) {\r\n        return w3color.getColorArr('names')[i];\r\n      }\r\n    }\r\n    return null;\r\n  }\r\n\r\n  toHexString() {\r\n    let r = w3color.toHex(this.red);\r\n    let g = w3color.toHex(this.green);\r\n    let b = w3color.toHex(this.blue);\r\n    return \"#\" + r + g + b;\r\n  }\r\n\r\n  toNameOrHexString() {\r\n    let name = this.toName();\r\n    if (!name)\r\n      name = this.toHexString();\r\n    return name;\r\n  }\r\n\r\n  toRgb() {\r\n    return { r: this.red, g: this.green, b: this.blue, a: this.opacity };\r\n  }\r\n\r\n  toHsl() {\r\n    return { h: this.hue, s: this.sat, l: this.lightness, a: this.opacity };\r\n  }\r\n\r\n  toHwb() {\r\n    return { h: this.hue, w: this.whiteness, b: this.blackness, a: this.opacity };\r\n  }\r\n\r\n  toCmyk() {\r\n    return { c: this.cyan, m: this.magenta, y: this.yellow, k: this.black, a: this.opacity };\r\n  }\r\n\r\n  toNcol() {\r\n    return { ncol: this.ncol, w: this.whiteness, b: this.blackness, a: this.opacity };\r\n  }\r\n\r\n  isDark(n) {\r\n    let m = (n || 128);\r\n    return (((this.red * 299 + this.green * 587 + this.blue * 114) / 1000) < m);\r\n  }\r\n\r\n  saturate(n) {\r\n    let x, rgb, color;\r\n    x = (n / 100 || 0.1);\r\n    this.sat += x;\r\n    if (this.sat > 1) { this.sat = 1; }\r\n    rgb = w3color.hslToRgb(this.hue, this.sat, this.lightness);\r\n    color = w3color.colorObject(rgb, this.opacity, this.hue, this.sat);\r\n    this.attachValues(color);\r\n  }\r\n\r\n  desaturate(n) {\r\n    let x, rgb, color;\r\n    x = (n / 100 || 0.1);\r\n    this.sat -= x;\r\n    if (this.sat < 0) { this.sat = 0; }\r\n    rgb = w3color.hslToRgb(this.hue, this.sat, this.lightness);\r\n    color = w3color.colorObject(rgb, this.opacity, this.hue, this.sat);\r\n    this.attachValues(color);\r\n  }\r\n\r\n  lighter(n) {\r\n    let x, rgb, color;\r\n    x = (n / 100 || 0.1);\r\n    this.lightness += x;\r\n    if (this.lightness > 1) { this.lightness = 1; }\r\n    rgb = w3color.hslToRgb(this.hue, this.sat, this.lightness);\r\n    color = w3color.colorObject(rgb, this.opacity, this.hue, this.sat);\r\n    this.attachValues(color);\r\n  }\r\n\r\n  darker(n) {\r\n    let x, rgb, color;\r\n    x = (n / 100 || 0.1);\r\n    this.lightness -= x;\r\n    if (this.lightness < 0) { this.lightness = 0; }\r\n    rgb = w3color.hslToRgb(this.hue, this.sat, this.lightness);\r\n    color = w3color.colorObject(rgb, this.opacity, this.hue, this.sat);\r\n    this.attachValues(color);\r\n  }\r\n\r\n  attachValues(color) {\r\n    this.red = color.red;\r\n    this.green = color.green;\r\n    this.blue = color.blue;\r\n    this.hue = color.hue;\r\n    this.sat = color.sat;\r\n    this.lightness = color.lightness;\r\n    this.whiteness = color.whiteness;\r\n    this.blackness = color.blackness;\r\n    this.cyan = color.cyan;\r\n    this.magenta = color.magenta;\r\n    this.yellow = color.yellow;\r\n    this.black = color.black;\r\n    this.ncol = color.ncol;\r\n    this.opacity = color.opacity;\r\n    this.valid = color.valid;\r\n  }\r\n\r\n  static toColorObject(c): w3color {\r\n    let x, y, typ, arr = [], arrlength, i, opacity, match, a, hue, sat, rgb, colornames = [], colorhexs = [];\r\n    c = w3color.w3trim(c.toLowerCase());\r\n    x = c.substr(0, 1).toUpperCase();\r\n    y = c.substr(1);\r\n    a = 1;\r\n    if ((x == \"R\" || x == \"Y\" || x == \"G\" || x == \"C\" || x == \"B\" || x == \"M\" || x == \"W\") && !isNaN(y)) {\r\n      if (c.length == 6 && c.indexOf(\",\") == -1) {\r\n      } else {\r\n        c = \"ncol(\" + c + \")\";\r\n      }\r\n    }\r\n    if (c.length != 3 && c.length != 6 && !isNaN(c)) { c = \"ncol(\" + c + \")\"; }\r\n    if (c.indexOf(\",\") > 0 && c.indexOf(\"(\") == -1) { c = \"ncol(\" + c + \")\"; }\r\n    if (c.substr(0, 3) == \"rgb\" || c.substr(0, 3) == \"hsl\" || c.substr(0, 3) == \"hwb\" || c.substr(0, 4) == \"ncol\" || c.substr(0, 4) == \"cmyk\") {\r\n      if (c.substr(0, 4) == \"ncol\") {\r\n        if (c.split(\",\").length == 4 && c.indexOf(\"ncola\") == -1) {\r\n          c = c.replace(\"ncol\", \"ncola\");\r\n        }\r\n        typ = \"ncol\";\r\n        c = c.substr(4);\r\n      } else if (c.substr(0, 4) == \"cmyk\") {\r\n        typ = \"cmyk\";\r\n        c = c.substr(4);\r\n      } else {\r\n        typ = c.substr(0, 3);\r\n        c = c.substr(3);\r\n      }\r\n      arrlength = 3;\r\n      opacity = false;\r\n      if (c.substr(0, 1).toLowerCase() == \"a\") {\r\n        arrlength = 4;\r\n        opacity = true;\r\n        c = c.substr(1);\r\n      } else if (typ == \"cmyk\") {\r\n        arrlength = 4;\r\n        if (c.split(\",\").length == 5) {\r\n          arrlength = 5;\r\n          opacity = true;\r\n        }\r\n      }\r\n      c = c.replace(\"(\", \"\");\r\n      c = c.replace(\")\", \"\");\r\n      arr = c.split(\",\");\r\n      if (typ == \"rgb\") {\r\n        if (arr.length != arrlength) {\r\n          return new w3color();\r\n        }\r\n        for (i = 0; i < arrlength; i++) {\r\n          if (arr[i] == \"\" || arr[i] == \" \") { arr[i] = \"0\"; }\r\n          if (arr[i].indexOf(\"%\") > -1) {\r\n            arr[i] = arr[i].replace(\"%\", \"\");\r\n            arr[i] = Number(arr[i] / 100);\r\n            if (i < 3) { arr[i] = Math.round(arr[i] * 255); }\r\n          }\r\n          if (isNaN(arr[i])) { return new w3color(); }\r\n          if (parseInt(arr[i]) > 255) { arr[i] = 255; }\r\n          if (i < 3) { arr[i] = parseInt(arr[i]); }\r\n          if (i == 3 && Number(arr[i]) > 1) { arr[i] = 1; }\r\n        }\r\n        rgb = { r: arr[0], g: arr[1], b: arr[2] };\r\n        if (opacity == true) { a = Number(arr[3]); }\r\n      }\r\n      if (typ == \"hsl\" || typ == \"hwb\" || typ == \"ncol\") {\r\n        while (arr.length < arrlength) { arr.push(\"0\"); }\r\n        if (typ == \"hsl\" || typ == \"hwb\") {\r\n          if (parseInt(arr[0]) >= 360) { arr[0] = 0; }\r\n        }\r\n        for (i = 1; i < arrlength; i++) {\r\n          if (arr[i].indexOf(\"%\") > -1) {\r\n            arr[i] = arr[i].replace(\"%\", \"\");\r\n            arr[i] = Number(arr[i]);\r\n            if (isNaN(arr[i])) { return new w3color(); }\r\n            arr[i] = arr[i] / 100;\r\n          } else {\r\n            arr[i] = Number(arr[i]);\r\n          }\r\n          if (Number(arr[i]) > 1) { arr[i] = 1; }\r\n          if (Number(arr[i]) < 0) { arr[i] = 0; }\r\n        }\r\n        if (typ == \"hsl\") { rgb = w3color.hslToRgb(arr[0], arr[1], arr[2]); hue = Number(arr[0]); sat = Number(arr[1]); }\r\n        if (typ == \"hwb\") { rgb = w3color.hwbToRgb(arr[0], arr[1], arr[2]); }\r\n        if (typ == \"ncol\") { rgb = w3color.ncolToRgb(arr[0], arr[1], arr[2]); }\r\n        if (opacity == true) { a = Number(arr[3]); }\r\n      }\r\n      if (typ == \"cmyk\") {\r\n        while (arr.length < arrlength) { arr.push(\"0\"); }\r\n        for (i = 0; i < arrlength; i++) {\r\n          if (arr[i].indexOf(\"%\") > -1) {\r\n            arr[i] = arr[i].replace(\"%\", \"\");\r\n            arr[i] = Number(arr[i]);\r\n            if (isNaN(arr[i])) { return new w3color(); }\r\n            arr[i] = arr[i] / 100;\r\n          } else {\r\n            arr[i] = Number(arr[i]);\r\n          }\r\n          if (Number(arr[i]) > 1) { arr[i] = 1; }\r\n          if (Number(arr[i]) < 0) { arr[i] = 0; }\r\n        }\r\n        rgb = w3color.cmykToRgb(arr[0], arr[1], arr[2], arr[3]);\r\n        if (opacity == true) { a = Number(arr[4]); }\r\n      }\r\n    } else if (c.substr(0, 3) == \"ncs\") {\r\n      rgb = w3color.ncsToRgb(c);\r\n    } else {\r\n      match = false;\r\n      colornames = w3color.getColorArr('names');\r\n      for (i = 0; i < colornames.length; i++) {\r\n        if (c.toLowerCase() == colornames[i].toLowerCase()) {\r\n          colorhexs = w3color.getColorArr('hexs');\r\n          match = true;\r\n          rgb = {\r\n            r: parseInt(colorhexs[i].substr(0, 2), 16),\r\n            g: parseInt(colorhexs[i].substr(2, 2), 16),\r\n            b: parseInt(colorhexs[i].substr(4, 2), 16)\r\n          };\r\n          break;\r\n        }\r\n      }\r\n      if (match == false) {\r\n        c = c.replace(\"#\", \"\");\r\n        if (c.length == 3) { c = c.substr(0, 1) + c.substr(0, 1) + c.substr(1, 1) + c.substr(1, 1) + c.substr(2, 1) + c.substr(2, 1); }\r\n        for (i = 0; i < c.length; i++) {\r\n          if (!w3color.isHex(c.substr(i, 1))) { return new w3color(); }\r\n        }\r\n        arr[0] = parseInt(c.substr(0, 2), 16);\r\n        arr[1] = parseInt(c.substr(2, 2), 16);\r\n        arr[2] = parseInt(c.substr(4, 2), 16);\r\n        for (i = 0; i < 3; i++) {\r\n          if (isNaN(arr[i])) { return new w3color(); }\r\n        }\r\n        rgb = {\r\n          r: arr[0],\r\n          g: arr[1],\r\n          b: arr[2]\r\n        };\r\n      }\r\n    }\r\n    return w3color.colorObject(rgb, a, hue, sat);\r\n  }\r\n  static colorObject(rgb, a, h, s) {\r\n    let hsl, hwb, cmyk, ncol, color, hue, sat;\r\n    if (!rgb) { return new w3color(); }\r\n    if (a === null) { a = 1; }\r\n    hsl = w3color.rgbToHsl(rgb.r, rgb.g, rgb.b);\r\n    hwb = w3color.rgbToHwb(rgb.r, rgb.g, rgb.b);\r\n    cmyk = w3color.rgbToCmyk(rgb.r, rgb.g, rgb.b);\r\n    hue = (h || hsl.h);\r\n    sat = (s || hsl.s);\r\n    ncol = w3color.hueToNcol(hue);\r\n    color = {\r\n      red: rgb.r,\r\n      green: rgb.g,\r\n      blue: rgb.b,\r\n      hue: hue,\r\n      sat: sat,\r\n      lightness: hsl.l,\r\n      whiteness: hwb.w,\r\n      blackness: hwb.b,\r\n      cyan: cmyk.c,\r\n      magenta: cmyk.m,\r\n      yellow: cmyk.y,\r\n      black: cmyk.k,\r\n      ncol: ncol,\r\n      opacity: a,\r\n      valid: true\r\n    };\r\n    color = w3color.roundDecimals(color);\r\n    return Object.assign(new w3color(), color);\r\n  }\r\n\r\n  static getColorArr(x) {\r\n    if (x == \"names\") { return ['AliceBlue', 'AntiqueWhite', 'Aqua', 'Aquamarine', 'Azure', 'Beige', 'Bisque', 'Black', 'BlanchedAlmond', 'Blue', 'BlueViolet', 'Brown', 'BurlyWood', 'CadetBlue', 'Chartreuse', 'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan', 'DarkBlue', 'DarkCyan', 'DarkGoldenRod', 'DarkGray', 'DarkGrey', 'DarkGreen', 'DarkKhaki', 'DarkMagenta', 'DarkOliveGreen', 'DarkOrange', 'DarkOrchid', 'DarkRed', 'DarkSalmon', 'DarkSeaGreen', 'DarkSlateBlue', 'DarkSlateGray', 'DarkSlateGrey', 'DarkTurquoise', 'DarkViolet', 'DeepPink', 'DeepSkyBlue', 'DimGray', 'DimGrey', 'DodgerBlue', 'FireBrick', 'FloralWhite', 'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod', 'Gray', 'Grey', 'Green', 'GreenYellow', 'HoneyDew', 'HotPink', 'IndianRed', 'Indigo', 'Ivory', 'Khaki', 'Lavender', 'LavenderBlush', 'LawnGreen', 'LemonChiffon', 'LightBlue', 'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey', 'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue', 'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime', 'LimeGreen', 'Linen', 'Magenta', 'Maroon', 'MediumAquaMarine', 'MediumBlue', 'MediumOrchid', 'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen', 'MediumTurquoise', 'MediumVioletRed', 'MidnightBlue', 'MintCream', 'MistyRose', 'Moccasin', 'NavajoWhite', 'Navy', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed', 'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed', 'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple', 'RebeccaPurple', 'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Salmon', 'SandyBrown', 'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue', 'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'Tan', 'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White', 'WhiteSmoke', 'Yellow', 'YellowGreen']; }\r\n    if (x == \"hexs\") { return ['f0f8ff', 'faebd7', '00ffff', '7fffd4', 'f0ffff', 'f5f5dc', 'ffe4c4', '000000', 'ffebcd', '0000ff', '8a2be2', 'a52a2a', 'deb887', '5f9ea0', '7fff00', 'd2691e', 'ff7f50', '6495ed', 'fff8dc', 'dc143c', '00ffff', '00008b', '008b8b', 'b8860b', 'a9a9a9', 'a9a9a9', '006400', 'bdb76b', '8b008b', '556b2f', 'ff8c00', '9932cc', '8b0000', 'e9967a', '8fbc8f', '483d8b', '2f4f4f', '2f4f4f', '00ced1', '9400d3', 'ff1493', '00bfff', '696969', '696969', '1e90ff', 'b22222', 'fffaf0', '228b22', 'ff00ff', 'dcdcdc', 'f8f8ff', 'ffd700', 'daa520', '808080', '808080', '008000', 'adff2f', 'f0fff0', 'ff69b4', 'cd5c5c', '4b0082', 'fffff0', 'f0e68c', 'e6e6fa', 'fff0f5', '7cfc00', 'fffacd', 'add8e6', 'f08080', 'e0ffff', 'fafad2', 'd3d3d3', 'd3d3d3', '90ee90', 'ffb6c1', 'ffa07a', '20b2aa', '87cefa', '778899', '778899', 'b0c4de', 'ffffe0', '00ff00', '32cd32', 'faf0e6', 'ff00ff', '800000', '66cdaa', '0000cd', 'ba55d3', '9370db', '3cb371', '7b68ee', '00fa9a', '48d1cc', 'c71585', '191970', 'f5fffa', 'ffe4e1', 'ffe4b5', 'ffdead', '000080', 'fdf5e6', '808000', '6b8e23', 'ffa500', 'ff4500', 'da70d6', 'eee8aa', '98fb98', 'afeeee', 'db7093', 'ffefd5', 'ffdab9', 'cd853f', 'ffc0cb', 'dda0dd', 'b0e0e6', '800080', '663399', 'ff0000', 'bc8f8f', '4169e1', '8b4513', 'fa8072', 'f4a460', '2e8b57', 'fff5ee', 'a0522d', 'c0c0c0', '87ceeb', '6a5acd', '708090', '708090', 'fffafa', '00ff7f', '4682b4', 'd2b48c', '008080', 'd8bfd8', 'ff6347', '40e0d0', 'ee82ee', 'f5deb3', 'ffffff', 'f5f5f5', 'ffff00', '9acd32']; }\r\n    return null;\r\n  }\r\n\r\n  static roundDecimals(c) {\r\n    c.red = Number(c.red.toFixed(0));\r\n    c.green = Number(c.green.toFixed(0));\r\n    c.blue = Number(c.blue.toFixed(0));\r\n    c.hue = Number(c.hue.toFixed(0));\r\n    c.sat = Number(c.sat.toFixed(2));\r\n    c.lightness = Number(c.lightness.toFixed(2));\r\n    c.whiteness = Number(c.whiteness.toFixed(2));\r\n    c.blackness = Number(c.blackness.toFixed(2));\r\n    c.cyan = Number(c.cyan.toFixed(2));\r\n    c.magenta = Number(c.magenta.toFixed(2));\r\n    c.yellow = Number(c.yellow.toFixed(2));\r\n    c.black = Number(c.black.toFixed(2));\r\n    c.ncol = c.ncol.substr(0, 1) + Math.round(Number(c.ncol.substr(1)));\r\n    c.opacity = Number(c.opacity.toFixed(2));\r\n    return c;\r\n  }\r\n\r\n  static hslToRgb(hue, sat, light) {\r\n    let t1, t2, r, g, b;\r\n    hue = hue / 60;\r\n    if (light <= 0.5) {\r\n      t2 = light * (sat + 1);\r\n    } else {\r\n      t2 = light + sat - (light * sat);\r\n    }\r\n    t1 = light * 2 - t2;\r\n    r = w3color.hueToRgb(t1, t2, hue + 2) * 255;\r\n    g = w3color.hueToRgb(t1, t2, hue) * 255;\r\n    b = w3color.hueToRgb(t1, t2, hue - 2) * 255;\r\n    return { r: r, g: g, b: b };\r\n  }\r\n\r\n  static hueToRgb(t1, t2, hue) {\r\n    if (hue < 0) hue += 6;\r\n    if (hue >= 6) hue -= 6;\r\n    if (hue < 1) return (t2 - t1) * hue + t1;\r\n    else if (hue < 3) return t2;\r\n    else if (hue < 4) return (t2 - t1) * (4 - hue) + t1;\r\n    else return t1;\r\n  }\r\n\r\n  static hwbToRgb(hue, white, black) {\r\n    let i, rgb, rgbArr = [], tot;\r\n    rgb = w3color.hslToRgb(hue, 1, 0.50);\r\n    rgbArr[0] = rgb.r / 255;\r\n    rgbArr[1] = rgb.g / 255;\r\n    rgbArr[2] = rgb.b / 255;\r\n    tot = white + black;\r\n    if (tot > 1) {\r\n      white = Number((white / tot).toFixed(2));\r\n      black = Number((black / tot).toFixed(2));\r\n    }\r\n    for (i = 0; i < 3; i++) {\r\n      rgbArr[i] *= (1 - (white) - (black));\r\n      rgbArr[i] += (white);\r\n      rgbArr[i] = Number(rgbArr[i] * 255);\r\n    }\r\n    return { r: rgbArr[0], g: rgbArr[1], b: rgbArr[2] };\r\n  }\r\n\r\n  static cmykToRgb(c, m, y, k) {\r\n    let r, g, b;\r\n    r = 255 - ((Math.min(1, c * (1 - k) + k)) * 255);\r\n    g = 255 - ((Math.min(1, m * (1 - k) + k)) * 255);\r\n    b = 255 - ((Math.min(1, y * (1 - k) + k)) * 255);\r\n    return { r: r, g: g, b: b };\r\n  }\r\n\r\n  static ncolToRgb(ncol, white, black) {\r\n    let letter, percent, h;\r\n    h = ncol;\r\n    if (isNaN(ncol.substr(0, 1))) {\r\n      letter = ncol.substr(0, 1).toUpperCase();\r\n      percent = ncol.substr(1);\r\n      if (percent == \"\") { percent = 0; }\r\n      percent = Number(percent);\r\n      if (isNaN(percent)) { return false; }\r\n      if (letter == \"R\") { h = 0 + (percent * 0.6); }\r\n      if (letter == \"Y\") { h = 60 + (percent * 0.6); }\r\n      if (letter == \"G\") { h = 120 + (percent * 0.6); }\r\n      if (letter == \"C\") { h = 180 + (percent * 0.6); }\r\n      if (letter == \"B\") { h = 240 + (percent * 0.6); }\r\n      if (letter == \"M\") { h = 300 + (percent * 0.6); }\r\n      if (letter == \"W\") {\r\n        h = 0;\r\n        white = 1 - (percent / 100);\r\n        black = (percent / 100);\r\n      }\r\n    }\r\n    return w3color.hwbToRgb(h, white, black);\r\n  }\r\n\r\n  static hueToNcol(hue) {\r\n    while (hue >= 360) {\r\n      hue = hue - 360;\r\n    }\r\n    if (hue < 60) { return \"R\" + (hue / 0.6); }\r\n    if (hue < 120) { return \"Y\" + ((hue - 60) / 0.6); }\r\n    if (hue < 180) { return \"G\" + ((hue - 120) / 0.6); }\r\n    if (hue < 240) { return \"C\" + ((hue - 180) / 0.6); }\r\n    if (hue < 300) { return \"B\" + ((hue - 240) / 0.6); }\r\n    if (hue < 360) { return \"M\" + ((hue - 300) / 0.6); }\r\n    return null;\r\n  }\r\n\r\n  static ncsToRgb(ncs): { r: number, b: number, g: number } {\r\n    let black, chroma, bc, percent, black1, chroma1, factor1, blue1, red1, red2, green1, green2, blue2, max, factor2, grey, r, g, b;\r\n    ncs = w3color.w3trim(ncs).toUpperCase();\r\n    ncs = ncs.replace(\"(\", \"\");\r\n    ncs = ncs.replace(\")\", \"\");\r\n    ncs = ncs.replace(\"NCS\", \"NCS \");\r\n    ncs = ncs.replace(/  /g, \" \");\r\n    if (ncs.indexOf(\"NCS\") == -1) { ncs = \"NCS \" + ncs; }\r\n    ncs = ncs.match(/^(?:NCS|NCS\\sS)\\s(\\d{2})(\\d{2})-(N|[A-Z])(\\d{2})?([A-Z])?$/);\r\n    if (ncs === null) return null;\r\n    black = parseInt(ncs[1], 10);\r\n    chroma = parseInt(ncs[2], 10);\r\n    bc = ncs[3];\r\n    if (bc != \"N\" && bc != \"Y\" && bc != \"R\" && bc != \"B\" && bc != \"G\") { return null; }\r\n    percent = parseInt(ncs[4], 10) || 0;\r\n    if (bc !== 'N') {\r\n      black1 = (1.05 * black - 5.25);\r\n      chroma1 = chroma;\r\n      if (bc === 'Y' && percent <= 60) {\r\n        red1 = 1;\r\n      } else if ((bc === 'Y' && percent > 60) || (bc === 'R' && percent <= 80)) {\r\n        if (bc === 'Y') {\r\n          factor1 = percent - 60;\r\n        } else {\r\n          factor1 = percent + 40;\r\n        }\r\n        red1 = ((Math.sqrt(14884 - Math.pow(factor1, 2))) - 22) / 100;\r\n      } else if ((bc === 'R' && percent > 80) || (bc === 'B')) {\r\n        red1 = 0;\r\n      } else if (bc === 'G') {\r\n        factor1 = (percent - 170);\r\n        red1 = ((Math.sqrt(33800 - Math.pow(factor1, 2))) - 70) / 100;\r\n      }\r\n      if (bc === 'Y' && percent <= 80) {\r\n        blue1 = 0;\r\n      } else if ((bc === 'Y' && percent > 80) || (bc === 'R' && percent <= 60)) {\r\n        if (bc === 'Y') {\r\n          factor1 = (percent - 80) + 20.5;\r\n        } else {\r\n          factor1 = (percent + 20) + 20.5;\r\n        }\r\n        blue1 = (104 - (Math.sqrt(11236 - Math.pow(factor1, 2)))) / 100;\r\n      } else if ((bc === 'R' && percent > 60) || (bc === 'B' && percent <= 80)) {\r\n        if (bc === 'R') {\r\n          factor1 = (percent - 60) - 60;\r\n        } else {\r\n          factor1 = (percent + 40) - 60;\r\n        }\r\n        blue1 = ((Math.sqrt(10000 - Math.pow(factor1, 2))) - 10) / 100;\r\n      } else if ((bc === 'B' && percent > 80) || (bc === 'G' && percent <= 40)) {\r\n        if (bc === 'B') {\r\n          factor1 = (percent - 80) - 131;\r\n        } else {\r\n          factor1 = (percent + 20) - 131;\r\n        }\r\n        blue1 = (122 - (Math.sqrt(19881 - Math.pow(factor1, 2)))) / 100;\r\n      } else if (bc === 'G' && percent > 40) {\r\n        blue1 = 0;\r\n      }\r\n      if (bc === 'Y') {\r\n        green1 = (85 - 17 / 20 * percent) / 100;\r\n      } else if (bc === 'R' && percent <= 60) {\r\n        green1 = 0;\r\n      } else if (bc === 'R' && percent > 60) {\r\n        factor1 = (percent - 60) + 35;\r\n        green1 = (67.5 - (Math.sqrt(5776 - Math.pow(factor1, 2)))) / 100;\r\n      } else if (bc === 'B' && percent <= 60) {\r\n        factor1 = (1 * percent - 68.5);\r\n        green1 = (6.5 + (Math.sqrt(7044.5 - Math.pow(factor1, 2)))) / 100;\r\n      } else if ((bc === 'B' && percent > 60) || (bc === 'G' && percent <= 60)) {\r\n        green1 = 0.9;\r\n      } else if (bc === 'G' && percent > 60) {\r\n        factor1 = (percent - 60);\r\n        green1 = (90 - (1 / 8 * factor1)) / 100;\r\n      }\r\n      factor1 = (red1 + green1 + blue1) / 3;\r\n      red2 = ((factor1 - red1) * (100 - chroma1) / 100) + red1;\r\n      green2 = ((factor1 - green1) * (100 - chroma1) / 100) + green1;\r\n      blue2 = ((factor1 - blue1) * (100 - chroma1) / 100) + blue1;\r\n      if (red2 > green2 && red2 > blue2) {\r\n        max = red2;\r\n      } else if (green2 > red2 && green2 > blue2) {\r\n        max = green2;\r\n      } else if (blue2 > red2 && blue2 > green2) {\r\n        max = blue2;\r\n      } else {\r\n        max = (red2 + green2 + blue2) / 3;\r\n      }\r\n      factor2 = 1 / max;\r\n      //@ts-ignore\r\n      r = parseInt((red2 * factor2 * (100 - black1) / 100) * 255, 10);\r\n      //@ts-ignore\r\n      g = parseInt((green2 * factor2 * (100 - black1) / 100) * 255, 10);\r\n      //@ts-ignore\r\n      b = parseInt((blue2 * factor2 * (100 - black1) / 100) * 255, 10);\r\n      if (r > 255) { r = 255; }\r\n      if (g > 255) { g = 255; }\r\n      if (b > 255) { b = 255; }\r\n      if (r < 0) { r = 0; }\r\n      if (g < 0) { g = 0; }\r\n      if (b < 0) { b = 0; }\r\n    } else {\r\n      //@ts-ignore\r\n      grey = parseInt((1 - black / 100) * 255, 10);\r\n      if (grey > 255) { grey = 255; }\r\n      if (grey < 0) { grey = 0; }\r\n      r = grey;\r\n      g = grey;\r\n      b = grey;\r\n    }\r\n    return {\r\n      r: r,\r\n      g: g,\r\n      b: b\r\n    };\r\n  }\r\n\r\n  static rgbToHsl(r, g, b) {\r\n    let min, max, i, l, s, maxcolor, h, rgb = [];\r\n    rgb[0] = r / 255;\r\n    rgb[1] = g / 255;\r\n    rgb[2] = b / 255;\r\n    min = rgb[0];\r\n    max = rgb[0];\r\n    maxcolor = 0;\r\n    for (i = 0; i < rgb.length - 1; i++) {\r\n      if (rgb[i + 1] <= min) { min = rgb[i + 1]; }\r\n      if (rgb[i + 1] >= max) { max = rgb[i + 1]; maxcolor = i + 1; }\r\n    }\r\n    if (maxcolor == 0) {\r\n      h = (rgb[1] - rgb[2]) / (max - min);\r\n    }\r\n    if (maxcolor == 1) {\r\n      h = 2 + (rgb[2] - rgb[0]) / (max - min);\r\n    }\r\n    if (maxcolor == 2) {\r\n      h = 4 + (rgb[0] - rgb[1]) / (max - min);\r\n    }\r\n    if (isNaN(h)) { h = 0; }\r\n    h = h * 60;\r\n    if (h < 0) { h = h + 360; }\r\n    l = (min + max) / 2;\r\n    if (min == max) {\r\n      s = 0;\r\n    } else {\r\n      if (l < 0.5) {\r\n        s = (max - min) / (max + min);\r\n      } else {\r\n        s = (max - min) / (2 - max - min);\r\n      }\r\n    }\r\n    s = s;\r\n    return { h: h, s: s, l: l };\r\n  }\r\n\r\n  static rgbToHwb(r, g, b) {\r\n    let h, w, bl;\r\n    r = r / 255;\r\n    g = g / 255;\r\n    b = b / 255;\r\n    let max = Math.max(r, g, b);\r\n    let min = Math.min(r, g, b);\r\n    let chroma = max - min;\r\n    if (chroma == 0) {\r\n      h = 0;\r\n    } else if (r == max) {\r\n      h = (((g - b) / chroma) % 6) * 360;\r\n    } else if (g == max) {\r\n      h = ((((b - r) / chroma) + 2) % 6) * 360;\r\n    } else {\r\n      h = ((((r - g) / chroma) + 4) % 6) * 360;\r\n    }\r\n    w = min;\r\n    bl = 1 - max;\r\n    return { h: h, w: w, b: bl };\r\n  }\r\n\r\n  static rgbToCmyk(r, g, b) {\r\n    let c, m, y, k;\r\n    r = r / 255;\r\n    g = g / 255;\r\n    b = b / 255;\r\n    let max = Math.max(r, g, b);\r\n    k = 1 - max;\r\n    if (k == 1) {\r\n      c = 0;\r\n      m = 0;\r\n      y = 0;\r\n    } else {\r\n      c = (1 - r - k) / (1 - k);\r\n      m = (1 - g - k) / (1 - k);\r\n      y = (1 - b - k) / (1 - k);\r\n    }\r\n    return { c: c, m: m, y: y, k: k };\r\n  }\r\n\r\n  static toHex(n) {\r\n    let hex = n.toString(16);\r\n    while (hex.length < 2) { hex = \"0\" + hex; }\r\n    return hex;\r\n  }\r\n\r\n  static w3trim(x) {\r\n    return x.replace(/^\\s+|\\s+$/g, '');\r\n  }\r\n\r\n\r\n  static isHex(x) {\r\n    return ('0123456789ABCDEFabcdef'.indexOf(x) > -1);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/BindingMode.ts",
    "content": "export enum BindingMode {\n    oneWay = 'oneWay',\n    twoWay = 'twoWay'\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/BindingTarget.ts",
    "content": "export enum BindingTarget {\r\n    /** Bindings for example starting with . to explicitly target a property */\r\n    explicitProperty = 'explicitProperty',\r\n    property = 'property',\r\n    attribute = 'attribute',\r\n    class = 'class',\r\n    css = 'css',\r\n    cssvar = 'cssvar',\r\n    event = 'event',\r\n    content = 'content', //innertext or html... mhmmm,\r\n    visible = 'visible'\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/DesignItem.ts",
    "content": "import { ServiceContainer } from '../services/ServiceContainer.js';\r\nimport { IDesignItem } from './IDesignItem.js';\r\nimport { InstanceServiceContainer } from '../services/InstanceServiceContainer.js';\r\nimport { CssStyleChangeAction } from '../services/undoService/transactionItems/CssStyleChangeAction.js';\r\nimport { ChangeGroup } from '../services/undoService/ChangeGroup.js';\r\nimport { NodeType } from './NodeType.js';\r\nimport { AttributeChangeAction } from '../services/undoService/transactionItems/AttributeChangeAction.js';\r\nimport { ExtensionType } from '../widgets/designerView/extensions/ExtensionType.js';\r\nimport { CssAttributeParser } from '../helper/CssAttributeParser.js';\r\nimport { ISize } from '../../interfaces/ISize.js';\r\nimport { PropertiesHelper } from '../services/propertiesService/services/PropertiesHelper.js';\r\nimport { InsertChildAction } from '../services/undoService/transactionItems/InsertChildAction.js';\r\nimport { DomConverter } from '../widgets/designerView/DomConverter.js';\r\nimport { IStyleRule } from '../services/stylesheetService/IStylesheetService.js';\r\nimport { enableStylesheetService } from '../widgets/designerView/extensions/buttons/StylesheetServiceDesignViewConfigButtons.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IPlacementService } from '../services/placementService/IPlacementService.js';\r\nimport { TextContentChangeAction } from '../services/undoService/transactionItems/TextContentChangeAction.js';\r\nimport { PropertyChangeAction } from '../services/undoService/transactionItems/PropertyChangeAction.js';\r\nimport { deepValue } from '../helper/Helper.js';\r\nimport { AttributeAndPropertyChangeAction } from '../services/undoService/transactionItems/AttributeAndPropertyChangeAction.js';\r\n\r\nexport const hideAtDesignTimeAttributeName = 'node-projects-hide-at-design-time';\r\nexport const hideAtRunTimeAttributeName = 'node-projects-hide-at-run-time';\r\nexport const lockAtDesignTimeAttributeName = 'node-projects-lock-at-design-time';\r\n\r\nexport const forceHoverAttributeName = 'node-projects-force-hover';\r\nexport const forceActiveAttributeName = 'node-projects-force-active';\r\nexport const forceVisitedAttributeName = 'node-projects-force-visited';\r\nexport const forceFocusAttributeName = 'node-projects-force-focus';\r\nexport const forceFocusWithinAttributeName = 'node-projects-force-focus-within';\r\nexport const forceFocusVisibleAttributeName = 'node-projects-force-focus-visible';\r\n\r\nexport class DesignItem implements IDesignItem {\r\n\r\n  public lastContainerSize: ISize;\r\n\r\n  parsedNode: any;\r\n\r\n  node: Node;\r\n  view: Node;\r\n  serviceContainer: ServiceContainer;\r\n  instanceServiceContainer: InstanceServiceContainer;\r\n  nodeReplaced = new TypedEvent<void>;\r\n\r\n  get window() {\r\n    if (this.isRootItem && this.node instanceof HTMLIFrameElement)\r\n      return this.node.contentDocument.defaultView;\r\n    return (this.node.ownerDocument.defaultView ?? window);\r\n  }\r\n\r\n  get document() {\r\n    if (this.isRootItem && this.node instanceof HTMLIFrameElement)\r\n      return this.node.contentDocument;\r\n    return this.node.ownerDocument;\r\n  }\r\n\r\n  get usableContainer() {\r\n    if (this.isRootItem && this.element instanceof (this.element.ownerDocument.defaultView ?? window).HTMLIFrameElement)\r\n      return this.element.contentWindow.document;\r\n    else if (this.isRootItem)\r\n      return (<HTMLElement>this.node).shadowRoot;\r\n    return this.element;\r\n  }\r\n\r\n  async clone() {\r\n    try {\r\n      const html = DomConverter.ConvertToString([this], false);\r\n      const parsed = await this.serviceContainer.htmlParserService.parse(html, this.serviceContainer, this.instanceServiceContainer, true);\r\n      return parsed[0];\r\n    }\r\n    catch (err) {\r\n      //TODO: clone service for design item, maybe refactor copy&paste to use this also...\r\n      console.warn(\"could not clone design item.\", this);\r\n    }\r\n    return null;\r\n  }\r\n\r\n  *allMatching(selectors: string) {\r\n    if (this.hasChildren) {\r\n      for (let d of this.children()) {\r\n        if (d.nodeType == NodeType.Element && d.element.matches(selectors))\r\n          yield d;\r\n        yield* d.allMatching(selectors);\r\n      }\r\n    }\r\n  }\r\n\r\n  public replaceNode(newNode: Node) {\r\n    DesignItem._designItemMap.delete(this.node);\r\n    DesignItem._designItemMap.set(newNode, this);\r\n    if (this.view == this.node)\r\n      this.view = newNode;\r\n    this.node = newNode;\r\n    this.nodeReplaced.emit();\r\n  }\r\n\r\n  public get nodeType(): NodeType {\r\n    if (this.node instanceof (this.node.ownerDocument.defaultView ?? window).Comment)\r\n      return NodeType.Comment;\r\n    if (this.node instanceof (this.node.ownerDocument.defaultView ?? window).Text)\r\n      return NodeType.TextNode;\r\n    return NodeType.Element;\r\n  }\r\n\r\n  private _attributes: Map<string, string>\r\n  public get hasAttributes() {\r\n    return this._attributes.size > 0;\r\n  }\r\n  public hasAttribute(name: string) {\r\n    return this._attributes.has(name);\r\n  }\r\n  public getAttribute(name: string): string {\r\n    return this._attributes.get(name);\r\n  }\r\n  public *attributes() {\r\n    for (let s of this._attributes) {\r\n      yield s;\r\n    }\r\n  }\r\n  _withoutUndoSetAttribute(name: string, value: string) {\r\n    try {\r\n      if (!this.isRootItem)\r\n        this.element.setAttribute(name, value);\r\n    } catch (e: any) {\r\n      if (e?.code !== 5)\r\n        console.warn(e)\r\n    }\r\n    this._attributes.set(name, value);\r\n    this.serviceContainer.designItemService.handleSpecialAttributes(name, this);\r\n  }\r\n  _withoutUndoRemoveAttribute(name: string) {\r\n    try {\r\n      if (!this.isRootItem)\r\n        this.element.removeAttribute(name);\r\n    } catch (e: any) {\r\n      if (e?.code !== 5)\r\n        console.warn(e)\r\n    }\r\n    this._attributes.delete(name);\r\n    this.serviceContainer.designItemService.handleSpecialAttributes(name, this);\r\n  }\r\n\r\n  private _styles: Map<string, string>\r\n  private _stylePriorities: Map<string, boolean>\r\n  public get hasStyles() {\r\n    return this._styles.size > 0;\r\n  }\r\n  public hasStyle(name: string) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    return this._styles.has(nm);\r\n  }\r\n  public getStyle(name: string) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    return this._styles.get(nm);\r\n  }\r\n  public isStyleImportant(name: string) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    return this._stylePriorities.get(nm) === true;\r\n  }\r\n  public *styles() {\r\n    for (let s of this._styles) {\r\n      yield s;\r\n    }\r\n  }\r\n  _withoutUndoSetStyle(name: string, value: string, important: boolean = false) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    this._styles.set(nm, value);\r\n    if (important)\r\n      this._stylePriorities.set(nm, true);\r\n    else\r\n      this._stylePriorities.delete(nm);\r\n  }\r\n  _withoutUndoRemoveStyle(name: string) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    this._styles.delete(nm);\r\n    this._stylePriorities.delete(nm);\r\n  }\r\n\r\n  /*\r\n  // this could maybe usefull to have such function, but naaa atm.\r\n  public refreshAttributesAndStylesFromElement() {\r\n    if (this.nodeType != NodeType.Element)\r\n      return;\r\n\r\n    const specialAttributeNames = [hideAtDesignTimeAttributeName, hideAtRunTimeAttributeName, lockAtDesignTimeAttributeName];\r\n    const specialAttributesToRefresh = new Set<string>();\r\n    for (const attributeName of specialAttributeNames) {\r\n      if (this._attributes.has(attributeName))\r\n        specialAttributesToRefresh.add(attributeName);\r\n    }\r\n\r\n    this._attributes.clear();\r\n    for (const attribute of this.element.attributes) {\r\n      if (attribute.name === 'style')\r\n        continue;\r\n      this._attributes.set(attribute.name, attribute.value);\r\n      if (specialAttributeNames.includes(attribute.name))\r\n        specialAttributesToRefresh.add(attribute.name);\r\n    }\r\n\r\n    this._styles.clear();\r\n    if (this.element instanceof (this.node.ownerDocument.defaultView ?? window).HTMLElement || this.element instanceof (this.node.ownerDocument.defaultView ?? window).SVGElement) {\r\n      const cssParser = new CssAttributeParser();\r\n      const styleText = this.element.getAttribute('style');\r\n      if (styleText) {\r\n        cssParser.parse(styleText);\r\n        for (const entry of cssParser.entries) {\r\n          this._styles.set(entry.name, entry.value);\r\n          if (entry.important)\r\n            this._stylePriorities.set(entry.name, true);\r\n        }\r\n      }\r\n    }\r\n    this._stylesCache = null;\r\n\r\n    for (const attributeName of specialAttributesToRefresh)\r\n      this.serviceContainer.designItemService.handleSpecialAttributes(attributeName, this);\r\n  }\r\n  */\r\n\r\n  private static _designItemMap = new WeakMap<Node, IDesignItem>();\r\n\r\n  public get element(): Element {\r\n    return <Element>this.view;\r\n  }\r\n\r\n  public get name() {\r\n    return (<Element>this.node).localName;\r\n  }\r\n\r\n  public get id(): string {\r\n    return this.element.id;\r\n  }\r\n  public set id(value: string) {\r\n    const oldValue = this.element.id;\r\n    this.element.id = value;\r\n    if (this.id)\r\n      this.setAttribute(\"id\", value);\r\n    else\r\n      this.removeAttribute(\"id\");\r\n    if (this.serviceContainer.referencesChangedService)\r\n      this.serviceContainer.referencesChangedService.notifyReferencesChanged([{ designItem: this, oldValue, type: 'idChanged' }]);\r\n  }\r\n\r\n  public get isRootItem(): boolean {\r\n    return this.instanceServiceContainer.designerCanvas.rootDesignItem === this;\r\n  }\r\n\r\n  *childrenRect(selectors: string) {\r\n    if (this.hasChildren) {\r\n      for (let d of this.children()) {\r\n        if (d.nodeType == NodeType.Element && d.element.matches(selectors))\r\n          yield d;\r\n        yield* d.allMatching(selectors);\r\n      }\r\n    }\r\n  }\r\n\r\n  _childArray: IDesignItem[] = [];\r\n  public get hasChildren() {\r\n    return this._childArray.length > 0;\r\n  }\r\n  public *children(recursive: boolean = false): IterableIterator<IDesignItem> {\r\n    for (const e of this._childArray) {\r\n      yield e;\r\n      if (recursive) {\r\n        for (const c of e.children(recursive)) {\r\n          yield c;\r\n        }\r\n      }\r\n    }\r\n  }\r\n  public get childCount(): number {\r\n    return this._childArray.length;\r\n  }\r\n  public get firstChild(): IDesignItem {\r\n    return this._childArray[0];\r\n  }\r\n  private _parent: IDesignItem;\r\n  public get parent(): IDesignItem {\r\n    return this._parent;\r\n  }\r\n\r\n  public indexOf(designItem: IDesignItem): number {\r\n    return this._childArray.indexOf(designItem);\r\n  }\r\n\r\n  public insertAdjacentElement(designItem: IDesignItem, where: InsertPosition) {\r\n    let action: InsertChildAction;\r\n    if (where == 'afterbegin') {\r\n      action = new InsertChildAction(designItem, this, 0);\r\n    } else if (where == 'beforeend') {\r\n      action = new InsertChildAction(designItem, this, this._childArray.length);\r\n    } else if (where == 'beforebegin') {\r\n      action = new InsertChildAction(designItem, this.parent, this.parent.indexOf(this));\r\n    } else if (where == 'afterend') {\r\n      action = new InsertChildAction(designItem, this.parent, this.parent.indexOf(this) + 1);\r\n    }\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n\r\n  public insertChild(designItem: IDesignItem, index?: number) {\r\n    const action = new InsertChildAction(designItem, this, index);\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n  public removeChild(designItem: IDesignItem) {\r\n    this.serviceContainer.deletionService.removeItems([designItem]);\r\n  }\r\n  public remove() {\r\n    this.serviceContainer.deletionService.removeItems([this]);\r\n  }\r\n  public clearChildren() {\r\n    for (let i = this._childArray.length - 1; i >= 0; i--) {\r\n      let di = this._childArray[i];\r\n      di.remove();\r\n    }\r\n  }\r\n\r\n  //abstract text content to own property. so only change via designer api will use it.\r\n  public get hasContent() {\r\n    return ((this.nodeType == NodeType.TextNode || this.nodeType == NodeType.Comment) && this.element.textContent != \"\") || (this._childArray.length === 0);\r\n  }\r\n  public get content(): string {\r\n    if (this.nodeType == NodeType.TextNode || this.nodeType == NodeType.Comment)\r\n      return this.node.textContent;\r\n    else\r\n      return this._childArray.map(x => x.content).join();\r\n  }\r\n  public set content(value: string) {\r\n    const grp = this.openGroup('set content');\r\n    this.clearChildren();\r\n    let t = document.createTextNode(value);\r\n    let di = DesignItem.GetOrCreateDesignItem(t, t, this.serviceContainer, this.instanceServiceContainer);\r\n    if (this.nodeType == NodeType.TextNode) {\r\n      const idx = this.parent.indexOf(this);\r\n      const parent = this.parent;\r\n      this.remove()\r\n      parent.insertChild(di, idx);\r\n    } else if (this.nodeType == NodeType.Comment) {\r\n      const action = new TextContentChangeAction(this, value, this.content);\r\n      this.instanceServiceContainer.undoService.execute(action);\r\n    } else\r\n      this.insertChild(di);\r\n    grp.commit();\r\n  }\r\n\r\n  public get innerHTML(): string {\r\n    const innerHTML = DomConverter.ConvertToString([...this.children()], false);\r\n    return innerHTML;\r\n  }\r\n  public set innerHTML(value: string) {\r\n    if (this.nodeType != NodeType.TextNode) {\r\n      const grp = this.openGroup('set innerHTML');\r\n      this.clearChildren();\r\n      const range = document.createRange();\r\n      range.selectNode(document.body);\r\n      const fragment = range.createContextualFragment(value);\r\n      for (const n of [...fragment.childNodes]) {\r\n        let di = DesignItem.createDesignItemFromInstance(n, this.serviceContainer, this.instanceServiceContainer)\r\n        this.insertChild(di);\r\n      }\r\n      grp.commit();\r\n    }\r\n  }\r\n\r\n  public get isEmptyTextNode(): boolean {\r\n    return this.nodeType === NodeType.TextNode && this.content?.trim() == '';\r\n  }\r\n\r\n  public get hideAtDesignTime() {\r\n    return this.hasAttribute(hideAtDesignTimeAttributeName);\r\n  }\r\n  public set hideAtDesignTime(value: boolean) {\r\n    if (value)\r\n      this.setAttribute(hideAtDesignTimeAttributeName, \"\");\r\n    else\r\n      this.removeAttribute(hideAtDesignTimeAttributeName);\r\n  }\r\n\r\n  public get hideAtRunTime() {\r\n    return this.hasAttribute(hideAtRunTimeAttributeName);\r\n  }\r\n  public set hideAtRunTime(value: boolean) {\r\n    if (value)\r\n      this.setAttribute(hideAtRunTimeAttributeName, \"\");\r\n    else\r\n      this.removeAttribute(hideAtRunTimeAttributeName);\r\n  }\r\n\r\n  public get lockAtDesignTime() {\r\n    return this.hasAttribute(lockAtDesignTimeAttributeName);\r\n  }\r\n  public set lockAtDesignTime(value: boolean) {\r\n    if (value)\r\n      this.setAttribute(lockAtDesignTimeAttributeName, \"\")\r\n    else\r\n      this.removeAttribute(lockAtDesignTimeAttributeName);\r\n  }\r\n\r\n  public static createDesignItemFromInstance(node: Node, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): DesignItem {\r\n    node = DesignItem.updateRenderedNode(serviceContainer, node);\r\n    let designItem = <DesignItem>serviceContainer.designItemService.createDesignItem(node, node, serviceContainer, instanceServiceContainer);\r\n\r\n    if (node instanceof (node.ownerDocument.defaultView ?? window).HTMLTemplateElement && node.getAttribute('shadowrootmode') == 'open') {\r\n      try {\r\n        const shadow = (<HTMLElement>node.parentNode).attachShadow({ mode: 'open' });\r\n        const content = node.content.cloneNode(true);\r\n        shadow.appendChild(content);\r\n      } catch (err) {\r\n        console.error(\"error attaching shadowdom\", err)\r\n      }\r\n    }\r\n\r\n    if (designItem.nodeType == NodeType.Element) {\r\n      for (let a of designItem.element.attributes) {\r\n        if (a.name !== 'style') {\r\n          designItem._attributes.set(a.name, a.value);\r\n        }\r\n      }\r\n\r\n      if (node instanceof (node.ownerDocument.defaultView ?? window).HTMLElement || node instanceof (node.ownerDocument.defaultView ?? window).SVGElement) {\r\n        const cssParser = new CssAttributeParser();\r\n        const st = node.getAttribute(\"style\");\r\n        if (st) {\r\n          cssParser.parse(st);\r\n          for (let e of cssParser.entries) {\r\n            designItem._styles.set(<string>e.name, e.value);\r\n            if (e.important)\r\n              designItem._stylePriorities.set(<string>e.name, true);\r\n          }\r\n        }\r\n        serviceContainer.designItemService.handleSpecialAttributes(lockAtDesignTimeAttributeName, designItem);\r\n      }\r\n\r\n      (<HTMLElement>node).draggable = false; //even if it should be true, for better designer exp.\r\n    }\r\n\r\n    designItem._childArray = designItem._internalUpdateChildrenFromNodesChildren();\r\n    for (let c of designItem._childArray) {\r\n      (<DesignItem>c)._parent = designItem;\r\n    }\r\n\r\n    designItem.refreshRenderedDesignItem();\r\n\r\n    return designItem;\r\n  }\r\n\r\n  static updateRenderedNode(serviceContainer: ServiceContainer, node: Node) {\r\n    let renderedNode = node;\r\n    for (const service of serviceContainer.renderedDesignItemServices ?? []) {\r\n      const nextNode = service.updateRenderedNode(renderedNode) ?? renderedNode;\r\n      if (nextNode !== renderedNode && renderedNode.parentNode)\r\n        renderedNode.parentNode.replaceChild(nextNode, renderedNode);\r\n      renderedNode = nextNode;\r\n    }\r\n    return renderedNode;\r\n  }\r\n\r\n  querySelectorAll<T extends HTMLElement>(selectors: string): NodeListOf<T> {\r\n    return this.usableContainer.querySelectorAll(selectors);\r\n  }\r\n\r\n  removeDesignerAttributesAndStylesFromChildren() {\r\n    const els = this.querySelectorAll('*');\r\n    for (let e of els) {\r\n      const di = DesignItem.GetDesignItem(e);\r\n      if (!di.hasAttribute(\"draggable\"))\r\n        e.removeAttribute(\"draggable\");\r\n      if (!di.hasStyle(\"pointer-events\"))\r\n        e.style.pointerEvents = '';\r\n    }\r\n  }\r\n\r\n  updateChildrenFromNodesChildren() {\r\n    this._childArray = this._internalUpdateChildrenFromNodesChildren();\r\n    for (let c of this._childArray) {\r\n      (<DesignItem>c)._parent = this;\r\n    }\r\n  }\r\n\r\n  _internalUpdateChildrenFromNodesChildren() {\r\n    const newChilds = [];\r\n    if (this.nodeType == NodeType.Element) {\r\n      if (this.element instanceof (this.node.ownerDocument.defaultView ?? window).HTMLTemplateElement) {\r\n        for (const c of this.element.content.childNodes) {\r\n          const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);\r\n          newChilds.push(di);\r\n        }\r\n      } else if (this.isRootItem && this.element instanceof (this.node.ownerDocument.defaultView ?? window).HTMLIFrameElement) {\r\n        for (const c of this.element.contentWindow.document.childNodes) {\r\n          const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);\r\n          newChilds.push(di);\r\n        }\r\n      } else {\r\n        for (const c of this.element.childNodes) {\r\n          const di = DesignItem.createDesignItemFromInstance(c, this.serviceContainer, this.instanceServiceContainer);\r\n          newChilds.push(di);\r\n        }\r\n      }\r\n    }\r\n    return newChilds;\r\n  }\r\n\r\n  _backupWhenEditContent;\r\n  _inEditContent = false;\r\n  editContent() {\r\n    this._inEditContent = true;\r\n    this._backupWhenEditContent = [...this.element.childNodes];\r\n    const nn = this.element.innerHTML\r\n    this.element.innerHTML = '';\r\n    this.element.innerHTML = nn;\r\n    this.element.setAttribute('contenteditable', '');\r\n  }\r\n\r\n  editContentFinish() {\r\n    if (this._inEditContent) {\r\n      this._inEditContent = false;\r\n      this.element.removeAttribute('contenteditable');\r\n      this.element.innerHTML = '';\r\n      for (let n of this._backupWhenEditContent) {\r\n        this.element.appendChild(n);\r\n      }\r\n      this._backupWhenEditContent = null;\r\n    }\r\n  }\r\n\r\n  public constructor(node: Node, parsedNode: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer) {\r\n    this.node = node;\r\n    this.view = node;\r\n    this.parsedNode = parsedNode;\r\n    this.serviceContainer = serviceContainer;\r\n    this.instanceServiceContainer = instanceServiceContainer;\r\n\r\n    this._attributes = new Map();\r\n    this._styles = new Map();\r\n    this._stylePriorities = new Map();\r\n\r\n    DesignItem._designItemMap.set(node, this);\r\n  }\r\n\r\n  public setView(node: Element) {\r\n    this.view = node;\r\n    DesignItem._designItemMap.set(node, this);\r\n    this.refreshRenderedDesignItem();\r\n  }\r\n\r\n  public refreshRenderedDesignItem() {\r\n    for (const service of this.serviceContainer.renderedDesignItemServices ?? [])\r\n      service.updateRenderedDesignItem(this);\r\n  }\r\n\r\n  public openGroup(title: string): ChangeGroup {\r\n    return this.instanceServiceContainer.undoService.openGroup(title);\r\n  }\r\n\r\n  public getOrCreateDesignItem(node: Node) {\r\n    return DesignItem.GetOrCreateDesignItem(node, node, this.serviceContainer, this.instanceServiceContainer);\r\n  }\r\n\r\n  static GetOrCreateDesignItem(node: Node, parsedNode: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): IDesignItem {\r\n    if (!node)\r\n      return null;\r\n    let designItem: IDesignItem = DesignItem._designItemMap.get(node);\r\n    if (!designItem) {\r\n      let dis = serviceContainer.designItemService;\r\n      designItem = dis.createDesignItem(node, parsedNode, serviceContainer, instanceServiceContainer);\r\n    }\r\n    return designItem;\r\n  }\r\n\r\n  static GetDesignItem(node: Node): IDesignItem {\r\n    if (!node)\r\n      return null;\r\n    let designItem: IDesignItem = DesignItem._designItemMap.get(node);\r\n    return designItem;\r\n  }\r\n\r\n  public setStyle(name: string, value?: string | null, important?: boolean) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    if (this.isRootItem) {\r\n      throw 'not allowed to set style on root item or use async setStyle';\r\n    } else {\r\n      const action = new CssStyleChangeAction(this, nm, value, this._styles.get(nm), important, this.isStyleImportant(nm));\r\n      this.instanceServiceContainer.undoService.execute(action);\r\n    }\r\n  }\r\n  public async setStyleAsync(name: string, value?: string | null, important?: boolean): Promise<void> {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    //TODO: remove this special case (I think), should be in CSSSytleChangeAction.\r\n    //Maybe we should be able to set a setscope (sheet, local, pseudo selctor, ...)\r\n    if (this.isRootItem) {\r\n      if (!this.instanceServiceContainer.stylesheetService)\r\n        throw 'not allowed to set style on root item';\r\n      else {\r\n        let decls = this.instanceServiceContainer.stylesheetService.getDeclarationsSortedBySpecificity(this, name);\r\n        if (decls !== null && decls.length > 0) {\r\n          this.instanceServiceContainer.stylesheetService.updateDeclarationValue(decls[0], value, important);\r\n        } else {\r\n          let rules = this.instanceServiceContainer.stylesheetService.getRules(':host').filter(x => !x.stylesheet?.readOnly);\r\n          if (decls === null || rules.length === 0) {\r\n            const cg = this.openGroup('add rule and set style: ' + name);\r\n            const sheets = this.instanceServiceContainer.stylesheetService.getStylesheets();\r\n            const rule = await this.instanceServiceContainer.stylesheetService.addRule(sheets[0], ':host')\r\n            this.instanceServiceContainer.stylesheetService.insertDeclarationIntoRule(rule, name, value, important);\r\n            cg.commit();\r\n          } else {\r\n            this.instanceServiceContainer.stylesheetService.insertDeclarationIntoRule(rules[0], name, value, important);\r\n          }\r\n        }\r\n      }\r\n    } else {\r\n      const action = new CssStyleChangeAction(this, nm, value, this._styles.get(nm), important, this.isStyleImportant(nm));\r\n      this.instanceServiceContainer.undoService.execute(action);\r\n    }\r\n  }\r\n\r\n  public removeStyle(name: string) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n    const action = new CssStyleChangeAction(this, nm, '', this._styles.get(nm), false, this.isStyleImportant(nm));\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n  public updateStyleInSheetOrLocal(name: string, value?: string | null, important?: boolean, forceSet?: boolean) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n\r\n    let declarations = this.instanceServiceContainer.stylesheetService?.getDeclarationsSortedBySpecificity(this, nm).filter(x => !x.stylesheet?.readOnly);\r\n\r\n    if (this.hasStyle(name) || this.instanceServiceContainer.designContext.extensionOptions[enableStylesheetService] === false || !declarations?.length) {\r\n      // Set style locally\r\n      if (this.getStyle(nm) != value || forceSet) {\r\n        this.setStyle(nm, value, important);\r\n      } else if (value == null) {\r\n        this.removeStyle(nm);\r\n      }\r\n    } else {\r\n      this.instanceServiceContainer.stylesheetService.updateDeclarationValue(declarations[0], value, important);\r\n    }\r\n  }\r\n  public async updateStyleInSheetOrLocalAsync(name: string, value?: string | null, important?: boolean, forceSet?: boolean): Promise<void> {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n\r\n    let declarations = this.instanceServiceContainer.stylesheetService?.getDeclarationsSortedBySpecificity(this, nm).filter(x => !x.stylesheet?.readOnly);\r\n\r\n    if (this.hasStyle(name) || this.instanceServiceContainer.designContext.extensionOptions[enableStylesheetService] === false || !declarations?.length) {\r\n      // Set style locally\r\n      if (this.getStyle(nm) != value || forceSet) {\r\n        await this.setStyleAsync(nm, value, important);\r\n      } else if (value == null) {\r\n        this.removeStyle(nm);\r\n      }\r\n    } else {\r\n      this.instanceServiceContainer.stylesheetService.updateDeclarationValue(declarations[0], value, important);\r\n    }\r\n  }\r\n\r\n  public getStyleFromSheetOrLocal(name: string, fallback: string = null) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n\r\n    if (this.hasStyle(name))\r\n      // Get style locally\r\n      return this.getStyle(nm);\r\n\r\n    let decls = this.instanceServiceContainer.stylesheetService?.getDeclarationsSortedBySpecificity(this, nm);\r\n    if (decls && decls.length > 0)\r\n      return decls[0].value;\r\n\r\n    return null;\r\n  }\r\n\r\n  getStyleFromSheetOrLocalOrComputed(name: string, fallback: string = null) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n\r\n    let value = this.getStyleFromSheetOrLocal(nm);\r\n    if (!value) {\r\n      value = getComputedStyle(this.element).getPropertyValue(nm)\r\n    }\r\n    return value ?? fallback;\r\n  }\r\n\r\n  getComputedStyleProperty(name: string, fallback: string = null) {\r\n    let nm = name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(name);\r\n\r\n    let value = this.getStyleFromSheetOrLocal(nm);\r\n    if (!value) {\r\n      value = getComputedStyle(this.element).getPropertyValue(nm)\r\n    }\r\n    return value ?? fallback;\r\n  }\r\n\r\n  getComputedStyle() {\r\n    if (this.nodeType == NodeType.Element)\r\n      return this.window.getComputedStyle(this.element);\r\n    return null;\r\n  }\r\n\r\n  _stylesCache: IStyleRule[] = null;\r\n  _cacheClearTimer: NodeJS.Timeout;\r\n\r\n  public getAllStyles(): IStyleRule[] {\r\n    let styles = this._stylesCache;\r\n    if (styles)\r\n      return styles;\r\n    if (this.nodeType != NodeType.Element)\r\n      return [];\r\n\r\n    const localStyles = [...this._styles.entries()].map(x => ({ name: x[0], value: x[1], important: this.isStyleImportant(x[0]), parent: null }));\r\n    if (this.instanceServiceContainer.stylesheetService) {\r\n      try {\r\n        const rules = this.instanceServiceContainer.stylesheetService?.getAppliedRules(this);\r\n        if (rules) {\r\n          return [{ selector: null, declarations: localStyles, specificity: null, stylesheet: null }, ...rules];\r\n        }\r\n      }\r\n      catch (err) {\r\n        console.warn('getAppliedRules', err);\r\n      }\r\n    }\r\n    styles = [{ selector: null, declarations: localStyles, specificity: null, stylesheet: null }];\r\n    this._stylesCache = styles;\r\n    clearTimeout(this._cacheClearTimer);\r\n    this._cacheClearTimer = setTimeout(() => this._stylesCache = null, 30);\r\n    return styles;\r\n  }\r\n\r\n  public setAttribute(name: string, value?: string | null) {\r\n    const action = new AttributeChangeAction(this, name, value, this._attributes.get(name));\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n  public removeAttribute(name: string) {\r\n    const action = new AttributeChangeAction(this, name, null, this._attributes.get(name));\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n\r\n  public setPropertyAndAttribute(name: string, value?: string | null) {\r\n    const attributeName = PropertiesHelper.camelToDashCase(name);\r\n    const propertyName = PropertiesHelper.dashToCamelCase(name);\r\n    if (this.isRootItem)\r\n      throw 'not allowed to set attribute on root item';\r\n    const action = new AttributeAndPropertyChangeAction(this, attributeName, propertyName, value, this.element[propertyName]);\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n  public removePropertyAndAttribute(name: string) {\r\n    const attributeName = PropertiesHelper.camelToDashCase(name);\r\n    const propertyName = PropertiesHelper.dashToCamelCase(name);\r\n    const action = new AttributeAndPropertyChangeAction(this, attributeName, propertyName, null, this.element[propertyName]);\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n\r\n  public setProperty(name: string, value?: any) {   //the prop change action should use the prop service. We need th setPropAndattribute. So undo works!\r\n    if (this.isRootItem)\r\n      throw 'not allowed to set attribute on root item';\r\n    const oldValue = deepValue(this.node, name);\r\n    const action = new PropertyChangeAction(this, name, value, oldValue);\r\n    this.instanceServiceContainer.undoService.execute(action);\r\n  }\r\n\r\n  // Internal implementations wich don't use undo/redo\r\n  public _insertChildInternal(designItem: DesignItem, index?: number) {\r\n    this._insertChildsInternal([designItem], index);\r\n  }\r\n  public _insertChildsInternal(designItems: DesignItem[], index?: number) {\r\n    const frag = this.document.createDocumentFragment();\r\n    let beforeDs: IDesignItem = null;\r\n    for (let designItem of designItems) {\r\n      if (designItem.parent && this.instanceServiceContainer.selectionService.primarySelection == designItem) {\r\n        designItem.instanceServiceContainer.designerCanvas.extensionManager.removeExtension(designItem.parent, ExtensionType.PrimarySelectionContainer);\r\n        designItem.instanceServiceContainer.designerCanvas.extensionManager.removeExtension(designItem.parent, ExtensionType.PrimarySelectionContainerAndCanBeEntered);\r\n      }\r\n      if (designItem.parent) {\r\n        designItem.parent._removeChildInternal(designItem);\r\n      }\r\n\r\n      frag.appendChild(designItem.view);\r\n      (<DesignItem>designItem)._parent = this;\r\n\r\n      if (index == null || this._childArray.length == 0 || index >= this._childArray.length) {\r\n        this._childArray.push(designItem);\r\n      } else {\r\n        beforeDs = this._childArray[index];\r\n        this._childArray.splice(index, 0, designItem);\r\n        index++;\r\n      }\r\n    }\r\n    if (beforeDs == null) {\r\n      if (this.isRootItem) {\r\n        if (this.usableContainer?.children[0] instanceof this.window.HTMLHtmlElement)\r\n          this.usableContainer.children[0].remove();\r\n        this.usableContainer.appendChild(frag);\r\n      } else if (this.view instanceof (this.node.ownerDocument.defaultView ?? window).HTMLTemplateElement) {\r\n        this.view.content.appendChild(frag);\r\n      } else\r\n        this.view.appendChild(frag);\r\n    } else {\r\n      if (this.isRootItem) {\r\n        if (this.usableContainer?.children[0] instanceof this.window.HTMLHtmlElement)\r\n          this.usableContainer.children[0].remove();\r\n        this.usableContainer.insertBefore(frag, beforeDs.element);\r\n      } else if (this.view instanceof (this.node.ownerDocument.defaultView ?? window).HTMLTemplateElement) {\r\n        this.view.content.insertBefore(frag, beforeDs.element)\r\n      } else\r\n        this.view.insertBefore(frag, beforeDs.element)\r\n    }\r\n\r\n    //TODO: is this still needed???\r\n    /*\r\n    if (this.instanceServiceContainer.selectionService.primarySelection == designItem) {\r\n      designItem.instanceServiceContainer.designerCanvas.extensionManager.applyExtension(designItem.parent, ExtensionType.PrimarySelectionContainer);\r\n      if (designItem.getPlacementService().isEnterableContainer(this))\r\n        designItem.instanceServiceContainer.designerCanvas.extensionManager.applyExtension(designItem.parent, ExtensionType.PrimarySelectionContainerAndCanBeEntered);\r\n    }\r\n    */\r\n\r\n    this.refreshRenderedDesignItem();\r\n  }\r\n  public _removeChildInternal(designItem: IDesignItem) {\r\n    if (designItem.parent && this.instanceServiceContainer.selectionService.primarySelection == designItem) {\r\n      designItem.instanceServiceContainer.designerCanvas.extensionManager.removeExtension(designItem.parent, ExtensionType.PrimarySelectionContainer);\r\n      designItem.instanceServiceContainer.designerCanvas.extensionManager.removeExtension(designItem.parent, ExtensionType.PrimarySelectionAndCanBeEntered);\r\n    }\r\n\r\n    designItem.instanceServiceContainer.designerCanvas.extensionManager.removeExtensions([designItem], true);\r\n\r\n    const index = this._childArray.indexOf(designItem);\r\n    if (index > -1) {\r\n      this._childArray.splice(index, 1);\r\n      designItem.element.remove();\r\n      (<DesignItem>designItem)._parent = null;\r\n    }\r\n\r\n    this.refreshRenderedDesignItem();\r\n  }\r\n\r\n  getPlacementService(style?: CSSStyleDeclaration): IPlacementService {\r\n    if (this.nodeType != NodeType.Element)\r\n      return null;\r\n    style ??= getComputedStyle(this.element);\r\n    return this.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this, style));\r\n  }\r\n\r\n  static createDesignItemFromImageBlob(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, data: Blob): Promise<IDesignItem> {\r\n    return new Promise<IDesignItem>(resolve => {\r\n      let reader = new FileReader();\r\n      reader.onloadend = () => {\r\n        const img = document.createElement('img');\r\n        img.src = <string>reader.result;\r\n        const di = DesignItem.createDesignItemFromInstance(img, serviceContainer, instanceServiceContainer);\r\n        return resolve(di);\r\n      }\r\n      reader.readAsDataURL(data);\r\n    })\r\n  }\r\n\r\n  get hasForcedCss() {\r\n    return this.cssForceHover || this.cssForceActive || this.cssForceVisited || this.cssForceFocus || this.cssForceFocusWithin || this.cssForceFocusVisible;\r\n  }\r\n\r\n  get cssForceHover() {\r\n    return this.element.hasAttribute(forceHoverAttributeName);\r\n  }\r\n  set cssForceHover(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceHoverAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceHoverAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceHoverAttributeName, designItems: [this] }]);\r\n  }\r\n\r\n  get cssForceActive() {\r\n    return this.element.hasAttribute(forceActiveAttributeName);\r\n  }\r\n  set cssForceActive(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceActiveAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceActiveAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceActiveAttributeName, designItems: [this] }]);\r\n  }\r\n\r\n  get cssForceVisited() {\r\n    return this.element.hasAttribute(forceVisitedAttributeName);\r\n  }\r\n  set cssForceVisited(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceVisitedAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceVisitedAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceVisitedAttributeName, designItems: [this] }]);\r\n  }\r\n\r\n  get cssForceFocus() {\r\n    return this.element.hasAttribute(forceFocusAttributeName);\r\n  }\r\n  set cssForceFocus(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceFocusAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceFocusAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceFocusAttributeName, designItems: [this] }]);\r\n  }\r\n\r\n  get cssForceFocusWithin() {\r\n    return this.element.hasAttribute(forceFocusWithinAttributeName);\r\n  }\r\n  set cssForceFocusWithin(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceFocusWithinAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceFocusWithinAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceFocusWithinAttributeName, designItems: [this] }]);\r\n  }\r\n\r\n  get cssForceFocusVisible() {\r\n    return this.element.hasAttribute(forceFocusVisibleAttributeName);\r\n  }\r\n  set cssForceFocusVisible(value: boolean) {\r\n    if (value)\r\n      this.element.setAttribute(forceFocusVisibleAttributeName, '');\r\n    else\r\n      this.element.removeAttribute(forceFocusVisibleAttributeName);\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'changed', type: 'attribute', name: forceFocusVisibleAttributeName, designItems: [this] }]);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/IBinding.ts",
    "content": "import { IBindingService } from '../services/bindingsService/IBindingService.js';\r\nimport { BindingMode } from './BindingMode.js';\r\nimport { BindingTarget } from './BindingTarget.js';\r\n\r\nexport interface IBinding {\r\n  targetName?: string; //Name of Attribute, CSS-Property, Event, ...\r\n  target?: BindingTarget;\r\n  rawName?: string; //raw attribute name (if it's an attribute)\r\n  rawValue?: string; //raw attribute value (or element html)\r\n\r\n  type?: string //here a name wich the bindings Service recognizes....\r\n\r\n  expression?: string;  //the bindings expression\r\n  expressionTwoWay?: string;  //a expression wich is used for write back\r\n\r\n  bindableObjectNames?: string[];      //TODO: deprecate and remove\r\n  bindableObjects?: IBindableComplexName[]; //if a name is not enough, use this list\r\n\r\n  converters?: any;\r\n\r\n  mode?: BindingMode;\r\n\r\n  invert?: boolean;\r\n  changedEvents?: string[];\r\n  nullSafe?: boolean;\r\n  service: IBindingService;\r\n}\r\n\r\nexport interface IBindableComplexName {   //e.g.   aa:$uservalue.0.name\r\n  name?: string;                  //uservalue.0.name\r\n  alias?: string;                 //aa\r\n  modificator?: string;           //$\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/IDesignItem.ts",
    "content": "import { ServiceContainer } from '../services/ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../services/InstanceServiceContainer.js';\r\nimport { ChangeGroup } from '../services/undoService/ChangeGroup.js';\r\nimport { NodeType } from './NodeType.js';\r\nimport { ISize } from \"../../interfaces/ISize.js\";\r\nimport { IStyleRule } from '../services/stylesheetService/IStylesheetService.js';\r\nimport { IPlacementService } from '../services/placementService/IPlacementService.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport interface IDesignItem {\r\n\r\n  lastContainerSize: ISize;\r\n\r\n  readonly window: Window & typeof globalThis;\r\n  readonly document: Document;\n  readonly usableContainer: ShadowRoot | Element | Document;\n  updateChildrenFromNodesChildren();\n  refreshRenderedDesignItem();\n\n  setView(node: Element);\n\r\n  replaceNode(newNode: Node);\r\n  nodeReplaced: TypedEvent<void>;\r\n  clone(): Promise<IDesignItem>\r\n\r\n  readonly nodeType: NodeType;\r\n\r\n  readonly name: string;\r\n  id: string;\r\n  readonly isRootItem: boolean;\r\n\r\n  readonly hasAttributes: boolean;\r\n  readonly hasStyles: boolean;\r\n\r\n  readonly hasChildren: boolean;\r\n  children(recursive?: boolean): IterableIterator<IDesignItem>\r\n  allMatching(selectors: string): IterableIterator<IDesignItem>\r\n  readonly childCount: number;\r\n  readonly firstChild: IDesignItem;\r\n  readonly parent: IDesignItem;\r\n\r\n  _insertChildsInternal(designItems: IDesignItem[], index?: number);\r\n  _insertChildInternal(designItem: IDesignItem, index?: number);\r\n  _removeChildInternal(designItem: IDesignItem);\r\n  _withoutUndoSetStyle(name: string, value: string, important?: boolean);\n  _withoutUndoRemoveStyle(name: string);\n  _withoutUndoSetAttribute(name: string, value: string);\r\n  _withoutUndoRemoveAttribute(name: string);\r\n\r\n  indexOf(designItem: IDesignItem): number;\r\n  insertAdjacentElement(designItem: IDesignItem, where: InsertPosition);\r\n  insertChild(designItem: IDesignItem, index?: number);\r\n  removeChild(designItem: IDesignItem);\r\n  remove();\r\n  clearChildren();\r\n\r\n  removeDesignerAttributesAndStylesFromChildren();\r\n\r\n  editContent();\r\n  editContentFinish();\r\n\r\n  readonly hasContent: boolean;\r\n  content: string;\r\n  innerHTML?: string;\r\n  readonly isEmptyTextNode: boolean;\r\n\r\n  /** Could be a special node if another parser is used */\r\n  readonly parsedNode: any;\r\n  readonly node: Node;\r\n  readonly element: Element;\r\n\r\n  serviceContainer: ServiceContainer;\r\n  instanceServiceContainer: InstanceServiceContainer;\r\n\r\n  getOrCreateDesignItem(node: Node);\r\n\r\n  openGroup(title: string): ChangeGroup\r\n\r\n  styles(): Iterable<[name: string, value: string]>;\n  getStyle(name: string): string\n  isStyleImportant(name: string): boolean\n  hasStyle(name: string): boolean\n  setStyle(name: string, value?: string | null, important?: boolean);\r\n  setStyleAsync(name: string, value?: string | null, important?: boolean): Promise<void>;\r\n  removeStyle(name: string);\r\n  updateStyleInSheetOrLocal(name: string, value?: string | null, important?: boolean, forceSet?: boolean);\r\n  updateStyleInSheetOrLocalAsync(name: string, value?: string | null, important?: boolean, forceSet?: boolean): Promise<void>;\r\n  getStyleFromSheetOrLocal(name: string, fallback?: string);\r\n  getStyleFromSheetOrLocalOrComputed(name: string, fallback?: string)\r\n  getAllStyles(): IStyleRule[];\r\n\r\n  readonly hasForcedCss: boolean;\r\n  cssForceHover: boolean;\r\n  cssForceActive: boolean;\r\n  cssForceVisited: boolean;\r\n  cssForceFocus: boolean;\r\n  cssForceFocusWithin: boolean;\r\n  cssForceFocusVisible: boolean;\r\n\r\n  attributes(): Iterable<[name: string, value: string]>\r\n  getAttribute(name: string): string\r\n  hasAttribute(name: string): boolean\r\n  setAttribute(name: string, value?: string | null);\r\n  removeAttribute(name: string);\r\n\r\n  setProperty(name: string, value?: any);\r\n  setPropertyAndAttribute(name: string, value?: string | null);\r\n  removePropertyAndAttribute(name: string);\r\n\r\n  hideAtDesignTime: boolean;\r\n  hideAtRunTime: boolean;\r\n  lockAtDesignTime: boolean;\r\n\r\n  getPlacementService(style?: CSSStyleDeclaration): IPlacementService;\r\n\r\n  getComputedStyleProperty(name: string, fallback: string): string;\r\n  getComputedStyle(): CSSStyleDeclaration;\r\n  querySelectorAll<T extends HTMLElement>(selectors: string): NodeListOf<T>;\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/NodeType.ts",
    "content": "export enum NodeType {\r\n  Element = 1,\r\n  Attribute = 2,\r\n  TextNode = 3,\r\n  Comment = 8,\r\n  Document = 9,\r\n  DocumentFragment = 11\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/item/info.txt",
    "content": "Todo -> unfiy Properties & Attributes.\r\n\r\nDesignItem should only store Attributes, not Properties.\r\nMaybe setAttribute should set Attribute directly, setProperty should use PropertiesService??\r\nor do we always use properties PropertiesService?\r\n\r\nAttributes List in Properties shows all set attributes and you could also add one,\r\nbut they show no binding etc information cause they are only the real attributes\r\n\r\nhow should we serialize bindings?? if they are binding objects, like in tagbinding?\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/BaseServiceContainer.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IService } from './IService.js';\r\n\r\n\r\nexport class BaseServiceContainer<NameMap> {\r\n  protected _services: Map<string, IService[]> = new Map();\r\n  public servicesChanged = new TypedEvent<{ serviceName: keyof NameMap }>();\r\n\r\n  getLastService<K extends keyof NameMap>(service: K): NameMap[K] {\r\n    let list: [] = <any>this._services.get(<string>service);\r\n    if (list && list.length)\r\n      return list[list.length - 1];\r\n    return null;\r\n  }\r\n\r\n  getServices<K extends keyof NameMap>(service: K): NameMap[K][] {\r\n    return <any>this._services.get(<string>service);\r\n  }\r\n\r\n  register<K extends keyof NameMap>(name: K, service: NameMap[K]) {\r\n    if (!this._services.has(<string>name))\r\n      this._services.set(<string>name, []);\r\n    this._services.get(<string>name).push(service);\r\n    this.servicesChanged.emit({ serviceName: <keyof NameMap>name });\r\n  }\r\n\r\n  registerLast<K extends keyof NameMap>(name: K, service: NameMap[K]) {\r\n    if (!this._services.has(<string>name))\r\n      this._services.set(<string>name, []);\r\n    this._services.get(<string>name).unshift(service);\r\n    this.servicesChanged.emit({ serviceName: <keyof NameMap>name });\r\n  }\r\n\r\n  registerMultiple<K extends keyof NameMap>(names: K[], service: NameMap[K]) {\r\n    for (const name of names) {\r\n      if (!this._services.has(<string>name))\r\n        this._services.set(<string>name, []);\r\n      this._services.get(<string>name).push(service);\r\n      this.servicesChanged.emit({ serviceName: <keyof NameMap>name });\r\n    }\r\n  }\r\n\r\n  forSomeServicesTillResult<K extends keyof NameMap, Y>(service: K, callback: (service: NameMap[K]) => Y): Y {\r\n    let services = this.getServices<K>(<any>service);\r\n    if (services == null) {\r\n      return null;\r\n    }\r\n    for (let index = services.length - 1; index >= 0; index--) {\r\n      const currentService = services[index];\r\n      let result = callback(currentService);\r\n      if (result != null)\r\n        return result;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  getLastServiceWhere<K extends keyof NameMap, Y>(service: K, callback: (service: NameMap[K]) => Y): NameMap[K] {\r\n    let services = this.getServices<K>(<any>service);\r\n    if (services == null) {\r\n      return null;\r\n    }\r\n    for (let index = services.length - 1; index >= 0; index--) {\r\n      const currentService = services[index];\r\n      let result = callback(currentService);\r\n      if (result)\r\n        return currentService;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  getLastServiceResult<K extends keyof NameMap, Y>(service: K, callback: (service: NameMap[K]) => Y): Y {\r\n    let services = this.getServices<K>(<any>service);\r\n    if (services == null) {\r\n      return null;\r\n    }\r\n    for (let index = services.length - 1; index >= 0; index--) {\r\n      const currentService = services[index];\r\n      let result = callback(currentService);\r\n      if (result)\r\n        return result;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  async getLastServiceWhereAsync<K extends keyof NameMap, Y>(service: K, callback: (service: NameMap[K]) => Promise<Y>): Promise<NameMap[K]> {\r\n    let services = this.getServices<K>(<any>service);\r\n    if (services == null) {\r\n      return null;\r\n    }\r\n    for (let index = services.length - 1; index >= 0; index--) {\r\n      const currentService = services[index];\r\n      let result = await callback(currentService);\r\n      if (result)\r\n        return currentService;\r\n    }\r\n    return null;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/DefaultServiceBootstrap.ts",
    "content": "import { ServiceContainer } from './ServiceContainer.js';\r\nimport { PolymerPropertiesService } from './propertiesService/services/PolymerPropertiesService.js';\r\nimport { LitElementPropertiesService } from './propertiesService/services/LitElementPropertiesService.js';\r\nimport { NativeElementsPropertiesService } from './propertiesService/services/NativeElementsPropertiesService.js';\r\nimport { SVGElementsPropertiesService } from './propertiesService/services/SVGElementsPropertiesService.js';\r\nimport { DefaultInstanceService } from './instanceService/DefaultInstanceService.js';\r\nimport { DefaultPropertyEditorTypesService } from './propertiesService/DefaultPropertyEditorTypesService.js';\r\nimport { BaseCustomWebComponentPropertiesService } from './propertiesService/services/BaseCustomWebComponentPropertiesService.js';\r\nimport { DefaultPlacementService } from './placementService/DefaultPlacementService.js';\r\nimport { DefaultHtmlParserService } from './htmlParserService/DefaultHtmlParserService.js';\r\nimport { Lit2PropertiesService } from './propertiesService/services/Lit2PropertiesService.js';\r\nimport { ExtensionType } from '../widgets/designerView/extensions/ExtensionType.js';\r\nimport { ElementDragTitleExtensionProvider } from '../widgets/designerView/extensions/ElementDragTitleExtensionProvider.js';\r\nimport { TransformOriginExtensionProvider } from '../widgets/designerView/extensions/transforms/TransformOriginExtensionProvider.js';\r\nimport { MarginExtensionProvider } from '../widgets/designerView/extensions/MarginExtensionProvider.js';\r\nimport { PositionExtensionProvider } from '../widgets/designerView/extensions/PositionExtensionProvider.js';\r\nimport { HighlightElementExtensionProvider } from '../widgets/designerView/extensions/HighlightElementExtensionProvider.js';\r\nimport { NamedTools } from '../widgets/designerView/tools/NamedTools.js';\r\nimport { PointerTool } from '../widgets/designerView/tools/PointerTool.js';\r\nimport { DrawPathTool } from '../widgets/designerView/tools/DrawPathTool.js';\r\nimport { SelectionDefaultExtensionProvider } from '../widgets/designerView/extensions/SelectionDefaultExtensionProvider.js';\r\nimport { ResizeExtensionProvider } from '../widgets/designerView/extensions/ResizeExtensionProvider.js';\r\nimport { RotateExtensionProvider } from '../widgets/designerView/extensions/transforms/RotateExtensionProvider.js';\r\nimport { RotateGroupExtensionProvider } from '../widgets/designerView/extensions/transforms/RotateGroupExtensionProvider.js';\r\nimport { ZoomTool } from '../widgets/designerView/tools/ZoomTool.js';\r\nimport { PanTool } from '../widgets/designerView/tools/PanTool.js';\r\nimport { CopyPasteContextMenu } from '../widgets/designerView/extensions/contextMenu/CopyPasteContextMenu.js';\r\nimport { PasteFormatContextMenu } from '../widgets/designerView/extensions/contextMenu/PasteFormatContextMenu.js';\r\nimport { ToolWindowsContextMenu } from '../widgets/designerView/extensions/contextMenu/ToolWindowsContextMenu.js';\r\nimport { ZMoveContextMenu } from '../widgets/designerView/extensions/contextMenu/ZMoveContextMenu.js';\r\nimport { MultipleItemsSelectedContextMenu } from '../widgets/designerView/extensions/contextMenu/MultipleItemsSelectedContextMenu.js';\r\nimport { RectangleSelectorTool } from '../widgets/designerView/tools/RectangleSelectorTool.js';\r\nimport { MagicWandSelectorTool } from '../widgets/designerView/tools/MagicWandSelectorTool.js';\r\nimport { PickColorTool } from '../widgets/designerView/tools/PickColorTool.js';\r\nimport { TextTool } from '../widgets/designerView/tools/TextTool.js';\r\nimport { GrayOutExtensionProvider } from '../widgets/designerView/extensions/GrayOutExtensionProvider.js';\r\nimport { AltToEnterContainerExtensionProvider } from '../widgets/designerView/extensions/AltToEnterContainerExtensionProvider.js';\r\nimport { InvisibleElementExtensionProvider } from '../widgets/designerView/extensions/InvisibleElementExtensionProvider.js';\r\nimport { ItemsBelowContextMenu } from '../widgets/designerView/extensions/contextMenu/ItemsBelowContextMenu.js';\r\nimport { GridPlacementService } from './placementService/GridPlacementService.js';\r\nimport { ElementAtPointService } from './elementAtPointService/ElementAtPointService.js';\r\nimport { FlexBoxPlacementService } from './placementService/FlexBoxPlacementService.js';\r\nimport { SnaplinesProviderService } from './placementService/SnaplinesProviderService.js';\r\nimport { ExternalDragDropService } from './dragDropService/ExternalDragDropService.js';\r\nimport { EditTextExtensionProvider } from '../widgets/designerView/extensions/EditText/EditTextExtensionProvider.js';\r\nimport { CopyPasteService } from './copyPasteService/CopyPasteService.js';\r\nimport { DefaultModelCommandService } from './modelCommandService/DefaultModelCommandService.js';\r\nimport { ButtonSeperatorProvider } from '../widgets/designerView/extensions/buttons/ButtonSeperatorProvider.js';\r\nimport { GridExtensionDesignViewConfigButtons } from '../widgets/designerView/extensions/buttons/GridExtensionDesignViewConfigButtons.js';\r\nimport { DrawRectTool } from '../widgets/designerView/tools/DrawRectTool.js';\r\nimport { DrawEllipsisTool } from '../widgets/designerView/tools/DrawEllipsisTool.js';\r\nimport { DrawLineTool } from '../widgets/designerView/tools/DrawLineTool.js';\r\nimport { HtmlWriterService } from './htmlWriterService/HtmlWriterService.js';\r\nimport { RectContextMenu } from '../widgets/designerView/extensions/contextMenu/RectContextMenu.js';\r\nimport { PathContextMenu } from '../widgets/designerView/extensions/contextMenu/PathContextMenu.js';\r\nimport { SeperatorContextMenu } from '../widgets/designerView/extensions/contextMenu/SeperatorContextMenu.js';\r\nimport { ZoomToElementContextMenu } from '../widgets/designerView/extensions/contextMenu/ZoomToElementContextMenu.js';\r\nimport { RotateLeftAndRight } from '../widgets/designerView/extensions/contextMenu/RotateLeftAndRightContextMenu.js';\r\nimport { SelectAllChildrenContextMenu } from '../widgets/designerView/extensions/contextMenu/SelectAllChildrenContextMenu.js';\r\nimport { PointerToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/PointerToolButtonProvider.js';\r\nimport { SeperatorToolProvider } from '../widgets/designerView/tools/toolBar/buttons/SeperatorToolProvider.js';\r\nimport { ZoomToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/ZoomToolButtonProvider.js';\r\nimport { DrawToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/DrawToolButtonProvider.js';\r\nimport { TextToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/TextToolButtonProvider.js';\r\nimport { SelectorToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/SelectorToolButtonProvider.js';\r\nimport { GrayOutDragOverContainerExtensionProvider } from '../widgets/designerView/extensions/GrayOutDragOverContainerExtensionProvider.js';\r\nimport { PropertyGroupsService } from './propertiesService/PropertyGroupsService.js';\r\nimport { PlacementExtensionProvider } from '../widgets/designerView/extensions/PlacementExtensionProvider.js';\r\nimport { FlexboxExtensionProvider } from '../widgets/designerView/extensions/flex/FlexboxExtensionProvider.js';\r\nimport { FlexboxExtensionDesignViewConfigButtons } from '../widgets/designerView/extensions/buttons/FlexboxExtensionDesignViewConfigButtons.js';\r\nimport { InvisibleElementExtensionDesignViewConfigButtons } from '../widgets/designerView/extensions/buttons/InvisibleElementExtensionDesignViewConfigButtons.js';\r\nimport { UndoService } from './undoService/UndoService.js';\r\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\r\nimport { SelectionService } from './selectionService/SelectionService.js';\r\nimport { StylesheetServiceDesignViewConfigButtons } from '../widgets/designerView/extensions/buttons/StylesheetServiceDesignViewConfigButtons.js';\r\nimport { JumpToElementContextMenu } from '../widgets/designerView/extensions/contextMenu/JumpToElementContextMenu.js';\r\nimport { EditGridColumnRowSizesExtensionProvider } from '../widgets/designerView/extensions/grid/EditGridColumnRowSizesExtensionProvider.js';\r\nimport { DisplayGridExtensionProvider } from '../widgets/designerView/extensions/grid/DisplayGridExtensionProvider.js';\r\nimport { ApplyFirstMachingExtensionProvider } from '../widgets/designerView/extensions/logic/ApplyFirstMachingExtensionProvider.js';\r\nimport { DesignItemDocumentPositionService } from './designItemDocumentPositionService/DesignItemDocumentPositionService.js';\r\nimport { TransformToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/TransformToolButtonProvider.js';\r\nimport { MultipleSelectionRectExtensionProvider } from '../widgets/designerView/extensions/MultipleSelectionRectExtensionProvider.js';\r\nimport { DragDropService } from './dragDropService/DragDropService.js';\r\nimport { EventsService } from './eventsService/EventsService.js';\r\nimport { SimpleDemoProviderService } from './demoProviderService/SimpleDemoProviderService.js';\r\nimport { DrawElementTool } from '../widgets/designerView/tools/DrawElementTool.js';\r\nimport { RoundPixelsDesignViewConfigButton } from '../widgets/designerView/extensions/buttons/RoundPixelsDesignViewConfigButton.js';\r\nimport { MathMLElementsPropertiesService } from './propertiesService/services/MathMLElementsPropertiesService.js';\r\nimport { UnifiedGeometryExtensionProvider } from '../widgets/designerView/extensions/svg/UnifiedGeometryExtensionProvider.js';\nimport { SvgPathSourceMapProvider } from './sourceMapService/SvgPathSourceMapProvider.js';\nimport { ConditionExtensionProvider } from '../widgets/designerView/extensions/logic/ConditionExtensionProvider.js';\r\nimport { GridToolbarExtensionProvider } from '../widgets/designerView/extensions/grid/GridToolbarExtensionProvider.js';\r\nimport { FlexToolbarExtensionProvider } from '../widgets/designerView/extensions/flex/FlexToolbarExtensionProvider.js';\r\nimport { BlockToolbarExtensionProvider } from '../widgets/designerView/extensions/block/BlockToolbarExtensionProvider.js';\r\nimport { ChildContextMenu } from '../widgets/designerView/extensions/contextMenu/ChildContextMenu.js';\r\nimport { GridChildToolbarExtensionProvider } from '../widgets/designerView/extensions/grid/GridChildToolbarExtensionProvider.js';\r\nimport { ToolbarExtensionsDesignViewConfigButtons } from '../widgets/designerView/extensions/buttons/ToolbarExtensionsDesignViewConfigButtons.js';\r\nimport { PaddingExtensionProvider } from '../widgets/designerView/extensions/PaddingExtensionProvider.js';\r\nimport { GridChildResizeExtensionProvider } from '../widgets/designerView/extensions/grid/GridChildResizeExtensionProvider.js';\r\nimport { AlignItemsContextMenu } from '../widgets/designerView/extensions/contextMenu/AlignItemsContextMenu.js';\r\nimport { BasicWebcomponentPropertiesService } from './propertiesService/services/BasicWebcomponentPropertiesService.js';\r\nimport { PreviousElementSelectExtensionProvider } from '../widgets/designerView/extensions/PreviousElementSelectExtensionProvider.js';\r\nimport { ForceCssContextMenu } from '../widgets/designerView/extensions/contextMenu/ForceCssContextMenu.js';\r\nimport { OptionsContextMenuButton } from '../widgets/designerView/extensions/buttons/OptionsContextMenuButton.js';\r\nimport { ChildrenContextMenu } from '../widgets/designerView/extensions/contextMenu/ChildrenContextMenu.js';\r\nimport { MarginTool } from '../widgets/designerView/tools/MarginTool.js';\r\nimport { SimpleToolButtonProvider } from '../widgets/designerView/tools/toolBar/buttons/SimpleToolButtonProvider.js';\r\nimport { assetsPath } from '../../Constants.js';\r\nimport { PaddingTool } from '../widgets/designerView/tools/PaddingTool.js';\r\nimport { DesignItemService } from './designItemService/DesignItemService.js';\r\nimport { DeletionService } from './deletionService/DeletionService.js';\r\nimport { MiniatureViewService } from './miniatureViewService/MiniatureViewService.js';\r\nimport { DisplayMediaPngWriterService } from './pngCreatorService/DisplayMediaPngWriterService.js';\r\nimport { SearchService } from './searchService/SearchService.js';\r\nimport { BasicContextMenu } from '../widgets/designerView/extensions/contextMenu/BasicContextMenu.js';\r\nimport { ProjectiveTransformExtension } from '../widgets/designerView/extensions/transforms/ProjectiveTransformExtension.js';\nimport { ProjectiveTransformExtensionProvider } from '../widgets/designerView/extensions/transforms/ProjectiveTransformExtensionProvider.js';\nimport { DefaultEditorTypeService } from './propertiesService/DefaultEditorTypeService.js';\nimport { StyleElementRenderedDesignItemService } from './renderedDesignItemService/StyleElementRenderedDesignItemService.js';\n\r\nexport function createDefaultServiceContainer() {\r\n  let serviceContainer = new ServiceContainer();\r\n\r\n  let defaultPlacementService = new DefaultPlacementService();\r\n  serviceContainer.register(\"containerService\", defaultPlacementService);\r\n  serviceContainer.register(\"containerService\", new GridPlacementService(defaultPlacementService));\r\n  serviceContainer.register(\"containerService\", new FlexBoxPlacementService(defaultPlacementService));\r\n  serviceContainer.register(\"propertyService\", new BasicWebcomponentPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new PolymerPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new LitElementPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new NativeElementsPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new SVGElementsPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new MathMLElementsPropertiesService());\r\n  serviceContainer.register(\"propertyService\", new Lit2PropertiesService());\r\n  serviceContainer.register(\"propertyService\", new BaseCustomWebComponentPropertiesService());\r\n  serviceContainer.register(\"propertyGroupsService\", new PropertyGroupsService());\r\n  serviceContainer.register(\"instanceService\", new DefaultInstanceService());\r\n  serviceContainer.register(\"propertyEditorTypesService\", new DefaultPropertyEditorTypesService());\r\n  serviceContainer.register(\"editorTypeService\", new DefaultEditorTypeService());\r\n  serviceContainer.register(\"htmlWriterService\", new HtmlWriterService());\n  serviceContainer.register(\"snaplinesProviderService\", new SnaplinesProviderService());\n  serviceContainer.register(\"htmlParserService\", new DefaultHtmlParserService());\n  serviceContainer.register(\"renderedDesignItemService\", new StyleElementRenderedDesignItemService());\n  serviceContainer.register(\"elementAtPointService\", new ElementAtPointService());\n  serviceContainer.register(\"externalDragDropService\", new ExternalDragDropService());\r\n  serviceContainer.register(\"dragDropService\", new DragDropService());\r\n  serviceContainer.register(\"copyPasteService\", new CopyPasteService());\r\n  serviceContainer.register(\"modelCommandService\", new DefaultModelCommandService());\r\n  serviceContainer.register(\"demoProviderService\", new SimpleDemoProviderService());\r\n  serviceContainer.register(\"eventsService\", new EventsService());\r\n  serviceContainer.register(\"designItemService\", new DesignItemService());\r\n  serviceContainer.register(\"deletionService\", new DeletionService());\r\n  serviceContainer.register(\"miniatureViewService\", new MiniatureViewService());\r\n  serviceContainer.register(\"pngCreatorService\", new DisplayMediaPngWriterService());\r\n  serviceContainer.register(\"searchService\", new SearchService());\r\n\r\n  serviceContainer.register(\"undoService\", (designerCanvas: IDesignerCanvas) => new UndoService(designerCanvas));\r\n  serviceContainer.register(\"selectionService\", (designerCanvas: IDesignerCanvas) => new SelectionService(designerCanvas, false));\r\n  serviceContainer.register(\"designItemDocumentPositionService\", (designerCanvas: IDesignerCanvas) => new DesignItemDocumentPositionService(designerCanvas));\n  serviceContainer.sourceMapProviders.push(new SvgPathSourceMapProvider());\n\n  serviceContainer.designerExtensions.set(ExtensionType.Permanent, [\n    new InvisibleElementExtensionProvider(),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.PrimarySelection, [\r\n    new ConditionExtensionProvider(new MultipleSelectionRectExtensionProvider(), item => !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.Selection, [\r\n    new ConditionExtensionProvider(new SelectionDefaultExtensionProvider(), item => !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.OnlyOneItemSelected, [\r\n    new ConditionExtensionProvider(new ElementDragTitleExtensionProvider(), item => !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n    new ConditionExtensionProvider(new PreviousElementSelectExtensionProvider(), item => !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n    new ConditionExtensionProvider(new MarginExtensionProvider, (_, c) => c.activeTool instanceof MarginTool || c.activeTool instanceof PaddingTool, true),\r\n    new ConditionExtensionProvider(new PaddingExtensionProvider, (_, c) => c.activeTool instanceof MarginTool || c.activeTool instanceof PaddingTool, true),\r\n    new PositionExtensionProvider(),\r\n    new UnifiedGeometryExtensionProvider(),\r\n    new ApplyFirstMachingExtensionProvider(new GridChildResizeExtensionProvider(), new ResizeExtensionProvider(true)),\r\n    new TransformOriginExtensionProvider(true),\r\n    new RotateExtensionProvider(),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.MultipleItemsSelected, [\r\n    new RotateGroupExtensionProvider(),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.PrimarySelectionRefreshed, [\r\n    new GridChildToolbarExtensionProvider(),\r\n    new GridToolbarExtensionProvider(),\r\n    new FlexToolbarExtensionProvider(),\r\n    new BlockToolbarExtensionProvider(),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.PrimarySelectionAndCanBeEntered, [\r\n    new DisplayGridExtensionProvider(),\r\n    new EditGridColumnRowSizesExtensionProvider(),\r\n    new FlexboxExtensionProvider(),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.PrimarySelectionContainerAndCanBeEntered, [\r\n    new DisplayGridExtensionProvider('lightgray', '#8080802b'),\r\n    new FlexboxExtensionProvider()\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.MouseOver, [\r\n    new HighlightElementExtensionProvider(),\r\n    //new ConditionExtensionProvider(new ElementDragTitleExtensionProvider(), item => item.instanceServiceContainer.selectionService.primarySelection !== item && !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n    //new ConditionExtensionProvider(new PreviousElementSelectExtensionProvider(), item => item.instanceServiceContainer.selectionService.primarySelection !== item && !(item.node instanceof item.window.SVGElement) || item.node instanceof item.window.SVGSVGElement),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.Placement, [\r\n    new PlacementExtensionProvider()\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.ContainerDrag, [\r\n    new GrayOutExtensionProvider()\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.ContainerDragOverAndCanBeEntered, [\r\n    new ApplyFirstMachingExtensionProvider(\r\n      new DisplayGridExtensionProvider(),\r\n      new GrayOutDragOverContainerExtensionProvider(),\r\n    ),\r\n    new AltToEnterContainerExtensionProvider()\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.ContainerExternalDragOverAndCanBeEntered, [\r\n    new ApplyFirstMachingExtensionProvider(\r\n      new DisplayGridExtensionProvider(),\r\n      new GrayOutDragOverContainerExtensionProvider(),\r\n    ),\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.Doubleclick, [\r\n    new EditTextExtensionProvider()\r\n  ]);\r\n  serviceContainer.designerExtensions.set(ExtensionType.ManualApplied, [\r\n    new ProjectiveTransformExtensionProvider()\r\n  ]);\r\n\r\n  serviceContainer.designerPointerExtensions.push(\r\n    //new CursorLinePointerExtensionProvider()\r\n  );\r\n\r\n  serviceContainer.designerTools.set(NamedTools.Pointer, new PointerTool());\r\n  serviceContainer.designerTools.set(NamedTools.DrawSelection, new RectangleSelectorTool());\r\n  serviceContainer.designerTools.set(NamedTools.DrawPath, new DrawPathTool({ interpolatePoints: true }));\r\n  serviceContainer.designerTools.set(NamedTools.DrawRect, new DrawRectTool());\r\n  serviceContainer.designerTools.set(NamedTools.DrawEllipsis, new DrawEllipsisTool());\r\n  serviceContainer.designerTools.set(NamedTools.DrawLine, new DrawLineTool());\r\n  serviceContainer.designerTools.set(NamedTools.Zoom, new ZoomTool());\r\n  serviceContainer.designerTools.set(NamedTools.Pan, new PanTool());\r\n  serviceContainer.designerTools.set(NamedTools.RectangleSelector, new RectangleSelectorTool());\r\n  serviceContainer.designerTools.set(NamedTools.MagicWandSelector, new MagicWandSelectorTool());\r\n  serviceContainer.designerTools.set(NamedTools.PickColor, new PickColorTool());\r\n  serviceContainer.designerTools.set(NamedTools.Text, new TextTool());\r\n  serviceContainer.designerTools.set(NamedTools.DrawElementTool, DrawElementTool);\r\n  serviceContainer.designerTools.set(NamedTools.Margin, new MarginTool());\r\n  serviceContainer.designerTools.set(NamedTools.Padding, new PaddingTool());\r\n\r\n  serviceContainer.designViewConfigButtons.push(\r\n    new ButtonSeperatorProvider(20),\r\n    new GridExtensionDesignViewConfigButtons(),\r\n    new FlexboxExtensionDesignViewConfigButtons(),\r\n    new ButtonSeperatorProvider(10),\r\n    new InvisibleElementExtensionDesignViewConfigButtons(),\r\n    new ButtonSeperatorProvider(10),\r\n    new StylesheetServiceDesignViewConfigButtons(),\r\n    new ButtonSeperatorProvider(10),\r\n    new ToolbarExtensionsDesignViewConfigButtons(),\r\n    new ButtonSeperatorProvider(30),\r\n    new RoundPixelsDesignViewConfigButton(),\r\n    new ButtonSeperatorProvider(30),\r\n    new OptionsContextMenuButton()\r\n  );\r\n\r\n  serviceContainer.designViewToolbarButtons.push(\r\n    new PointerToolButtonProvider(),\r\n    new SeperatorToolProvider(22),\r\n    new SelectorToolButtonProvider(),\r\n    new SeperatorToolProvider(22),\r\n    new SimpleToolButtonProvider(\"Margin\", assetsPath + 'images/tools/Margin.svg'),\r\n    new SeperatorToolProvider(22),\r\n    new SimpleToolButtonProvider(\"Padding\", assetsPath + 'images/tools/Padding.svg'),\r\n    new SeperatorToolProvider(22),\r\n    new ZoomToolButtonProvider(),\r\n    new SeperatorToolProvider(22),\r\n    new DrawToolButtonProvider(),\r\n    new SeperatorToolProvider(22),\r\n    new TextToolButtonProvider(),\r\n    new SeperatorToolProvider(22),\r\n    new TransformToolButtonProvider()\r\n  );\r\n\r\n  serviceContainer.designerContextMenuExtensions = [\r\n    new ChildContextMenu('edit', new CopyPasteContextMenu(), new SeperatorContextMenu(), new PasteFormatContextMenu()),\r\n    new SeperatorContextMenu(),\r\n    new ToolWindowsContextMenu(),\r\n    new SeperatorContextMenu(),\r\n    new ChildContextMenu('modify',\r\n      new RotateLeftAndRight(),\r\n      new SeperatorContextMenu(),\r\n      new ZMoveContextMenu(),\r\n      new SeperatorContextMenu(),\r\n      new AlignItemsContextMenu(),\r\n      new SeperatorContextMenu(),\r\n      new BasicContextMenu({ title: '3D transform', action: (e, designerCanvas, designItem) => { \r\n        designerCanvas.extensionManager.removeExtensions([designItem], false, ExtensionType.PrimarySelection); \r\n        designerCanvas.extensionManager.removeExtensions([designItem], false, ExtensionType.OnlyOneItemSelected); \r\n        designerCanvas.extensionManager.applyExtensionInstance(designItem, new ProjectiveTransformExtension(designerCanvas.extensionManager, designerCanvas, designItem), ExtensionType.OnlyOneItemSelected); } })),\r\n    new SeperatorContextMenu(),\r\n    new ChildContextMenu('view', new JumpToElementContextMenu(), new ZoomToElementContextMenu()),\r\n    new SeperatorContextMenu(),\r\n    new ChildContextMenu('force', new ForceCssContextMenu()),\r\n    new SeperatorContextMenu(),\r\n    new MultipleItemsSelectedContextMenu(),\r\n    new PathContextMenu(),\r\n    new RectContextMenu(),\r\n    new SeperatorContextMenu(),\r\n    new SelectAllChildrenContextMenu(),\r\n    new SeperatorContextMenu(),\r\n    new ItemsBelowContextMenu(),\r\n    new ChildrenContextMenu(),\r\n  ];\r\n\r\n  return serviceContainer;\r\n}\r\n\r\nexport default createDefaultServiceContainer;\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/GlobalContext.ts",
    "content": "//Service container should not be something with changeing information, so global context is for tool and color (and maybe more)\r\n\r\nimport { PropertyChangedArgs, TypedEvent } from \"@node-projects/base-custom-webcomponent\";\r\nimport { ITool } from '../widgets/designerView/tools/ITool.js';\r\nimport { ServiceContainer } from './ServiceContainer.js';\r\nimport { IDesignItem } from \"../item/IDesignItem.js\";\r\n\r\nexport class GlobalContext {\r\n\r\n  private _serviceContainer: ServiceContainer\r\n  private _tool: ITool;\r\n  private _strokeColor: string = 'black';\r\n  private _strokeThickness: string = '3';\r\n  private _fillBrush: string = 'none';\r\n\r\n  constructor(serviceContainer: ServiceContainer) {\r\n    this._serviceContainer = serviceContainer;\r\n  }\r\n\r\n  public set tool(tool: ITool) {\r\n    if (this._tool !== tool) {\r\n      const oldTool = this._tool;\r\n      if (oldTool) {\r\n        oldTool.dispose();\r\n      }\r\n      this._tool = tool;\r\n      let toolName = null;\r\n      for (let t of this._serviceContainer.designerTools) {\r\n        if (t[1] == tool)\r\n          toolName = t[0];\r\n      }\r\n\r\n      this.onToolChanged.emit(new PropertyChangedArgs<{ name: string, tool: ITool }>({ name: toolName, tool: tool }, { name: null, tool: oldTool }));\r\n      if (this._tool)\r\n        this._tool.activated(this._serviceContainer);\r\n    }\r\n  }\r\n  public get tool(): ITool {\r\n    return this._tool;\r\n  }\r\n  readonly onToolChanged = new TypedEvent<PropertyChangedArgs<{ name: string, tool: ITool }>>();\r\n\r\n  finishedWithTool: (tool: ITool) => void = () => this.tool = null;\r\n\r\n  public set strokeColor(strokeColor: string) {\r\n    if (this._strokeColor !== strokeColor) {\r\n      const oldStrokeColor = this._strokeColor;\r\n      this._strokeColor = strokeColor;\r\n      this.onStrokeColorChanged.emit(new PropertyChangedArgs<string>(strokeColor, oldStrokeColor));\r\n    }\r\n  }\r\n  public get strokeColor(): string {\r\n    return this._strokeColor;\r\n  }\r\n  readonly onStrokeColorChanged = new TypedEvent<PropertyChangedArgs<string>>();\r\n\r\n  public set strokeThickness(strokeThickness: string) {\r\n    if (this._strokeThickness !== strokeThickness) {\r\n      const oldStrokeThickness = this._strokeThickness;\r\n      this._strokeThickness = strokeThickness;\r\n      this.onStrokeThicknessChanged.emit(new PropertyChangedArgs<string>(strokeThickness, oldStrokeThickness));\r\n    }\r\n  }\r\n  public get strokeThickness(): string {\r\n    return this._strokeThickness;\r\n  }\r\n  readonly onStrokeThicknessChanged = new TypedEvent<PropertyChangedArgs<string>>();\r\n\r\n  public set fillBrush(fillBrush: string) {\r\n    this._fillBrush = fillBrush;\r\n    if (this._fillBrush !== fillBrush) {\r\n      const oldFillBrush = this._fillBrush;\r\n      this._fillBrush = fillBrush;\r\n      this.onFillBrushChanged.emit(new PropertyChangedArgs<string>(fillBrush, oldFillBrush));\r\n    }\r\n  }\r\n  public get fillBrush(): string {\r\n    return this._fillBrush;\r\n  }\r\n  readonly onFillBrushChanged = new TypedEvent<PropertyChangedArgs<string>>();\r\n\r\n  readonly showConfigClicked = new TypedEvent<{configUi:Element, designItem: IDesignItem}>();\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/IService.ts",
    "content": "export interface IService {\r\n    \r\n} "
  },
  {
    "path": "packages/web-component-designer/src/elements/services/IServiceContainer.ts",
    "content": "export interface IServiceContainer {\r\n    register(name: string, service: any);\r\n    getLastService(service: string): any;\r\n    getServices(service: string): any[];\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/InstanceServiceContainer.ts",
    "content": "import { ISelectionService } from './selectionService/ISelectionService.js';\r\nimport { IUndoService } from './undoService/IUndoService.js';\r\nimport { BaseServiceContainer } from './BaseServiceContainer.js';\r\nimport { DesignContext } from '../widgets/designerView/DesignContext.js';\r\nimport { IDesignContext } from '../widgets/designerView/IDesignContext.js';\r\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\r\nimport { IStylesheetService } from './stylesheetService/IStylesheetService.js';\r\nimport { IDesignItemDocumentPositionService } from './designItemDocumentPositionService/IDesignItemDocumentPositionService.js';\r\nimport { DocumentContainer } from '../documentContainer.js';\r\nimport { ICollaborationService } from './collaborationService/ICollaborationService.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignItem } from '../item/IDesignItem.js';\r\n\r\ninterface InstanceServiceNameMap {\r\n  \"undoService\": IUndoService;\r\n  \"selectionService\": ISelectionService;\r\n  \"stylesheetService\": IStylesheetService;\r\n  \"designItemDocumentPositionService\": IDesignItemDocumentPositionService;\r\n}\r\n\r\ninterface IContentChangedParsed {\r\n  changeType: 'parsed';\r\n}\r\n\r\ninterface IContentChangedWithDesignItems {\r\n  changeType: \"added\" | \"removed\" | \"moved\";\r\n  designItems: IDesignItem[];\r\n}\r\n\r\ninterface IContentChangedChangeWithDesignItems {\r\n  changeType: \"changed\";\r\n  designItems: IDesignItem[];\r\n  type: \"attribute\" | \"css\" | \"property\";\r\n  name: string;\r\n  oldValue?: any;\r\n  newValue?: any;\r\n}\r\n\r\nexport type IContentChanged = IContentChangedParsed | IContentChangedWithDesignItems | IContentChangedChangeWithDesignItems ;\r\n\r\nexport class InstanceServiceContainer extends BaseServiceContainer<InstanceServiceNameMap> {\r\n  public designContext: IDesignContext = new DesignContext();\r\n  public readonly designerCanvas: IDesignerCanvas;\r\n  public collaborationService?: ICollaborationService;\r\n\r\n  public designer: any; //usable to assign designer from outside\r\n  public documentContainer: DocumentContainer; //usable to assign designer from outside\r\n\r\n  /** Event fired when the content of the designer changes, but raised from UndoService, so it should not be used to modify the elements again */\r\n  public readonly onContentChanged = new TypedEvent<IContentChanged[]>();\r\n  \r\n  constructor(designerCanvas: IDesignerCanvas) {\r\n    super();\r\n    this.designerCanvas = designerCanvas;\r\n  }\r\n\r\n  get rootDesignItem(): IDesignItem {\r\n    return this.designerCanvas.rootDesignItem;\r\n  }\r\n\r\n  get undoService(): IUndoService {\r\n    return this.getLastService('undoService');\r\n  }\r\n\r\n  get selectionService(): ISelectionService {\r\n    return this.getLastService('selectionService');\r\n  }\r\n\r\n  get stylesheetService(): IStylesheetService {\r\n    return this.getLastService('stylesheetService');\r\n  }\r\n\r\n  get designItemDocumentPositionService(): IDesignItemDocumentPositionService {\r\n    return this.getLastService('designItemDocumentPositionService');\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/ServiceContainer.ts",
    "content": "import { IPropertiesService } from './propertiesService/IPropertiesService.js';\r\nimport { IPlacementService } from './placementService/IPlacementService.js';\r\nimport { IElementsService } from './elementsService/IElementsService.js';\r\nimport { IInstanceService } from './instanceService/IInstanceService.js';\r\nimport { IPropertyEditorTypesService } from './propertiesService/IPropertyEditorTypesService.js';\r\nimport { BaseServiceContainer } from './BaseServiceContainer.js';\r\nimport { IHtmlWriterService } from './htmlWriterService/IHtmlWriterService.js';\r\nimport { ICodeView } from '../widgets/codeView/ICodeView.js';\r\nimport { IHtmlParserService } from './htmlParserService/IHtmlParserService.js';\r\nimport { IIntializationService } from './initializationService/IIntializationService.js';\r\nimport { IDemoView } from '../widgets/demoView/IDemoView.js';\r\nimport { DemoView } from '../widgets/demoView/demoView.js';\r\nimport { ITool } from '../widgets/designerView/tools/ITool.js';\r\nimport { ExtensionType } from '../widgets/designerView/extensions/ExtensionType.js';\r\nimport { IDesignerExtensionProvider } from '../widgets/designerView/extensions/IDesignerExtensionProvider.js';\r\nimport { NamedTools } from '../widgets/designerView/tools/NamedTools.js';\r\nimport { IContextMenuExtension } from '../widgets/designerView/extensions/contextMenu/IContextMenuExtension.js';\r\nimport { GlobalContext } from './GlobalContext.js';\r\nimport { IBindingService } from './bindingsService/IBindingService.js';\r\nimport { IElementAtPointService } from './elementAtPointService/IElementAtPointService.js';\r\nimport { ISnaplinesProviderService } from \"./placementService/ISnaplinesProviderService.js\";\r\nimport { IExternalDragDropService } from './dragDropService/IExternalDragDropService.js';\r\nimport { ICopyPasteService } from \"./copyPasteService/ICopyPasteService.js\";\r\nimport { IDesignerPointerExtensionProvider } from \"../widgets/designerView/extensions/pointerExtensions/IDesignerPointerExtensionProvider.js\";\r\nimport { IModelCommandService } from \"./modelCommandService/IModelCommandService.js\";\r\nimport { IDesignViewConfigButtonsProvider } from \"../widgets/designerView/extensions/buttons/IDesignViewConfigButtonsProvider.js\";\r\nimport { IDemoProviderService } from \"./demoProviderService/IDemoProviderService.js\";\r\nimport { IBindableObjectsService } from \"./bindableObjectsService/IBindableObjectsService.js\";\r\nimport { IBindableObjectDragDropService } from \"./bindableObjectsService/IBindableObjectDragDropService.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../widgets/designerView/tools/toolBar/IDesignViewToolbarButtonProvider.js\";\r\nimport { IElementInteractionService } from './elementInteractionService/IElementInteractionService.js';\r\nimport { IProperty } from \"./propertiesService/IProperty.js\";\r\nimport { IDesignItem } from \"../item/IDesignItem.js\";\r\nimport { IBinding } from '../item/IBinding.js';\r\nimport { BindingTarget } from '../item/BindingTarget.js';\r\nimport { IPropertyGroupsService } from './propertiesService/IPropertyGroupsService.js';\r\nimport { CodeViewSimple } from '../widgets/codeView/code-view-simple.js';\r\nimport { IUndoService } from './undoService/IUndoService.js';\r\nimport { ISelectionService } from './selectionService/ISelectionService.js';\r\nimport { IStylesheetService } from './stylesheetService/IStylesheetService.js';\r\nimport { IDesignerCanvas } from '../widgets/designerView/IDesignerCanvas.js';\r\nimport { IDesignItemDocumentPositionService } from './designItemDocumentPositionService/IDesignItemDocumentPositionService.js';\r\nimport { IDragDropService } from './dragDropService/IDragDropService.js';\r\nimport { IDesignItemService } from './designItemService/IDesignItemService.js';\r\nimport { IEventsService } from './eventsService/IEventsService.js';\r\nimport { IPropertyGridDragDropService } from './dragDropService/IPropertyGridDragDropService.js';\r\nimport { IConfigUiService } from './configUiService/IConfigUiService.js';\r\nimport { IRefactorService } from './refactorService/IRefactorService.js';\r\nimport { InstanceServiceContainer } from './InstanceServiceContainer.js';\r\nimport { IDeletionService } from './deletionService/IDeletionService.js';\r\nimport { IReferencesChangedService } from './referencesChangedService/IReferencesChangedService.js';\r\nimport { IMiniatureViewService } from './miniatureViewService/IMiniatureViewService.js';\r\nimport { IPngCreatorService } from './pngCreatorService/IPngCreatorService.js';\r\nimport { ISearchService } from './searchService/ISearchService.js';\r\nimport { ICollaborationService } from './collaborationService/ICollaborationService.js';\nimport { IEditorTypeService } from './propertiesService/IEditorTypeService.js';\nimport { ISourceMapProvider } from './sourceMapService/ISourceMapProvider.js';\nimport { IRenderedDesignItemService } from './renderedDesignItemService/IRenderedDesignItemService.js';\n\r\ninterface ServiceNameMap {\r\n  \"propertyService\": IPropertiesService;\r\n  \"attachedPropertyService\": IPropertiesService;\r\n  \"containerService\": IPlacementService;\r\n  \"snaplinesProviderService\": ISnaplinesProviderService;\r\n  \"elementsService\": IElementsService;\r\n  \"instanceService\": IInstanceService;\r\n  \"propertyEditorTypesService\": IPropertyEditorTypesService;\r\n  \"htmlWriterService\": IHtmlWriterService;\r\n  \"htmlParserService\": IHtmlParserService;\r\n  \"intializationService\": IIntializationService;\r\n  \"bindingService\": IBindingService;\r\n  \"bindableObjectsService\": IBindableObjectsService;\r\n  \"bindableObjectDragDropService\": IBindableObjectDragDropService;\r\n  \"elementAtPointService\": IElementAtPointService;\r\n  \"externalDragDropService\": IExternalDragDropService;\r\n  \"copyPasteService\": ICopyPasteService;\r\n  \"modelCommandService\": IModelCommandService\r\n  \"demoProviderService\": IDemoProviderService;\r\n  \"elementInteractionService\": IElementInteractionService;\r\n  \"propertyGroupsService\": IPropertyGroupsService;\r\n  \"dragDropService\": IDragDropService;\r\n  \"designItemService\": IDesignItemService;\r\n  \"eventsService\": IEventsService;\r\n  \"propertyGridDragDropService\": IPropertyGridDragDropService;\r\n  \"configUiService\": IConfigUiService;\r\n  \"refactorService\": IRefactorService;\r\n  \"deletionService\": IDeletionService;\r\n  \"referencesChangedService\": IReferencesChangedService;\r\n  \"miniatureViewService\": IMiniatureViewService;\r\n  \"pngCreatorService\": IPngCreatorService;\r\n  \"searchService\": ISearchService;\r\n  \"editorTypeService\": IEditorTypeService;\n  \"renderedDesignItemService\": IRenderedDesignItemService;\n\r\n  //Factories for Instance Service Containers\r\n  \"undoService\": (designerCanvas: IDesignerCanvas) => IUndoService;\r\n  \"selectionService\": (designerCanvas: IDesignerCanvas) => ISelectionService;\r\n  \"stylesheetService\": (designerCanvas: IDesignerCanvas) => IStylesheetService;\r\n  \"collaborationService\": (designerCanvas: IDesignerCanvas) => ICollaborationService;\r\n  \"designItemDocumentPositionService\": (designerCanvas: IDesignerCanvas) => IDesignItemDocumentPositionService;\r\n}\r\n\r\nconst isTouchUi = navigator.maxTouchPoints > 0;\r\nexport class ServiceContainer extends BaseServiceContainer<ServiceNameMap> {\r\n\r\n  readonly config: {\r\n    codeViewWidget: new (...args: any[]) => ICodeView & HTMLElement;\r\n    demoViewWidget: new (...args: any[]) => IDemoView & HTMLElement;\r\n    openBindingsEditor?: (property: IProperty, designItems: IDesignItem[], binding: IBinding, bindingTarget: BindingTarget) => Promise<void>\r\n  } = {\r\n      codeViewWidget: CodeViewSimple,\r\n      demoViewWidget: DemoView\r\n    };\r\n\r\n  public readonly designerExtensions: Map<(ExtensionType | string), IDesignerExtensionProvider[]> = new Map();\n  public readonly sourceMapProviders: ISourceMapProvider[] = [];\n\r\n  removeDesignerExtensionOfType(container: (ExtensionType | string), lambda: new (...args: any[]) => IDesignerExtensionProvider): void {\r\n    const extContainer = this.designerExtensions.get(container);\r\n    for (let i = 0; i < extContainer.length; i++) {\r\n      if (extContainer[i].constructor === lambda) {\r\n        extContainer.splice(i, 1);\r\n      }\r\n    }\r\n  }\r\n\r\n  public readonly instanceServiceContainerCreatedCallbacks: ((instanceServiceContainer: InstanceServiceContainer) => void)[] = [];\r\n\r\n  public readonly designViewConfigButtons: IDesignViewConfigButtonsProvider[] = [];\r\n\r\n  public readonly designViewToolbarButtons: IDesignViewToolbarButtonProvider[] = [];\r\n\r\n  public readonly designerPointerExtensions: IDesignerPointerExtensionProvider[] = [];\r\n\r\n  public designerContextMenuExtensions: IContextMenuExtension[];\r\n\r\n  public readonly overlayLayerViewAdditionalStyles: CSSStyleSheet[] = [];\r\n\r\n  public readonly globalContext: GlobalContext = new GlobalContext(this);\r\n\r\n  public readonly options = {\r\n    zoomDesignerBackground: true,\r\n    roundPixelsToDecimalPlaces: 0,\r\n    resizerPixelSize: isTouchUi ? 8 : 3\r\n  };\r\n\r\n  public readonly designerTools: Map<string | NamedTools, ITool | (new (IElementDefinition) => ITool)> = new Map();\r\n\r\n  get bindingService(): IBindingService {\r\n    return this.getLastService('bindingService');\r\n  }\r\n\r\n  get bindableObjectsServices(): IBindableObjectsService[] {\r\n    return this.getServices('bindableObjectsService');\r\n  }\r\n\r\n  get bindableObjectDragDropService(): IBindableObjectDragDropService {\r\n    return this.getLastService('bindableObjectDragDropService');\r\n  }\r\n\r\n  get propertyGridDragDropService(): IPropertyGridDragDropService {\r\n    return this.getLastService('propertyGridDragDropService');\r\n  }\r\n\r\n  get dragDropService(): IDragDropService {\r\n    return this.getLastService('dragDropService');\r\n  }\r\n\r\n  get elementInteractionServices(): IElementInteractionService[] {\r\n    return this.getServices('elementInteractionService');\r\n  }\r\n\r\n  get propertiesServices(): IPropertiesService[] {\r\n    return this.getServices('propertyService');\r\n  }\r\n\r\n  get attachedPropertyServices(): IPropertiesService[] {\r\n    return this.getServices('attachedPropertyService');\r\n  }\r\n\r\n  get propertyGroupService(): IPropertyGroupsService {\r\n    return this.getLastService('propertyGroupsService');\r\n  }\r\n\r\n  get containerServices(): IPlacementService[] {\r\n    return this.getServices('containerService');\r\n  }\r\n\r\n  get snaplinesProviderService(): ISnaplinesProviderService {\r\n    return this.getLastService('snaplinesProviderService');\r\n  }\r\n\r\n  get elementsServices(): IElementsService[] {\r\n    return this.getServices('elementsService');\r\n  }\r\n\r\n  get eventsService(): IEventsService[] {\r\n    return this.getServices('eventsService');\r\n  }\r\n\r\n  get instanceServices(): IInstanceService[] {\r\n    return this.getServices('instanceService');\r\n  }\r\n\r\n  get propertyEditorTypesServices(): IPropertyEditorTypesService[] {\r\n    return this.getServices('propertyEditorTypesService');\r\n  }\r\n\r\n  get htmlWriterService(): IHtmlWriterService {\r\n    return this.getLastService('htmlWriterService');\r\n  }\r\n\r\n  get htmlParserService(): IHtmlParserService {\n    return this.getLastService('htmlParserService');\n  }\n\n  get renderedDesignItemServices(): IRenderedDesignItemService[] {\n    return this.getServices('renderedDesignItemService');\n  }\n\r\n  get intializationService(): IIntializationService {\r\n    return this.getLastService('intializationService');\r\n  }\r\n\r\n  get elementAtPointService(): IElementAtPointService {\r\n    return this.getLastService('elementAtPointService');\r\n  }\r\n\r\n  get externalDragDropService(): IExternalDragDropService {\r\n    return this.getLastService('externalDragDropService');\r\n  }\r\n\r\n  get copyPasteService(): ICopyPasteService {\r\n    return this.getLastService('copyPasteService');\r\n  }\r\n\r\n  get modelCommandService(): IModelCommandService {\r\n    return this.getLastService('modelCommandService');\r\n  }\r\n\r\n  get demoProviderService(): IDemoProviderService {\r\n    return this.getLastService('demoProviderService');\r\n  }\r\n\r\n  get designItemService(): IDesignItemService {\r\n    return this.getLastService('designItemService');\r\n  }\r\n\r\n  get configUiServices(): IConfigUiService[] {\r\n    return this.getServices('configUiService');\r\n  }\r\n\r\n  get refactorServices(): IRefactorService[] {\r\n    return this.getServices('refactorService');\r\n  }\r\n\r\n  get deletionService(): IDeletionService {\r\n    return this.getLastService('deletionService');\r\n  }\r\n\r\n  get referencesChangedService(): IReferencesChangedService {\r\n    return this.getLastService('referencesChangedService');\r\n  }\r\n\r\n  get miniatureViewService(): IMiniatureViewService {\r\n    return this.getLastService('miniatureViewService');\r\n  }\r\n\r\n  get pngCreatorService(): IPngCreatorService {\r\n    return this.getLastService('pngCreatorService');\r\n  }\r\n\r\n  get searchService(): ISearchService {\r\n    return this.getLastService('searchService');\r\n  }\r\n\r\n  get editorTypesServices(): IEditorTypeService[] {\r\n    return this.getServices('editorTypeService');\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindableObjectsService/BindableObjectType.ts",
    "content": "export enum BindableObjectType {\r\n    undefined = 'undefined',\r\n    folder = 'folder',\r\n    boolean = 'boolean',\r\n    number = 'number',\r\n    string = 'string',\r\n    date = 'date',\r\n    color = 'color',\r\n    object = 'object',\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindableObjectsService/BindableObjectsTarget.ts",
    "content": "export type BindableObjectsTarget = 'itemsView' | 'binding' | 'script' | 'property';"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindableObjectsService/IBindableObject.ts",
    "content": "import { BindableObjectType } from './BindableObjectType.js';\r\n\r\nexport interface IBindableObject<T> {\r\n    readonly bindabletype?: 'signal' | 'property' | 'context'\r\n    readonly specialType?: string //e.g. signalProperty\r\n    readonly type: BindableObjectType\r\n    readonly name: string;\r\n    readonly fullName: string;\r\n    readonly children?: false | IBindableObject<T>[];\r\n    readonly originalObject?: T;\r\n    readonly description?: string;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindableObjectsService/IBindableObjectDragDropService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { IProperty } from \"../propertiesService/IProperty.js\";\r\nimport { IBindableObject } from \"./IBindableObject.js\";\r\n\r\nexport interface IBindableObjectDragDropService {\r\n  dragEnter(designerCanvas: IDesignerCanvas, event: DragEvent, element: Element): void;\r\n  dragLeave(designerCanvas: IDesignerCanvas, event: DragEvent, element: Element): void;\r\n  dragOver(designerCanvas: IDesignerCanvas, event: DragEvent, element: Element): 'none' | 'copy' | 'link' | 'move';\r\n  drop(designerCanvas: IDesignerCanvas, event: DragEvent, bindableObject: IBindableObject<any>, element: Element): void;\r\n\r\n  dragOverOnProperty?(event: DragEvent, property: IProperty, designItems: IDesignItem[]): 'none' | 'copy' | 'link' | 'move';\r\n  dropOnProperty?(event: DragEvent, property: IProperty, bindableObject: IBindableObject<any>, designItems: IDesignItem[]): void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindableObjectsService/IBindableObjectsService.ts",
    "content": "import { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { BindableObjectsTarget } from './BindableObjectsTarget.js';\r\nimport { IBindableObject } from './IBindableObject.js';\r\n\r\nexport interface IBindableObjectsService {\r\n  readonly name: string;\r\n  hasObjectsForInstanceServiceContainer(instanceServiceContainer: InstanceServiceContainer, source: BindableObjectsTarget);\r\n  getBindableObject(fullName: string, instanceServiceContainer?: InstanceServiceContainer): Promise<IBindableObject<any>>;\r\n  getBindableObjects(parent?: IBindableObject<any>, instanceServiceContainer?: InstanceServiceContainer): Promise<IBindableObject<any>[]>;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindingsService/BaseCustomWebcomponentBindingsService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IBinding } from '../../item/IBinding.js';\r\nimport { IBindingService } from './IBindingService.js';\r\nimport { BindingMode } from '../../item/BindingMode.js';\r\nimport { BindingTarget } from \"../../item/BindingTarget.js\";\r\nimport { PropertiesHelper } from '../propertiesService/services/PropertiesHelper.js';\r\n\r\nexport class BaseCustomWebcomponentBindingsService implements IBindingService {\r\n\r\n  public static type = 'base-custom-webcomponent-binding';\r\n\r\n  getBindings(designItem: IDesignItem): IBinding[] {\r\n    let bindings: IBinding[] = null;\r\n    for (let a of designItem.attributes()) {\r\n      const name = a[0];\r\n      const value = a[1];\r\n      if ((value.startsWith('[[') || value.startsWith('{{')) && (value.endsWith('}}') || value.endsWith(']]'))) {\r\n        if (!bindings)\r\n          bindings = [];\r\n        let bnd: IBinding = { rawName: name, rawValue: value, service: this };\r\n        if (a[0] === 'bcw:visible') {\r\n          bnd.targetName = 'visibility';\r\n          bnd.target = BindingTarget.css;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else if (a[0].startsWith('css:')) {\r\n          bnd.targetName = name.substring(4);\r\n          bnd.target = BindingTarget.css;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else if (a[0].startsWith('class:')) {\r\n          bnd.targetName = name.substring(4);\r\n          bnd.target = BindingTarget.class;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else if (a[0].startsWith('$')) {\r\n          bnd.targetName = name.substring(1);\r\n          bnd.target = BindingTarget.attribute;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else if (a[0].startsWith('@')) {\r\n          bnd.targetName = name.substring(1);\r\n          bnd.target = BindingTarget.event;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else if (a[0].startsWith('.')) {\r\n          bnd.targetName = PropertiesHelper.dashToCamelCase(name.substring(1));\r\n          bnd.target = BindingTarget.explicitProperty;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        } else {\r\n          bnd.targetName = PropertiesHelper.dashToCamelCase(name);\r\n          bnd.target = BindingTarget.property;\r\n          bnd.expression = value.substring(2, value.length - 2);\r\n        }\r\n        bnd.type = BaseCustomWebcomponentBindingsService.type;\r\n        bnd.targetName = bnd.targetName;\r\n        bnd.bindableObjectNames = [value.substring(2, value.length - 2)];\r\n        bindings.push(bnd);\r\n      }\r\n    }\r\n    return bindings;\r\n  }\r\n\r\n  setBinding(designItem: IDesignItem, binding: IBinding): boolean {\r\n    if (binding.type !== BaseCustomWebcomponentBindingsService.type)\r\n      return false;\r\n\r\n    let nm = '';\r\n    switch (binding.target) {\r\n      case BindingTarget.explicitProperty:\r\n        nm += '.';\r\n        break;\r\n      case BindingTarget.css:\r\n        nm += 'css:';\r\n        break;\r\n      case BindingTarget.class:\r\n        nm += 'class';\r\n        break;\r\n      case BindingTarget.attribute:\r\n        nm += '$';\r\n        break;\r\n      case BindingTarget.event:\r\n        nm += '@';\r\n        break;\r\n    }\r\n    nm += binding.targetName;\r\n    let value = (binding.mode == BindingMode.oneWay ? '[[' : '{{') + binding.expression + (binding.mode == BindingMode.oneWay ? ']]' : '}}')\r\n    designItem.setAttribute(nm, value);\r\n    return true;\r\n  }\r\n\r\n  clearBinding(designItem: IDesignItem, propertyName: string, propertyTarget: BindingTarget): boolean {\r\n    return true;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindingsService/IBindingService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IBinding } from '../../item/IBinding.js';\r\nimport { BindingTarget } from '../../item/BindingTarget.js';\r\n\r\n/**\r\n * Can be used to parse bindings wich are done via special HTML Attributes or special Elements\r\n * If your Bindings are to special, or HTML is not valid with them, maybe you need to parse the Bindings already in the\r\n * htmlParserService\r\n */\r\nexport interface IBindingService {\r\n  getBindings(designItem: IDesignItem): IBinding[];\r\n  setBinding(designItem: IDesignItem, binding: IBinding): boolean;\r\n  clearBinding(designItem: IDesignItem, propertyName: string, propertyTarget: BindingTarget): boolean;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindingsService/SpecialTagsBindingService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IBinding } from '../../item/IBinding.js';\r\nimport { IBindingService } from './IBindingService.js';\r\nimport { BindingTarget } from \"../../item/BindingTarget.js\";\r\nimport { BindingMode } from \"../../item/BindingMode.js\";\r\n\r\n\r\n/* Service wich read bindings from special HTML elements -> like tag-binding */\r\n//TODO: refactor so we could use it\r\nexport class SpecialTagsBindingService implements IBindingService {\r\n\r\n  public static type = 'visu-tagbinding-binding'\r\n\r\n  _bindingTagName: string = \"visu-tagbinding\";\r\n  _elementIdAttribute: string = \"elemnt-id\";\r\n  _propertyNameAttribute: string = \"property\";\r\n  _isStyleNameAttribute: string = \"is-style\";\r\n\r\n  constructor() {\r\n  }\r\n\r\n  getBindings(designItem: IDesignItem): IBinding[] {\r\n    const bindings = [];\r\n    const directBindings = designItem.element.querySelectorAll(':scope > ' + this._bindingTagName);\r\n    for (let b of directBindings) {\r\n      bindings.push(this._parseBindingElement(b));\r\n    }\r\n\r\n    if (designItem.id) {\r\n      const nameBindings = designItem.instanceServiceContainer.designerCanvas.rootDesignItem.element.querySelectorAll(this._bindingTagName + \"[\" + this._elementIdAttribute + \"='\" + designItem.id + \"]\");\r\n      for (let b of nameBindings) {\r\n        const bnd = this._parseBindingElement(b);\r\n        (<any>bnd).targetId = designItem.id\r\n        bindings.push(bnd);\r\n      }\r\n    }\r\n\r\n    return null;\r\n  }\r\n\r\n  private _parseBindingElement(b: Element): IBinding {\r\n    let bnd: IBinding = { targetName: b.getAttribute(this._propertyNameAttribute), service: this }\r\n    bnd.target = b.hasAttribute(this._isStyleNameAttribute) ? BindingTarget.css : BindingTarget.property;\r\n    bnd.invert = b.hasAttribute('negative-logic');\r\n    bnd.rawValue = b.outerHTML;\r\n    bnd.type = SpecialTagsBindingService.type;\r\n    bnd.mode = b.hasAttribute('two-way') ? BindingMode.twoWay : BindingMode.oneWay;\r\n    return bnd;\r\n  }\r\n\r\n  setBinding(designItem: IDesignItem, binding: IBinding): boolean {\r\n    return true;\r\n  }\r\n\r\n  clearBinding(designItem: IDesignItem, propertyName: string, propertyTarget: BindingTarget): boolean {\r\n    return true;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/bindingsService/VueBindingsService.ts",
    "content": "//read vue bindings:\r\n\r\n//v-bind:class\r\n//v-bind:style\r\n//v-if\r\n//v-else\r\n//v-show"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/collaborationService/CollaborationNodeIndex.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\nimport { NodeType } from '../../item/NodeType.js';\n\ntype CollaborationNodeIndexCache = {\n  orderedItems: IDesignItem[];\n  byItem: WeakMap<IDesignItem, number>;\n};\n\nconst collaborationNodeIndexCacheKey = Symbol('collaborationNodeIndexCache');\n\nfunction buildCache(rootDesignItem: IDesignItem): CollaborationNodeIndexCache {\n  const orderedItems: IDesignItem[] = [];\n  const byItem = new WeakMap<IDesignItem, number>();\n  let index = 0;\n\n  const visit = (designItem: IDesignItem) => {\n    if (!designItem)\n      return;\n\n    if (!designItem.isRootItem && designItem.nodeType === NodeType.Element) {\n      orderedItems.push(designItem);\n      byItem.set(designItem, index++);\n    }\n\n    if (designItem.hasChildren) {\n      for (const child of designItem.children())\n        visit(child);\n    }\n  };\n\n  visit(rootDesignItem);\n\n  return { orderedItems, byItem };\n}\n\nfunction getCache(rootDesignItem: IDesignItem, cache?: Record<string | symbol, any>): CollaborationNodeIndexCache {\n  if (cache?.[collaborationNodeIndexCacheKey])\n    return cache[collaborationNodeIndexCacheKey];\n\n  const nodeIndexCache = buildCache(rootDesignItem);\n  if (cache)\n    cache[collaborationNodeIndexCacheKey] = nodeIndexCache;\n  return nodeIndexCache;\n}\n\nexport function getCollaborationNodeIndex(designItem: IDesignItem, cache?: Record<string | symbol, any>): number {\n  if (!designItem || designItem.isRootItem)\n    return null;\n\n  const rootDesignItem = designItem.instanceServiceContainer.designerCanvas.rootDesignItem;\n  return getCache(rootDesignItem, cache).byItem.get(designItem) ?? null;\n}\n\nexport function getCollaborationNodeIndexes(designItems: IDesignItem[], cache?: Record<string | symbol, any>): number[] {\n  if (!designItems?.length)\n    return [];\n\n  return designItems.map(x => getCollaborationNodeIndex(x, cache)).filter(x => x != null);\n}\n\nexport function getDesignItemByCollaborationNodeIndex(rootDesignItem: IDesignItem, nodeIndex: number, cache?: Record<string | symbol, any>): IDesignItem {\n  if (nodeIndex == null || nodeIndex < 0)\n    return null;\n\n  return getCache(rootDesignItem, cache).orderedItems[nodeIndex] ?? null;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/collaborationService/ICollaborationService.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\nimport { IStylesheet } from '../stylesheetService/IStylesheetService.js';\nimport { IUndoChangeEvent, UndoChangeKind, UndoChangeSource } from '../undoService/IUndoChangeEvent.js';\nimport { IService } from '../IService.js';\n\nexport type CollaborationConnectionState = 'disconnected' | 'connecting' | 'connected';\n\nexport interface ICollaborationPeerPresence {\n  peerId: string;\n  displayName?: string;\n  color?: string;\n  activeDesignItemId?: string;\n  selectedDesignItemIds?: string[];\n  activeNodeIndex?: number;\n  selectedNodeIndexes?: number[];\n  cursorPosition?: { x: number, y: number };\n  updatedAt: number;\n}\n\nexport interface ICollaborationComment {\n  id: string;\n  targetDesignItemId?: string;\n  targetNodeIndex?: number;\n  text: string;\n  authorPeerId: string;\n  createdAt: number;\n  updatedAt: number;\n  resolved?: boolean;\n}\n\nexport interface ICollaborationSession {\n  sessionId: string;\n  peerId: string;\n  displayName?: string;\n}\n\nexport interface ICollaborationStateChangedEvent {\n  state: CollaborationConnectionState;\n  session?: ICollaborationSession;\n}\n\nexport interface ICollaborationSelectionEvent {\n  source: UndoChangeSource;\n  peerId: string;\n  selectedNodeIndexes: number[];\n  primaryNodeIndex?: number;\n  selectedDesignItemIds: string[];\n  primaryDesignItemId?: string;\n}\n\nexport interface ICollaborationDocumentSnapshot {\n  html: string;\n  stylesheets: IStylesheet[];\n  updatedAt: number;\n}\n\nexport interface ICollaborationRemoteChange {\n  kind: UndoChangeKind;\n  title?: string;\n}\n\nexport interface ICollaborationPeersChangedEvent {\n  source: UndoChangeSource;\n  peer?: ICollaborationPeerPresence;\n  peerId?: string;\n  peers: readonly ICollaborationPeerPresence[];\n}\n\nexport interface ICollaborationCommentsChangedEvent {\n  source: UndoChangeSource;\n  comment?: ICollaborationComment;\n  commentId?: string;\n  comments: readonly ICollaborationComment[];\n}\n\nexport interface ICollaborationTransport {\n  attach(service: ICollaborationService): void | Promise<void>;\n  detach(): void | Promise<void>;\n  connect(session: ICollaborationSession): void | Promise<void>;\n  disconnect(): void | Promise<void>;\n  sendChange(change: ICollaborationRemoteChange, snapshot: ICollaborationDocumentSnapshot): void | Promise<void>;\n  sendSelection(selection: ICollaborationSelectionEvent): void | Promise<void>;\n  sendPresence(peer: ICollaborationPeerPresence): void | Promise<void>;\n  sendComment(change: ICollaborationCommentsChangedEvent): void | Promise<void>;\n}\n\nexport interface ICollaborationService extends IService {\n  readonly state: CollaborationConnectionState;\n  readonly session: ICollaborationSession;\n  readonly peers: readonly ICollaborationPeerPresence[];\n  readonly comments: readonly ICollaborationComment[];\n  readonly transport: ICollaborationTransport;\n  readonly isApplyingRemoteChanges: boolean;\n\n  attachTransport(transport: ICollaborationTransport): void;\n  detachTransport(): void;\n  connect(sessionId: string, peerId: string, displayName?: string): void;\n  disconnect(): void;\n  createSnapshot(): ICollaborationDocumentSnapshot;\n  applyRemoteSnapshot(snapshot: ICollaborationDocumentSnapshot): Promise<void>;\n  applyRemoteChange(change: ICollaborationRemoteChange, snapshot?: ICollaborationDocumentSnapshot): Promise<void>;\n  updateRemoteSelection(peerId: string, selectedNodeIndexes: number[], primaryNodeIndex?: number): void;\n  updatePeerPresence(peer: ICollaborationPeerPresence, source?: UndoChangeSource): void;\n  removePeer(peerId: string, source?: UndoChangeSource): void;\n  upsertComment(comment: ICollaborationComment, source?: UndoChangeSource): void;\n  removeComment(commentId: string, source?: UndoChangeSource): void;\n\n  readonly onStateChanged: TypedEvent<ICollaborationStateChangedEvent>;\n  readonly onChange: TypedEvent<IUndoChangeEvent>;\n  readonly onSelectionChanged: TypedEvent<ICollaborationSelectionEvent>;\n  readonly onPeersChanged: TypedEvent<ICollaborationPeersChangedEvent>;\n  readonly onCommentsChanged: TypedEvent<ICollaborationCommentsChangedEvent>;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/configUiService/IConfigUiService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\n\r\nexport interface IConfigUiService {\r\n    hasConfigUi(designItem: IDesignItem): Promise<boolean>\r\n    getConfigUi(designItem: IDesignItem): Promise<HTMLElement>\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/copyPasteService/CopyPasteAsJsonService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { DomConverter } from \"../../widgets/designerView/DomConverter.js\";\r\nimport { ICopyPasteService } from \"./ICopyPasteService.js\";\r\nimport { ServiceContainer } from '../ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\nimport { copyToClipboard, getFromClipboard, getTextFromClipboard } from \"../../helper/ClipboardHelper.js\";\r\nimport { DesignItem } from \"../../item/DesignItem.js\";\r\nimport { filterChildPlaceItems } from \"../../helper/LayoutHelper.js\";\r\n\r\ninterface ICopyPasteJsonData {\r\n  html?: string;\r\n  positions?: IRect[];\r\n}\r\n\r\ninterface IClipboardPasteData {\r\n  html: string;\r\n  positions: IRect[] | null;\r\n  imageData: Blob | null;\r\n}\r\n\r\nexport class CopyPasteAsJsonService implements ICopyPasteService {\r\n  async copyItems(designItems: IDesignItem[]): Promise<void> {\r\n    const items = filterChildPlaceItems(designItems);\r\n    const copyText = DomConverter.ConvertToString(items, false);\r\n    const positions = items.map(x => x.instanceServiceContainer.designerCanvas.getNormalizedElementCoordinates(x.element));\r\n    const data: ICopyPasteJsonData = { html: copyText, positions: positions };\r\n    copyToClipboard([[\"text/html\", copyText], [\"text/plain\", copyText], [\"application/json\", JSON.stringify(data)]]);\r\n  }\r\n\r\n  async getPasteItems(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<[designItems: IDesignItem[], positions?: IRect[]]> {\r\n    const pasteData = await this._readClipboardPasteData();\r\n    if (pasteData.imageData) {\r\n      let di = await DesignItem.createDesignItemFromImageBlob(serviceContainer, instanceServiceContainer, pasteData.imageData);\r\n      return [[di]];\r\n    }\r\n\r\n    const parserService = serviceContainer.htmlParserService;\r\n    return [await parserService.parse(pasteData.html, serviceContainer, instanceServiceContainer, true), pasteData.positions ?? undefined];\r\n  }\r\n\r\n  private async _readClipboardPasteData(): Promise<IClipboardPasteData> {\r\n    let html = '';\r\n    let positions: IRect[] | null = null;\r\n    let imageData: Blob | null = null;\r\n\r\n    const items = await getFromClipboard();\r\n    if (items != null) {\r\n      const clipboardItem = items[0];\r\n      const jsonData = await this._tryReadClipboardType(clipboardItem, 'application/json');\r\n      if (jsonData) {\r\n        const parsedJson = this._parseJsonClipboardData(jsonData);\r\n        html = parsedJson?.html ?? '';\r\n        positions = parsedJson?.positions ?? null;\r\n      }\r\n\r\n      if (!html) {\r\n        html = await this._tryReadClipboardType(clipboardItem, 'text/html') ?? '';\r\n      }\r\n      if (!html) {\r\n        html = await this._tryReadClipboardType(clipboardItem, 'text/plain') ?? '';\r\n      }\r\n\r\n      try {\r\n        let imageFmt = clipboardItem.types.find(x => x.startsWith(\"image/\"));\r\n        if (imageFmt) {\r\n          imageData = await clipboardItem.getType(imageFmt);\r\n        }\r\n      } catch { }\r\n    } else {\r\n      html = await getTextFromClipboard();\r\n    }\r\n\r\n    return { html, positions, imageData };\r\n  }\r\n\r\n  private _parseJsonClipboardData(data: string): ICopyPasteJsonData | null {\r\n    try {\r\n      return JSON.parse(data);\r\n    } catch {\r\n      return null;\r\n    }\r\n  }\r\n\r\n  private async _tryReadClipboardType(item: ClipboardItem, type: string): Promise<string | null> {\r\n    try {\r\n      return await (await item.getType(type))?.text();\r\n    } catch {\r\n      return null;\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/copyPasteService/CopyPasteService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { DomConverter } from \"../../widgets/designerView/DomConverter.js\";\r\nimport { ICopyPasteService } from \"./ICopyPasteService.js\";\r\nimport { ServiceContainer } from '../ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\nimport { copyToClipboard, getFromClipboard, getTextFromClipboard } from \"../../helper/ClipboardHelper.js\";\r\nimport { filterChildPlaceItems } from \"../../helper/LayoutHelper.js\";\r\n\r\nexport const positionsJsonMime = 'web text/positions';\r\n\r\nexport class CopyPasteService implements ICopyPasteService {\r\n  async copyItems(designItems: IDesignItem[]): Promise<void> {\r\n    const items = filterChildPlaceItems(designItems);\r\n    const copyText = DomConverter.ConvertToString(items, false);\r\n    const positions = items.map(x => x.instanceServiceContainer.designerCanvas.getNormalizedElementCoordinates(x.element));\r\n    copyToClipboard([[\"text/html\", copyText], [positionsJsonMime, JSON.stringify(positions)]]);\r\n  }\r\n\r\n  async getPasteItems(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<[designItems: IDesignItem[], positions?: IRect[]]> {\r\n    let html = '';\r\n    let positions: IRect[] | null = null;\r\n    const items = await getFromClipboard();\r\n    if (items != null) {\r\n      try {\r\n        html = await (await items[0].getType('text/html'))?.text();\r\n      } catch { }\r\n      if (!html)\r\n        html = await (await items[0].getType('text/plain'))?.text();\r\n      try {\r\n        let positionsJson = await (await items[0].getType(positionsJsonMime))?.text();\r\n        positions = JSON.parse(positionsJson)\r\n      } catch { }\r\n    } else {\r\n      html = await getTextFromClipboard();\r\n    }\r\n    const parserService = serviceContainer.htmlParserService;\r\n    return [await parserService.parse(html, serviceContainer, instanceServiceContainer, true), positions ?? undefined];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/copyPasteService/ICopyPasteService.ts",
    "content": "import { IRect } from \"../../../interfaces/IRect.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\n\r\nexport interface ICopyPasteService {\r\n  copyItems(designItems: IDesignItem[]): Promise<void>\r\n  getPasteItems(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<[designItems: IDesignItem[], positions?: IRect[]]>\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/copyPasteService/PasteFormatSnapshot.ts",
    "content": "import { PropertiesHelper } from \"../propertiesService/services/PropertiesHelper.js\";\n\nexport const pasteFormatKinds = ['all', 'border', 'background', 'transform', 'text'] as const;\n\nexport type PasteFormatKind = typeof pasteFormatKinds[number];\n\nexport interface IPasteFormatEntry {\n  readonly name: string;\n  readonly value: string;\n}\n\nexport interface IPasteFormatSnapshot {\n  readonly all: readonly IPasteFormatEntry[];\n  readonly border: readonly IPasteFormatEntry[];\n  readonly background: readonly IPasteFormatEntry[];\n  readonly transform: readonly IPasteFormatEntry[];\n  readonly text: readonly IPasteFormatEntry[];\n}\n\ntype StyleArrayLike = Pick<CSSStyleDeclaration, 'getPropertyValue' | 'length'> & ArrayLike<string>;\ntype StyleEntries = Iterable<[name: string, value: string]>;\n\nconst explicitTextProperties = new Set([\n  'color',\n  'caret-color',\n  'line-height',\n  'letter-spacing',\n  'word-spacing',\n  'white-space',\n  'word-break',\n  'overflow-wrap',\n  'hyphens',\n  'tab-size',\n  'direction',\n  'writing-mode',\n  'unicode-bidi'\n]);\n\nconst explicitTransformProperties = new Set([\n  'translate',\n  'rotate',\n  'scale'\n]);\n\nfunction isBorderProperty(name: string): boolean {\n  return name.startsWith('border') || name.startsWith('corner');\n}\n\nfunction isBackgroundProperty(name: string): boolean {\n  return name.startsWith('background');\n}\n\nfunction isTransformProperty(name: string): boolean {\n  return name === 'transform'\n    || name.startsWith('transform-')\n    || explicitTransformProperties.has(name);\n}\n\nfunction isTextProperty(name: string): boolean {\n  return name.startsWith('font')\n    || name.startsWith('text')\n    || explicitTextProperties.has(name);\n}\n\nfunction collectEntries(style: StyleArrayLike, predicate: (name: string) => boolean): IPasteFormatEntry[] {\n  const entries: IPasteFormatEntry[] = [];\n  const seen = new Set<string>();\n\n  for (let index = 0; index < style.length; index++) {\n    const name = PropertiesHelper.camelToDashCase(style[index]);\n    if (!name || seen.has(name) || !predicate(name)) {\n      continue;\n    }\n\n    const value = style.getPropertyValue(name)?.trim();\n    if (!value) {\n      continue;\n    }\n\n    seen.add(name);\n    entries.push({ name, value });\n  }\n\n  return entries;\n}\n\nfunction createPasteFormatSnapshotFromNormalizedEntries(entries: IPasteFormatEntry[]): IPasteFormatSnapshot {\n  const border = entries.filter(x => isBorderProperty(x.name));\n  const background = entries.filter(x => isBackgroundProperty(x.name));\n  const transform = entries.filter(x => isTransformProperty(x.name));\n  const text = entries.filter(x => isTextProperty(x.name));\n\n  const all: IPasteFormatEntry[] = [];\n  const seen = new Set<string>();\n\n  for (const group of [border, background, transform, text]) {\n    for (const entry of group) {\n      if (seen.has(entry.name)) {\n        continue;\n      }\n\n      seen.add(entry.name);\n      all.push(entry);\n    }\n  }\n\n  return { all, border, background, transform, text };\n}\n\nexport function createPasteFormatSnapshot(style: StyleArrayLike): IPasteFormatSnapshot {\n  const entries = collectEntries(style, () => true);\n  return createPasteFormatSnapshotFromNormalizedEntries(entries);\n}\n\nexport function createPasteFormatSnapshotFromEntries(entries: StyleEntries): IPasteFormatSnapshot | null {\n  const normalizedEntries: IPasteFormatEntry[] = [];\n  const seen = new Set<string>();\n\n  for (const [rawName, rawValue] of entries) {\n    const name = rawName?.trim().toLowerCase();\n    const value = rawValue?.trim();\n    if (!name || !value || seen.has(name)) {\n      continue;\n    }\n\n    seen.add(name);\n    normalizedEntries.push({ name, value });\n  }\n\n  if (!normalizedEntries.length) {\n    return null;\n  }\n\n  return createPasteFormatSnapshotFromNormalizedEntries(normalizedEntries);\n}\n\nexport function getPasteFormatEntries(snapshot: IPasteFormatSnapshot, kind: PasteFormatKind): readonly IPasteFormatEntry[] {\n  return snapshot[kind];\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/deletionService/DeletionService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { DeleteAction } from \"../undoService/transactionItems/DeleteAction.js\";\r\nimport { IDeletionService } from \"./IDeletionService.js\";\r\n\r\nexport class DeletionService implements IDeletionService {\r\n  public removeItems(items: IDesignItem[]) {\r\n    items[0].instanceServiceContainer.undoService.execute(new DeleteAction(items));\r\n    if (items[0].serviceContainer.referencesChangedService)\r\n      items[0].serviceContainer.referencesChangedService.notifyReferencesChanged(items.map(item => ({ designItem: item, type: 'deleted' })));\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/deletionService/IDeletionService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\n\r\nexport interface IDeletionService {\r\n  removeItems(items: IDesignItem[]);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/demoProviderService/IDemoProviderService.ts",
    "content": "import { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\n\r\nexport interface IDemoProviderService {\r\n  provideDemo(container: HTMLElement, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/demoProviderService/IframeDemoProviderService.ts",
    "content": "import { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\nimport { DomHelper } from '@node-projects/base-custom-webcomponent';\r\nimport { IDemoProviderService } from \"./IDemoProviderService.js\";\r\n\r\nexport class IframeDemoProviderService implements IDemoProviderService {\r\n  provideDemo(container: HTMLElement, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string) {\r\n    return new Promise<void>(resolve => {\r\n      const iframe = document.createElement('iframe');\r\n      iframe.style.width = '100%';\r\n      iframe.style.height = '100%';\r\n      iframe.style.border = 'none';\r\n      iframe.style.display = 'none';\r\n      DomHelper.removeAllChildnodes(container);\r\n      container.appendChild(iframe);\r\n\r\n      iframe.onload = () => {\r\n        iframe.style.display = 'block';\r\n        resolve();\r\n      };\r\n\r\n      const doc = iframe.contentWindow.document;\r\n      doc.open();\r\n      doc.write('<script type=\"module\">');\r\n      if (instanceServiceContainer.designContext.npmPackages?.length) {\r\n        doc.write(\"import { NpmPackageLoader } from '../../helper/NpmPackageLoader.js'\");\r\n        doc.write(\"let loader =  new NpmPackageLoader();\");\r\n        for (let p of instanceServiceContainer.designContext.npmPackages) {\r\n          doc.write(\"loader.loadNpmPackage('\" + p + \"', null, null, true);\");\r\n        }\r\n      }\r\n      if (instanceServiceContainer.designContext.imports?.length) {\r\n        for (let i of instanceServiceContainer.designContext.imports) {\r\n          doc.write(\"import '\" + i + \"';\");\r\n        }\r\n      }\r\n      doc.write(\"document.body.style.display='';\");\r\n      doc.write('</script>');\r\n      doc.write('<style>' + (style ?? '') + '</style>');\r\n      doc.write('<body style=\"display:none; width: 100%; height: 100%; margin: 0; padding: 0; position: absolute;\">' + code + '</body>');\r\n      doc.close();\r\n    });\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/demoProviderService/SimpleDemoProviderService.ts",
    "content": "import { DomHelper, cssFromString } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDemoProviderService } from \"./IDemoProviderService.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\n\r\nexport class SimpleDemoProviderService implements IDemoProviderService {\r\n  async provideDemo(container: HTMLElement, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string) {\r\n    const contentDiv = document.createElement('div');\r\n    let shadowRoot = contentDiv.attachShadow({ mode: 'open', serializable: true });\r\n    contentDiv.style.width = '100%';\r\n    contentDiv.style.height = '100%';\r\n    contentDiv.style.border = 'none';\r\n    contentDiv.style.display = 'none';\r\n    contentDiv.style.overflow = 'auto';\r\n    contentDiv.style.position = 'absolute';\r\n\r\n    container.style.position = 'relative';\r\n\r\n    DomHelper.removeAllChildnodes(container);\r\n    container.appendChild(contentDiv);\r\n\r\n    let styles: CSSStyleSheet[] = [];\r\n    if (instanceServiceContainer.designerCanvas.additionalStyles)\r\n      styles.push(...instanceServiceContainer.designerCanvas.additionalStyles);\r\n    if (instanceServiceContainer.stylesheetService) {\r\n      styles.push(...instanceServiceContainer.stylesheetService\r\n        .getStylesheets()\r\n        .map(x => cssFromString(x.content)));\r\n    }\r\n    shadowRoot.adoptedStyleSheets = styles;\r\n    shadowRoot.innerHTML = '';\r\n    if ('setHTMLUnsafe' in shadowRoot)\r\n      //@ts-ignore\r\n      shadowRoot.setHTMLUnsafe(code);\r\n    else\r\n      //@ts-ignore\r\n      shadowRoot.innerHTML = code;\r\n\r\n    contentDiv.style.display = '';\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designItemDocumentPositionService/DesignItemDocumentPositionService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\nimport { IStringPosition } from \"../htmlWriterService/IStringPosition.js\";\nimport { ISourcePart } from \"../sourceMapService/ISourcePart.js\";\nimport { IDesignItemDocumentPositionService } from \"./IDesignItemDocumentPositionService.js\";\n\nexport class DesignItemDocumentPositionService implements IDesignItemDocumentPositionService {\n\n    private _designItemsAssignmentList?: WeakMap<IDesignItem, IStringPosition> = new WeakMap();\n    private _sourceParts: ISourcePart[] = [];\n    private _sourcePartsByDesignItem: WeakMap<IDesignItem, ISourcePart[]> = new WeakMap();\n    private _sourcePartsByDesignItemAndKey: WeakMap<IDesignItem, Map<string, ISourcePart>> = new WeakMap();\n\n    constructor(designerCanvas: IDesignerCanvas) { }\n\n    setPosition(designItem: IDesignItem, position: IStringPosition) {\n        this._designItemsAssignmentList.set(designItem, position);\n    }\r\n\r\n    getPosition(designItem: IDesignItem): IStringPosition {\n        return this._designItemsAssignmentList.get(designItem);\n    }\n\n    clearSourceParts(): void {\n        this._sourceParts = [];\n        this._sourcePartsByDesignItem = new WeakMap();\n        this._sourcePartsByDesignItemAndKey = new WeakMap();\n    }\n\n    addSourcePart(sourcePart: ISourcePart): void {\n        if (!sourcePart?.designItem || !sourcePart.textRange)\n            return;\n\n        this._sourceParts.push(sourcePart);\n\n        let sourceParts = this._sourcePartsByDesignItem.get(sourcePart.designItem);\n        if (!sourceParts) {\n            sourceParts = [];\n            this._sourcePartsByDesignItem.set(sourcePart.designItem, sourceParts);\n        }\n        sourceParts.push(sourcePart);\n\n        let sourcePartsByKey = this._sourcePartsByDesignItemAndKey.get(sourcePart.designItem);\n        if (!sourcePartsByKey) {\n            sourcePartsByKey = new Map();\n            this._sourcePartsByDesignItemAndKey.set(sourcePart.designItem, sourcePartsByKey);\n        }\n        sourcePartsByKey.set(sourcePart.key, sourcePart);\n    }\n\n    addSourceParts(sourceParts: ISourcePart[]): void {\n        for (const sourcePart of sourceParts ?? [])\n            this.addSourcePart(sourcePart);\n    }\n\n    getSourcePartAt(position: number): ISourcePart {\n        let bestPart: ISourcePart = null;\n        for (const sourcePart of this._sourceParts) {\n            const start = sourcePart.textRange.start;\n            const end = start + sourcePart.textRange.length;\n            if (start <= position && end >= position) {\n                if (!bestPart || sourcePart.textRange.length <= bestPart.textRange.length)\n                    bestPart = sourcePart;\n            }\n        }\n        return bestPart;\n    }\n\n    getSourceParts(designItem: IDesignItem): ISourcePart[] {\n        return this._sourcePartsByDesignItem.get(designItem) ?? [];\n    }\n\n    getSourcePartPosition(designItem: IDesignItem, key: string): IStringPosition {\n        return this._sourcePartsByDesignItemAndKey.get(designItem)?.get(key)?.textRange;\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designItemDocumentPositionService/IDesignItemDocumentPositionService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\"\nimport { IStringPosition } from \"../htmlWriterService/IStringPosition.js\"\nimport { ISourcePart } from \"../sourceMapService/ISourcePart.js\"\n\nexport interface IDesignItemDocumentPositionService {\n    setPosition(designItem: IDesignItem, position: IStringPosition)\n    getPosition(designItem: IDesignItem): IStringPosition\n    clearSourceParts(): void\n    addSourcePart(sourcePart: ISourcePart): void\n    addSourceParts(sourceParts: ISourcePart[]): void\n    getSourcePartAt(position: number): ISourcePart\n    getSourceParts(designItem: IDesignItem): ISourcePart[]\n    getSourcePartPosition(designItem: IDesignItem, key: string): IStringPosition\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designItemService/BaseCustomWebcomponentDesignItemService.ts",
    "content": "import { DesignItem } from \"../../item/DesignItem.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\nimport { DesignItemService } from \"./DesignItemService.js\";\r\n\r\nexport class BaseCustomWebcomponentDesignItemService extends DesignItemService {\r\n  override createDesignItem(node: Node, parsedNode: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): IDesignItem {\r\n    const di = new DesignItem(node, parsedNode, serviceContainer, instanceServiceContainer);\r\n    if (node instanceof (node.ownerDocument.defaultView ?? window).HTMLTemplateElement) {\r\n      requestAnimationFrame(() => {\r\n        let repeatCount = 1;\r\n        const attr = node.getAttribute('sample-repeat-count');\r\n        if (attr) {\r\n          repeatCount = parseInt(attr);\r\n        }\r\n        for (let n = 0; n < repeatCount; n++) {\r\n          let par: Element = node;\r\n          for (const n of node.content.childNodes) {\r\n            par = this.instanciateNode(par, n);\r\n          }\r\n        }\r\n      });\r\n    }\r\n    return di;\r\n  }\r\n\r\n  instanciateNode(parent: Element, node: Node, append = false): Element {\r\n    const nd = node.cloneNode(false);\r\n    if (append)\r\n      parent.appendChild(<Element>nd);\r\n    else {\r\n      parent.parentNode.insertBefore(nd, parent.nextSibling);\r\n      //parent.insertAdjacentElement('afterend', <Element>nd);\r\n    }\r\n    DesignItem.GetDesignItem(node).setView(<Element>nd);\r\n    for (const n of node.childNodes) {\r\n      this.instanciateNode(<Element>nd, n, true);\r\n    }\r\n    return <Element>nd;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designItemService/DesignItemService.ts",
    "content": "import { DesignItem, hideAtDesignTimeAttributeName, hideAtRunTimeAttributeName, lockAtDesignTimeAttributeName } from \"../../item/DesignItem.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\nimport { IDesignItemService } from \"./IDesignItemService.js\";\r\n\r\nexport class DesignItemService implements IDesignItemService {\r\n  createDesignItem(node: Node, parsedNode: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): IDesignItem {\r\n    return new DesignItem(node, parsedNode, serviceContainer, instanceServiceContainer);\r\n  }\r\n\r\n  handleSpecialAttributes(attributeName: string, designItem: IDesignItem) {\r\n    if (attributeName == hideAtDesignTimeAttributeName) {\r\n      if (designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).HTMLElement || designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).SVGElement) {\r\n        if (!designItem.hasAttribute(hideAtDesignTimeAttributeName))\r\n          designItem.element.style.display = <any>designItem.getStyle('display') ?? \"\";\r\n        else\r\n          designItem.element.style.display = 'none';\r\n      }\r\n    } else if (attributeName == hideAtRunTimeAttributeName) {\r\n      if (designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).HTMLElement || designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).SVGElement) {\r\n        if (!designItem.hasAttribute(hideAtRunTimeAttributeName))\r\n          designItem.element.style.opacity = <any>designItem.getStyle('opacity') ?? \"\";\r\n        else\r\n          designItem.element.style.opacity = '0.3';\r\n      }\r\n    } else if (attributeName == lockAtDesignTimeAttributeName) {\r\n      if (designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).HTMLElement || designItem.element instanceof (designItem.node.ownerDocument.defaultView ?? window).SVGElement) {\r\n        if (!designItem.hasAttribute(lockAtDesignTimeAttributeName))\r\n          requestAnimationFrame(() => ((<HTMLElement>designItem.element).style.pointerEvents = 'auto'));\r\n        else\r\n          designItem.element.style.pointerEvents = 'none';\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designItemService/IDesignItemService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { ServiceContainer } from \"../ServiceContainer.js\";\r\n\r\nexport interface IDesignItemService {\r\n    createDesignItem(node: Node, parsedNode: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): IDesignItem; \r\n    finishedDesignItem?(designItem: IDesignItem); \r\n    handleSpecialAttributes(attributeName: string, designItem: IDesignItem): void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/designerAddons/IDesignerAddonJson.ts",
    "content": "export interface IDesignerAddonJson {\r\n    services: Record<string, string>\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/dragDropService/DragDropService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { DesignItem } from '../../item/DesignItem.js';\r\nimport { IDragDropService } from \"./IDragDropService.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IPlacementService } from \"../placementService/IPlacementService.js\";\r\nimport { IElementDefinition } from \"../elementsService/IElementDefinition.js\";\r\nimport { ExtensionType } from \"../../widgets/designerView/extensions/ExtensionType.js\";\r\nimport { dragDropFormatNameElementDefinition } from \"../../../Constants.js\";\r\n\r\nexport class DragDropService implements IDragDropService {\r\n  private _dragOverExtensionItem: IDesignItem;\r\n  private _oldX: number;\r\n  private _oldY: number;\r\n  private _currentDragDropFormatNameElementDefinition: string;\r\n\r\n  constructor() {\r\n    window.addEventListener(\"dragstart\", (e) => {\r\n      const dt = e.dataTransfer;\r\n      if (!dt) return;\r\n      const origSetData = dt.setData.bind(dt);\r\n      dt.setData = (type, value) => {\r\n        if (type == dragDropFormatNameElementDefinition)\r\n          this._currentDragDropFormatNameElementDefinition = value;\r\n        return origSetData(type, value);\r\n      };\r\n    }, true); // <-- use capture phase!\r\n    window.addEventListener(\"dragend\", (e) => {\r\n      this._currentDragDropFormatNameElementDefinition = null;\r\n    }, true); // <-- use capture phase!\r\n  }\r\n\r\n  public dragEnter(designerCanvas: IDesignerCanvas, event: DragEvent) {\r\n  }\r\n\r\n  public dragLeave(designerCanvas: IDesignerCanvas, event: DragEvent) {\r\n    if (this._dragOverExtensionItem) {\r\n      designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerExternalDragOverAndCanBeEntered);\r\n      this._dragOverExtensionItem = null;\r\n    }\r\n  }\r\n\r\n  public async dragOver(designerCanvas: IDesignerCanvas, event: DragEvent) {\r\n    if (designerCanvas.readOnly) {\r\n      event.dataTransfer.dropEffect = 'none';\r\n      return;\r\n    }\r\n    let di: IDesignItem = null;\r\n    let transferData = event.dataTransfer.getData(dragDropFormatNameElementDefinition);\r\n    if (!transferData) {\r\n      transferData = this._currentDragDropFormatNameElementDefinition;\r\n    }\r\n    if (transferData) {\r\n      const elementDefinition = <IElementDefinition>JSON.parse(transferData);\r\n      if (elementDefinition) {\r\n        di = await designerCanvas.serviceContainer.forSomeServicesTillResult(\"instanceService\", (service) => service.getElement(elementDefinition, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer));\r\n      }\r\n    }\r\n\r\n    let [newContainer] = this.getPossibleContainerForDragDrop(designerCanvas, event, di ? [di] : null);\r\n    if (!newContainer)\r\n      newContainer = designerCanvas.rootDesignItem;\r\n\r\n    if (this._dragOverExtensionItem != newContainer) {\r\n      designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerExternalDragOverAndCanBeEntered);\r\n      designerCanvas.extensionManager.applyExtension(newContainer, ExtensionType.ContainerExternalDragOverAndCanBeEntered, event);\r\n      this._dragOverExtensionItem = newContainer;\r\n    } else {\r\n      if (event.x != this._oldX && event.y != this._oldY) {\r\n        this._oldX = event.x;\r\n        this._oldY = event.y;\r\n        designerCanvas.extensionManager.refreshExtension(newContainer, ExtensionType.ContainerExternalDragOverAndCanBeEntered, event);\r\n      }\r\n    }\r\n  }\r\n\r\n  public async drop(designerCanvas: IDesignerCanvas, event: DragEvent) {\r\n    if (this._dragOverExtensionItem) {\r\n      designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerExternalDragOverAndCanBeEntered);\r\n      this._dragOverExtensionItem = null;\r\n    }\r\n\r\n    const transferData = event.dataTransfer.getData(dragDropFormatNameElementDefinition);\r\n    const elementDefinition = <IElementDefinition>JSON.parse(transferData);\r\n    const di = await designerCanvas.serviceContainer.forSomeServicesTillResult(\"instanceService\", (service) => service.getElement(elementDefinition, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer));\r\n\r\n    let [newContainer] = this.getPossibleContainerForDragDrop(designerCanvas, event, [di]);\r\n    if (!newContainer)\r\n      newContainer = designerCanvas.rootDesignItem;\r\n\r\n    const grp = di.openGroup(\"Insert of &lt;\" + di.name + \"&gt;\");\r\n    const containerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainer, newContainer.getComputedStyle(), di))\r\n    containerService.enterContainer(newContainer, [di], 'drop');\r\n\r\n    const containerPos = designerCanvas.getNormalizedElementCoordinates(newContainer.element);\r\n    const evCoord = designerCanvas.getNormalizedEventCoordinates(event);\r\n    const pos = { x: evCoord.x - containerPos.x, y: evCoord.y - containerPos.y };\r\n\r\n    let offset = { x: 0, y: 0 };\r\n    if (elementDefinition.mouseOffset)\r\n      offset = elementDefinition.mouseOffset;\r\n    containerService.place(event, designerCanvas, newContainer, offset, { x: 0, y: 0 }, pos, [di]);\r\n    containerService.finishPlace(event, designerCanvas, newContainer, offset, { x: 0, y: 0 }, pos, [di]);\r\n    requestAnimationFrame(() => {\r\n      designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([di], event);\r\n      grp.commit();\r\n    });\r\n  }\r\n\r\n  public getPossibleContainerForDragDrop(designerCanvas: IDesignerCanvas, event: DragEvent, designItems?: IDesignItem[]): [newContainerElementDesignItem: IDesignItem, newContainerService: IPlacementService] {\r\n    let newContainerElementDesignItem: IDesignItem = null;\r\n    let newContainerService: IPlacementService = null;\r\n\r\n    const elementsFromPoint = designerCanvas.elementsFromPoint(event.clientX, event.clientY);\r\n    for (let e of elementsFromPoint) {\r\n      if (e == designerCanvas.rootDesignItem.element) {\r\n        newContainerElementDesignItem = designerCanvas.rootDesignItem;\r\n        const containerStyle = getComputedStyle(newContainerElementDesignItem.element);\r\n        newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle));\r\n        break;\r\n      } else if (false) {\r\n        //check we don't try to move a item over one of its children..\r\n      } else {\r\n        newContainerElementDesignItem = DesignItem.GetOrCreateDesignItem(e, e, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        const containerStyle = getComputedStyle(newContainerElementDesignItem.element);\r\n        newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle));\r\n        if (newContainerService) {\r\n          //TODO: Maybe the check for SVG Element should be in \"canEnterByDrop\"?\r\n          if (designItems && newContainerService.canEnter(newContainerElementDesignItem, designItems) && !(newContainerElementDesignItem.element instanceof newContainerElementDesignItem.window.SVGElement)) {\r\n            break;\r\n          } else if (!designItems && newContainerService.isEnterableContainer(newContainerElementDesignItem) && !(newContainerElementDesignItem.element instanceof newContainerElementDesignItem.window.SVGElement)) {\r\n            break;\r\n          } else {\r\n            newContainerElementDesignItem = null;\r\n            newContainerService = null;\r\n            continue;\r\n          }\r\n        }\r\n      }\r\n    }\r\n    return [newContainerElementDesignItem, newContainerService];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/dragDropService/ExternalDragDropService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { IExternalDragDropService } from \"./IExternalDragDropService.js\";\r\nimport { DesignItem } from '../../item/DesignItem.js';\r\nimport { InsertAction } from \"../undoService/transactionItems/InsertAction.js\";\r\n\r\nexport class ExternalDragDropService implements IExternalDragDropService {\r\n\r\n  public dragOver(designerCanvas: IDesignerCanvas, event: DragEvent): 'none' | 'copy' | 'link' | 'move' {\r\n    if (designerCanvas.readOnly)\r\n          return 'none';\r\n    if (event.dataTransfer.items[0].type.startsWith('image/'))\r\n      return 'copy';\r\n    return 'none';\r\n  }\r\n\r\n  async drop(designerCanvas: IDesignerCanvas, event: DragEvent) {\r\n    if (event.dataTransfer.files[0].type.startsWith('image/')) {\r\n      let di = await DesignItem.createDesignItemFromImageBlob(designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer, event.dataTransfer.files[0]);\r\n      let grp = di.openGroup(\"Insert of &lt;img&gt;\");\r\n      di.setStyle('position', 'absolute')\r\n      const coord = designerCanvas.getNormalizedEventCoordinates(event);\r\n      di.setStyle('top', coord.y + 'px')\r\n      di.setStyle('left', coord.x + 'px')\r\n      designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n      grp.commit();\r\n      requestAnimationFrame(() => designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([di]));\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/dragDropService/IDragDropService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { IPlacementService } from \"../placementService/IPlacementService.js\";\r\n\r\nexport interface IDragDropService {\r\n  dragEnter(designerCanvas: IDesignerCanvas, event: DragEvent);\r\n  dragLeave(designerCanvas: IDesignerCanvas, event: DragEvent);\r\n  dragOver(designerCanvas: IDesignerCanvas, event: DragEvent);\r\n  drop(designerCanvas: IDesignerCanvas, event: DragEvent);\r\n  getPossibleContainerForDragDrop(designerCanvas: IDesignerCanvas, event: DragEvent, designItems?: IDesignItem[]): [newContainerElementDesignItem: IDesignItem, newContainerService: IPlacementService]\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/dragDropService/IExternalDragDropService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\n\r\nexport interface IExternalDragDropService {\r\n  dragOver(designerCanvas: IDesignerCanvas, event: DragEvent): 'none' | 'copy' | 'link' | 'move';\r\n  drop(designerCanvas: IDesignerCanvas, event: DragEvent);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/dragDropService/IPropertyGridDragDropService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IProperty } from \"../propertiesService/IProperty.js\";\r\n\r\nexport interface IPropertyGridDragDropService {\r\n  dragOverOnProperty?(event: DragEvent, property: IProperty, designItems: IDesignItem[]): 'none' | 'copy' | 'link' | 'move';\r\n  dropOnProperty?(event: DragEvent, property: IProperty, dropObject: any, designItems: IDesignItem[]): void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementAtPointService/ElementAtPointService.ts",
    "content": "import { IElementAtPointService } from './IElementAtPointService.js';\r\nimport { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\n\r\nexport class ElementAtPointService implements IElementAtPointService {\r\n  getElementAtPoint(designerCanvas: IDesignerCanvas, point: IPoint) {\r\n    return designerCanvas.getElementAtPoint(point);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementAtPointService/IElementAtPointService.ts",
    "content": "import { IService } from '../IService.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { IPoint } from '../../../interfaces/IPoint.js';\r\n\r\nexport interface IElementAtPointService extends IService {\r\n  getElementAtPoint(designerView: IDesignerCanvas, point: IPoint);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementInteractionService/IElementInteractionService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { IService } from '../IService.js';\r\n\r\nexport interface IElementInteractionService extends IService {\r\n    stopEventHandling(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) : boolean;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/IElementDefinition.ts",
    "content": "import { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { IBinding } from '../../item/IBinding.js';\r\n\r\nexport interface IElementDefinition {\r\n  tag: string;\r\n  /**\r\n   * A path or a Object URL to an image\r\n   */\r\n  icon?: string;\r\n  /**\r\n   * A HTML String wich is used in the Palette\r\n   */\r\n  displayHtml?: string;\r\n  name?: string;\r\n  description?: string;\r\n  import?: string;\r\n  className?: string;\r\n  type?: string;\r\n  defaultContent?: any;\r\n  defaultAttributes?: { [key: string]: string; };\r\n  defaultStyles?: { [key: string]: string; };\r\n  defaultWidth?: string;\r\n  defaultHeight?: string;\r\n  ghostElement?: Element;\r\n  /**\r\n   * Name of the Tool activated when selecting the element.\r\n   * If none, the DrawElementTool is used\r\n   */\r\n  tool?: string;\r\n  defaultBinding?: IBinding;\r\n\r\n  /**\r\n   * Offset of mouse pointer when dragged\r\n   */\r\n  mouseOffset?: IPoint\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/IElementsJson.ts",
    "content": "import { IElementDefinition } from './IElementDefinition.js';\r\n\r\nexport interface IElementsJson {\r\n    \"imports\"?: string[],\r\n    \"elements\": (string | IElementDefinition)[]\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/IElementsService.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\nimport { IService } from '../IService.js';\nimport { IElementDefinition } from './IElementDefinition.js';\n\nexport interface IElementsService extends IService {\n    readonly name: string\n    /** Optional event emitted when the available element definitions changed and palette views should reload this service. */\n    readonly onElementsChanged?: TypedEvent<void>\n    getElements(): Promise<IElementDefinition[]>\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/JsonFileElementsService.ts",
    "content": "import { IElementsService } from './IElementsService.js';\r\nimport { IElementsJson } from './IElementsJson.js';\r\nimport { IElementDefinition } from './IElementDefinition.js';\r\nimport { LazyLoader } from '@node-projects/base-custom-webcomponent';\r\n\r\n// Reads a Json File and provides the Elements listed there\r\nexport class JsonFileElementsService implements IElementsService {\r\n  private _name: string;\r\n  get name() { return this._name; }\r\n\r\n  private _elementList: IElementDefinition[];\r\n  private _resolveStored: ((value: IElementDefinition[]) => void)[];\r\n  private _rejectStored: ((errorCode: number) => void)[];\r\n\r\n  public getElements(): Promise<IElementDefinition[]> {\r\n    if (this._elementList)\r\n      return Promise.resolve(this._elementList);\r\n    if (!this._resolveStored) {\r\n      this._resolveStored = [];\r\n      this._rejectStored = [];\r\n    }\r\n    return new Promise((resolve, reject) => { this._resolveStored.push(resolve); this._rejectStored.push(reject); });\r\n  }\r\n\r\n  constructor(name: string, file: string|URL) {\r\n    this._name = name;\r\n    LazyLoader.LoadText(file.toString()).then(data => {\r\n      let parsed = JSON.parse(data) as IElementsJson;\r\n      this._elementList = [];\r\n      parsed.elements.forEach(element => {\r\n        if (this.isIElementDefintion(element))\r\n          this._elementList.push(element)\r\n        else\r\n          this._elementList.push({ tag: element })\r\n      });\r\n      if (this._resolveStored) {\r\n        this._resolveStored.forEach(x => x(this._elementList));\r\n        this._resolveStored = null;\r\n        this._rejectStored = null;\r\n      }\r\n    }).catch(err => {\r\n      if (this._rejectStored) {\r\n        this._rejectStored.forEach(x => x(err));\r\n        this._resolveStored = null;\r\n        this._rejectStored = null;\r\n      }\r\n    });\r\n  }\r\n\r\n  private isIElementDefintion(object: string | IElementDefinition): object is IElementDefinition {\r\n    return object != null && (<IElementDefinition>object).tag != null;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/PreDefinedElementsService.ts",
    "content": "import { IElementsService } from './IElementsService.js';\r\nimport { IElementsJson } from './IElementsJson.js';\r\nimport { IElementDefinition } from './IElementDefinition.js';\r\n\r\nexport class PreDefinedElementsService implements IElementsService {\r\n  private _name: string;\r\n  get name() { return this._name; }\r\n\r\n  private _elementList: IElementDefinition[];\r\n\r\n  public getElements(): Promise<IElementDefinition[]> {\r\n    return Promise.resolve(this._elementList);\r\n  }\r\n\r\n  constructor(name: string, data: IElementsJson) {\r\n    this._name = name;\r\n\r\n    this._elementList = [];\r\n    data.elements.forEach(element => {\r\n      if (this.isIElementDefintion(element))\r\n        this._elementList.push(element)\r\n      else\r\n        this._elementList.push({ tag: element })\r\n    });\r\n  }\r\n\r\n  private isIElementDefintion(object: string | IElementDefinition): object is IElementDefinition {\r\n    return object != null && (<IElementDefinition>object).tag != null;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/elementsService/WebcomponentManifestElementsService.ts",
    "content": "import { removeLeading, removeTrailing } from \"../../helper/Helper.js\";\r\nimport { IElementDefinition } from \"./IElementDefinition.js\";\r\nimport { IElementsService } from \"./IElementsService.js\";\r\n\r\nexport class WebcomponentManifestElementsService implements IElementsService {\r\n\r\n  private _name: string;\r\n  get name() { return this._name; }\r\n\r\n  private _importPrefix: string;\r\n  private _elementList: IElementDefinition[];\r\n  private _resolveStored: ((value: IElementDefinition[]) => void)[];\r\n  private _rejectStored: ((errorCode: number) => void)[];\r\n\r\n  constructor(name: string, importPrefix: string, manifest: any) {\r\n    this._name = name;\r\n    this._importPrefix = importPrefix\r\n    this._parseManifest(manifest);\r\n  }\r\n\r\n  private _parseManifest(manifest: any) {\r\n    this._elementList = [];\r\n    for (let m of manifest.modules) {\r\n      for (let e of m.exports) {\r\n        if (e.kind == 'custom-element-definition') {\r\n          let elDef: IElementDefinition = { tag: e.name, import: removeTrailing(this._importPrefix, '/') + '/' + removeLeading(m.path, '/'), defaultWidth: \"200px\", defaultHeight: \"200px\", className: e.declaration.name }\r\n          this._elementList.push(elDef);\r\n        }\r\n      }\r\n      for (let d of m.declarations) {\r\n        if (d.tagName) {\r\n          let elDef: IElementDefinition = { tag: d.tagName, import: removeTrailing(this._importPrefix, '/') + '/' + removeLeading(m.path, '/'), defaultWidth: \"200px\", defaultHeight: \"200px\", className: d.name }\r\n          this._elementList.push(elDef);\r\n        }\r\n      }\r\n      if (this._resolveStored) {\r\n        this._resolveStored.forEach(x => x(this._elementList));\r\n        this._resolveStored = null;\r\n        this._rejectStored = null;\r\n      }\r\n    }\r\n  }\r\n\r\n  async getElements(): Promise<IElementDefinition[]> {\r\n    if (this._elementList)\r\n      return Promise.resolve(this._elementList);\r\n    if (!this._resolveStored) {\r\n      this._resolveStored = [];\r\n      this._rejectStored = [];\r\n    }\r\n    return new Promise((resolve, reject) => { this._resolveStored.push(resolve); this._rejectStored.push(reject); });\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/eventsService/EventsService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IEvent } from \"./IEvent.js\";\r\nimport { IEventsService } from \"./IEventsService.js\";\r\n\r\nexport class EventsService implements IEventsService {\r\n\r\n    constructor() {\r\n    }\r\n\r\n    public static _windowEvents = [\r\n        { name: \"afterprint\", propertyName: \"onafterprint\", eventObjectName: \"Event\" },\r\n        { name: \"beforeprint\", propertyName: \"onbeforeprint\", eventObjectName: \"Event\" },\r\n        { name: \"beforeunload\", propertyName: \"onbeforeunload\", eventObjectName: \"Event\" },\r\n        { name: \"error\", propertyName: \"onerror\", eventObjectName: \"Event\" },\r\n        { name: \"load\", propertyName: \"onload\", eventObjectName: \"Event\" },\r\n        { name: \"hashchange\", propertyName: \"onhashchange\", eventObjectName: \"Event\" },\r\n        { name: \"message\", propertyName: \"onmessage\", eventObjectName: \"Event\" },\r\n        { name: \"offline\", propertyName: \"onoffline\", eventObjectName: \"Event\" },\r\n        { name: \"online\", propertyName: \"ononline\", eventObjectName: \"Event\" },\r\n        { name: \"pageshow\", propertyName: \"onpageshow\", eventObjectName: \"Event\" },\r\n        { name: \"popstate\", propertyName: \"onpopstate\", eventObjectName: \"Event\" },\r\n        { name: \"resize\", propertyName: \"onresize\", eventObjectName: \"Event\" },\r\n        { name: \"storage\", propertyName: \"onstorage\", eventObjectName: \"Event\" },\r\n        { name: \"unload\", propertyName: \"onunload\", eventObjectName: \"Event\" }\r\n    ]\r\n\r\n    public static _allElements: IEvent[] = [\r\n        { name: \"contextmenu\", propertyName: \"oncontextmenu\", eventObjectName: \"PointerEvent\" }\r\n    ]\r\n\r\n    public static _focusableEvents = [\r\n        { name: \"blur\", propertyName: \"onblur\", eventObjectName: \"FocusEvent\" },\r\n        { name: \"focus\", propertyName: \"onfocus\", eventObjectName: \"FocusEvent\" },\r\n        { name: \"keydown\", propertyName: \"onkeydown\", eventObjectName: \"KeyboardEvent\" },\r\n        { name: \"keyup\", propertyName: \"onkeyup\", eventObjectName: \"KeyboardEvent\" }\r\n    ]\r\n\r\n    public static _simpleForm = [\r\n        { name: \"input\", propertyName: \"oninput\", eventObjectName: \"InputEvent\" },\r\n        { name: \"change\", propertyName: \"onchange\", eventObjectName: \"Event\" }\r\n    ]\r\n\r\n    public static _form = [\r\n        { name: \"beforeinput\", propertyName: \"onbeforeinput\", eventObjectName: \"InputEvent\" },\r\n        { name: \"invalid\", propertyName: \"oninvalid\", eventObjectName: \"Event\" },\r\n        { name: \"reset\", propertyName: \"onreset\", eventObjectName: \"Event\" },\r\n        { name: \"select\", propertyName: \"onselect\", eventObjectName: \"Event\" },\r\n        { name: \"submit\", propertyName: \"onsubmit\", eventObjectName: \"SubmitEvent\" }\r\n    ]\r\n\r\n    public static _simpleMouseEvents: IEvent[] = [\r\n        { name: \"click\", propertyName: \"onclick\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"dblclick\", propertyName: \"ondblclick\", eventObjectName: \"MouseEvent\" },\r\n        { name: \"wheel\", propertyName: \"onwheel\", eventObjectName: \"WheelEvent\" },\r\n        { name: \"scroll\", propertyName: \"onscroll\", eventObjectName: \"Event\" },\r\n    ]\r\n\r\n    public static _mouseEvents = [\r\n        { name: \"mousedown\", propertyName: \"onmousedown\", eventObjectName: \"MouseEvent\" },\r\n        { name: \"mouseup\", propertyName: \"onmouseup\", eventObjectName: \"MouseEvent\" },\r\n        { name: \"mousemove\", propertyName: \"onmousemove\", eventObjectName: \"MouseEvent\" },\r\n        { name: \"mouseover\", propertyName: \"onmouseover\", eventObjectName: \"MouseEvent\" },\r\n        { name: \"mouseout\", propertyName: \"onmouseout\", eventObjectName: \"MouseEvent\" }\r\n    ]\r\n\r\n    public static _pointerEvents: IEvent[] = [\r\n        { name: \"pointerdown\", propertyName: \"onpointerdown\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointerup\", propertyName: \"onpointerup\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointerenter\", propertyName: \"onpointerenter\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointerleave\", propertyName: \"onpointerleave\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointermove\", propertyName: \"onpointermove\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointerover\", propertyName: \"onpointerover\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointerout\", propertyName: \"onpointerout\", eventObjectName: \"PointerEvent\" },\r\n        { name: \"pointercancel\", propertyName: \"onpointercancel\", eventObjectName: \"PointerEvent\" }\r\n    ]\r\n\r\n    public static _touchEvents: IEvent[] = [\r\n        { name: \"touchstart\", propertyName: \"ontouchstart\", eventObjectName: \"TouchEvent\" },\r\n        { name: \"touchend\", propertyName: \"ontouchend\", eventObjectName: \"TouchEvent\" },\r\n        { name: \"touchmove\", propertyName: \"ontouchmove\", eventObjectName: \"TouchEvent\" },\r\n        { name: \"touchcancel\", propertyName: \"ontouchcancel\", eventObjectName: \"TouchEvent\" }\r\n    ]\r\n\r\n    public static _dragEvents = [\r\n        { name: \"drag\", propertyName: \"ondrag\", eventObjectName: \"DragEvent\" },\r\n        { name: \"dragend\", propertyName: \"ondragend\", eventObjectName: \"DragEvent\" },\r\n        { name: \"dragenter\", propertyName: \"ondragenter\", eventObjectName: \"DragEvent\" },\r\n        { name: \"dragleave\", propertyName: \"ondragleave\", eventObjectName: \"DragEvent\" },\r\n        { name: \"dragover\", propertyName: \"ondragover\", eventObjectName: \"DragEvent\" },\r\n        { name: \"dragstart\", propertyName: \"ondragstart\", eventObjectName: \"DragEvent\" },\r\n        { name: \"drop\", propertyName: \"ondrop\", eventObjectName: \"DragEvent\" }\r\n    ]\r\n\r\n    public static _clipboard = [\r\n        { name: \"copy\", propertyName: \"oncopy\", eventObjectName: \"ClipboardEvent\" },\r\n        { name: \"cut\", propertyName: \"oncut\", eventObjectName: \"ClipboardEvent\" },\r\n        { name: \"paste\", propertyName: \"onpaste\", eventObjectName: \"ClipboardEvent\" }\r\n    ]\r\n\r\n    public static _details = [\r\n        { name: \"toggle\", propertyName: \"ontoggle\", eventObjectName: \"Event\" }\r\n    ]\r\n\r\n    public static _allEvents = [...EventsService._windowEvents, ...EventsService._form, ...EventsService._simpleMouseEvents, ...EventsService._mouseEvents, ...EventsService._pointerEvents, ...EventsService._touchEvents, ...EventsService._allElements, ...EventsService._focusableEvents, ...EventsService._dragEvents, ...EventsService._clipboard, ...EventsService._dragEvents];\r\n\r\n    isHandledElementFromEventsService(designItem: IDesignItem): boolean {\r\n        return true;\r\n    }\r\n\r\n    public getPossibleEvents(designItem: IDesignItem): IEvent[] {\r\n        if (designItem.element instanceof designItem.window.HTMLInputElement ||\r\n            designItem.element instanceof designItem.window.HTMLTextAreaElement ||\r\n            designItem.element instanceof designItem.window.HTMLSelectElement) {\r\n            let events: IEvent[] = [...EventsService._simpleForm, ...EventsService._simpleMouseEvents, ...EventsService._form, ...EventsService._pointerEvents, ...EventsService._allElements, ...EventsService._focusableEvents];\r\n            return events;\r\n        }\r\n        let events: IEvent[] = [...EventsService._simpleMouseEvents, ...EventsService._pointerEvents, ...EventsService._allElements, ...EventsService._focusableEvents];\r\n        return events;\r\n    }\r\n\r\n    public getEvent(designItem: IDesignItem, name: string): IEvent {\r\n        let evt = EventsService._allEvents.find(x => x.name == name);\r\n        return evt ?? { name, propertyName: 'on' + name, eventObjectName: 'Event' };\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/eventsService/IEvent.ts",
    "content": "export interface IEvent {\r\n  name: string;\r\n  propertyName?: string;\r\n  eventObjectName?: string;\r\n  description?: string;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/eventsService/IEventsService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IEvent } from \"./IEvent.js\";\r\n\r\nexport interface IEventsService {\r\n    isHandledElementFromEventsService(designItem: IDesignItem): boolean;\r\n    getPossibleEvents(designItem: IDesignItem): IEvent[];\r\n    getEvent(designItem: IDesignItem, name: string): IEvent;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/eventsService/WebcomponentManifestEventsService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { EventsService } from \"./EventsService.js\";\r\nimport { IEvent } from \"./IEvent.js\";\r\n\r\nexport class WebcomponentManifestEventsService extends EventsService {\r\n\r\n  override isHandledElementFromEventsService(designItem: IDesignItem): boolean {\r\n    return this.__eventsList?.[designItem.name] != null;\r\n  }\r\n\r\n  override getPossibleEvents(designItem: IDesignItem): IEvent[] {\r\n    return [...this.__eventsList[designItem.name], ...EventsService._simpleMouseEvents, ...EventsService._pointerEvents, ...EventsService._allElements, ...EventsService._focusableEvents];\r\n  }\r\n\r\n  override getEvent(designItem: IDesignItem, name: string): IEvent {\r\n    let evt = this.getPossibleEvents(designItem).find(x => x.name == name);\r\n    return evt ?? { name, propertyName: 'on' + name, eventObjectName: 'Event' };\r\n  }\r\n\r\n  private __eventsList: Record<string, IEvent[]>;\r\n\r\n  constructor(name: string, manifest: any) {\r\n    super();\r\n    this._parseManifest(manifest);\r\n  }\r\n\r\n  private _parseManifest(manifest) {\r\n    this.__eventsList = {};\r\n    let declarations = [];\r\n    for (let m of manifest.modules) {\r\n      if (m.declarations)\r\n        declarations.push(...m.declarations);\r\n    }\r\n    for (let m of manifest.modules) {\r\n      for (let e of m.exports) {\r\n        if (e.kind == 'custom-element-definition') {\r\n          let declaration = declarations.find(x => x.name == e.declaration.name);\r\n          if (declaration) {\r\n            if (declaration.events) {\r\n              let events: IEvent[] = [];\r\n              for (let e of declaration.events) {\r\n                events.push({ name: e.name })\r\n              }\r\n              if (events.length)\r\n                this.__eventsList[e.name] = events;\r\n            }\r\n          } else {\r\n            console.warn('declaration for ' + e.declaration.name + ' not found', manifest);\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlParserService/DefaultHtmlParserService.ts",
    "content": "import { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { ServiceContainer } from '../ServiceContainer.js';\r\nimport { IHtmlParserService } from './IHtmlParserService.js';\r\nimport { DesignItem } from '../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { isFirefox } from '../../helper/Browser.js';\r\nimport { IndentedTextWriter } from '../../helper/IndentedTextWriter.js';\r\n\r\nexport class DefaultHtmlParserService implements IHtmlParserService {\r\n  async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\r\n    let doc: Document;\r\n    //@ts-ignore\r\n    if (Document.parseHTMLUnsafe && !isFirefox) {\r\n      //@ts-ignore\r\n      doc = Document.parseHTMLUnsafe(html);\r\n    } else {\r\n      //@ts-ignore\r\n      doc = new DOMParser().parseFromString(html, 'text/html', { includeShadowRoots: true });\r\n    }\r\n\r\n    const headNodes = this.createDesignItems(doc.head.childNodes, serviceContainer, instanceServiceContainer);\r\n    const bodyNodes = this.createDesignItems(doc.body.childNodes, serviceContainer, instanceServiceContainer);\r\n    const designItems = [...headNodes, ...bodyNodes];\r\n\r\n    if (!parseSnippet && instanceServiceContainer.designItemDocumentPositionService && designItems.length) {\r\n      serviceContainer.htmlWriterService.write(new IndentedTextWriter(), designItems, true, true);\r\n    }\r\n\r\n    return designItems;\r\n  }\r\n\r\n  public createDesignItems(elements: NodeListOf<ChildNode> | Node[] | HTMLCollection | HTMLElement[], serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer) {\r\n    let res: IDesignItem[] = [];\r\n    for (let el of elements) {\r\n      res.push(this._createDesignItemsRecursive(el, serviceContainer, instanceServiceContainer));\r\n    }\r\n    return res;\r\n  }\r\n\r\n  private _createDesignItemsRecursive(node: Node, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer) {\r\n    return DesignItem.createDesignItemFromInstance(node, serviceContainer, instanceServiceContainer);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlParserService/IHtmlParserService.ts",
    "content": "import { IService } from '../IService.js';\r\nimport type { ServiceContainer } from '../ServiceContainer.js';\r\nimport type { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\nexport interface IHtmlParserService extends IService {\r\n  parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean, positionOffset?: Number): Promise<IDesignItem[]>\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/AbstractHtmlWriterService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IHtmlWriterService } from './IHtmlWriterService.js';\r\nimport { IHtmlWriterOptions } from './IHtmlWriterOptions.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { CssCombiner } from '../../helper/CssCombiner.js';\nimport { PropertiesHelper } from '../propertiesService/services/PropertiesHelper.js';\nimport { ITextWriter } from '../../helper/ITextWriter.js';\nimport { IStringPosition } from './IStringPosition.js';\nimport { appendCssImportant } from '../../helper/CssImportant.js';\n\r\nexport abstract class AbstractHtmlWriterService implements IHtmlWriterService {\r\n\r\n  public options: IHtmlWriterOptions;\r\n\r\n  constructor(options?: IHtmlWriterOptions) {\r\n    this.options = options ?? {};\r\n    this.options.beautifyOutput ??= true;\r\n    this.options.compressCssToShorthandProperties ??= true;\r\n    this.options.writeDesignerProperties ??= true;\r\n    this.options.parseJsonInAttributes ??= true;\r\n    this.options.jsonWriteMode ??= 'min';\r\n  }\r\n\r\n  abstract write(indentedTextWriter: ITextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean, updatePositions?: boolean);\r\n\r\n  writeAttributes(indentedTextWriter: ITextWriter, designItem: IDesignItem, updatePositions: boolean = false) {\n    if (designItem.hasAttributes) {\n      for (const a of designItem.attributes()) {\n        const attributeStart = indentedTextWriter.position;\n        indentedTextWriter.write(' ');\n        if (typeof a[1] === 'string') {\n          if (a[1] === \"\")\n            indentedTextWriter.write(a[0]);\n          else {\n            if (this.options.parseJsonInAttributes &&\r\n              (\r\n                (a[1].startsWith('{') && !a[1].startsWith('{{') && a[1].endsWith('}')) ||\r\n                (a[1].startsWith('[') && !a[1].startsWith('[[') && a[1].endsWith(']')))\r\n            ) {\r\n              try {\r\n                let j = JSON.parse(a[1]);\r\n                let txt;\r\n                if (this.options.jsonWriteMode == 'beauty')\n                  txt = JSON.stringify(j, null, 2);\n                else\n                  txt = JSON.stringify(j);\n                const writtenValue = DomConverter.normalizeAttributeValue(txt, true);\n                this._writeAttributeWithValue(indentedTextWriter, designItem, a[0], writtenValue, '\\'', updatePositions);\n                continue;\n              }\n              catch { }\n            }\n            const content = DomConverter.normalizeAttributeValue(a[1]);\n            if (content.indexOf('&quot;')) {\n              const contentSingle = DomConverter.normalizeAttributeValue(a[1], true);\n              if (contentSingle.length < content.length)\n                this._writeAttributeWithValue(indentedTextWriter, designItem, a[0], contentSingle, '\\'', updatePositions);\n              else\n                this._writeAttributeWithValue(indentedTextWriter, designItem, a[0], content, '\"', updatePositions);\n            } else\n              this._writeAttributeWithValue(indentedTextWriter, designItem, a[0], content, '\"', updatePositions);\n          }\n        }\n        else if (!a[1])\n          indentedTextWriter.write(a[0]);\n        else {\n          //TODO: writing of bindings, really ???\n        }\n        if (updatePositions)\n          this._addAttributeSourcePart(designItem, a[0], { start: attributeStart + 1, length: indentedTextWriter.position - attributeStart - 1 });\n      }\n    }\n  }\n\n  private _writeAttributeWithValue(indentedTextWriter: ITextWriter, designItem: IDesignItem, attributeName: string, writtenValue: string, quote: '\"' | '\\'', updatePositions: boolean) {\n    indentedTextWriter.write(attributeName + '=' + quote);\n    const valueStart = indentedTextWriter.position;\n    indentedTextWriter.write(writtenValue);\n    const valueRange = { start: valueStart, length: indentedTextWriter.position - valueStart };\n    indentedTextWriter.write(quote);\n\n    if (!updatePositions)\n      return;\n\n    const positionService = designItem.instanceServiceContainer.designItemDocumentPositionService;\n    positionService.addSourcePart({\n      designItem,\n      kind: 'attribute-value',\n      key: `attribute:${attributeName}/value`,\n      name: attributeName,\n      textRange: valueRange\n    });\n\n    const context = { designItem, sourceKind: 'attribute' as const, name: attributeName, value: writtenValue, valueTextRange: valueRange };\n    for (const provider of designItem.serviceContainer.sourceMapProviders) {\n      if (provider.canMap(context))\n        positionService.addSourceParts(provider.map(context));\n    }\n  }\n\n  private _addAttributeSourcePart(designItem: IDesignItem, attributeName: string, textRange: IStringPosition) {\n    designItem.instanceServiceContainer.designItemDocumentPositionService.addSourcePart({\n      designItem,\n      kind: 'attribute',\n      key: `attribute:${attributeName}`,\n      name: attributeName,\n      textRange\n    });\n  }\n\r\n  writeStyles(indentedTextWriter: ITextWriter, designItem: IDesignItem) {\r\n    if (designItem.hasStyles) {\r\n      indentedTextWriter.write(' style=\"');\r\n      const styleEntries = [...designItem.styles()];\n      const hasImportantStyle = styleEntries.some(x => designItem.isStyleImportant(x[0]));\n      let styles: Iterable<[name: string, value: string]> = styleEntries;\n      if (this.options.compressCssToShorthandProperties && !hasImportantStyle)\n        styles = CssCombiner.combine(new Map(styleEntries));\n      for (const s of styles) {\n        if (s[0]) {\n          const value = DomConverter.normalizeAttributeValue(appendCssImportant(s[1], designItem.isStyleImportant(s[0])));\n          if (s[0].startsWith('--'))\n            indentedTextWriter.write(s[0] + ':' + value + ';');\n          else\n            indentedTextWriter.write(PropertiesHelper.camelToDashCase(s[0]) + ':' + value + ';');\n        }\n      }\n      indentedTextWriter.write('\"');\r\n    }\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/FormatingHtmlWriterService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IHtmlWriterService } from './IHtmlWriterService.js';\r\nimport { IHtmlWriterOptions } from './IHtmlWriterOptions.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { IndentedTextWriter } from '../../helper/IndentedTextWriter.js';\r\nimport { CssCombiner } from '../../helper/CssCombiner.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { IStringPosition } from './IStringPosition.js';\nimport { PropertiesHelper } from '../propertiesService/services/PropertiesHelper.js';\nimport { ElementDisplayType, getElementDisplaytype } from '../../helper/ElementHelper.js';\nimport { appendCssImportant } from '../../helper/CssImportant.js';\n\r\nenum ElementContainerType {\r\n  inline,\r\n  block,\r\n  complex\r\n}\r\n\r\ninterface IWriteContext {\r\n  options: IHtmlWriterOptions;\r\n  indentedTextWriter: IndentedTextWriter;\r\n  lastElementDisplayType: ElementDisplayType | null;\r\n  containerDisplayType: ElementContainerType;\r\n  designItemsAssignmentList?: Map<IDesignItem, IStringPosition>;\r\n}\r\n\r\n// const defaultDisplayNoneContainerDisplayType: ElementContainerType = ElementContainerType.complex;\r\n\r\nexport class FormatingHtmlWriterService implements IHtmlWriterService {\r\n  public options: IHtmlWriterOptions;\r\n\r\n  constructor(options?: IHtmlWriterOptions) {\r\n    this.options = options ?? {};\r\n    this.options.beautifyOutput ??= true;\r\n    this.options.compressCssToShorthandProperties ??= true;\r\n    this.options.writeDesignerProperties ??= true;\r\n    this.options.parseJsonInAttributes ??= true;\r\n    this.options.jsonWriteMode ??= 'min';\r\n  }\r\n\r\n  private writeAttributes(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    if (designItem.hasAttributes) {\r\n      for (const a of designItem.attributes()) {\r\n        writeContext.indentedTextWriter.write(' ');\r\n        if (typeof a[1] === 'string') {\r\n          if (a[1] === \"\")\r\n            writeContext.indentedTextWriter.write(a[0]);\r\n          else\r\n            writeContext.indentedTextWriter.write(a[0] + '=\"' + DomConverter.normalizeAttributeValue(a[1]) + '\"');\r\n        }\r\n        else if (!a[1])\r\n          writeContext.indentedTextWriter.write(a[0]);\r\n        else {\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  private writeStyles(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    if (designItem.hasStyles) {\r\n      writeContext.indentedTextWriter.write(' style=\"');\r\n      const styleEntries = [...designItem.styles()];\n      const hasImportantStyle = styleEntries.some(x => designItem.isStyleImportant(x[0]));\n      let styles: Iterable<[name: string, value: string]> = styleEntries;\n      if (writeContext.options.compressCssToShorthandProperties && !hasImportantStyle)\n        styles = CssCombiner.combine(new Map(styleEntries));\n      for (const s of styles) {\n        if (s[0]) {\n          writeContext.indentedTextWriter.write(PropertiesHelper.camelToDashCase(s[0]) + ':' + DomConverter.normalizeAttributeValue(appendCssImportant(s[1], designItem.isStyleImportant(s[0]))) + ';');\n        }\n      }\n      writeContext.indentedTextWriter.write('\"');\r\n    }\r\n  }\r\n\r\n  private _writeTextNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    writeContext.lastElementDisplayType = ElementDisplayType.inline;\r\n    let content = DomConverter.normalizeContentValue(designItem.content);\r\n    if (writeContext.containerDisplayType === ElementContainerType.inline)\r\n      content = this._normalizeInlineTextContent(content);\r\n    else\r\n      content = content.trim();\r\n    if (content) {\r\n      writeContext.indentedTextWriter.write(content);\r\n    }\r\n  }\r\n\r\n  private _normalizeInlineTextContent(content: string) {\r\n    if (!content?.trim())\r\n      return '';\r\n\r\n    const hasLeadingWhitespace = /^\\s/.test(content);\r\n    const hasTrailingWhitespace = /\\s$/.test(content);\r\n    const normalized = content.replaceAll(/[\\t\\r\\n ]+/g, ' ').trim();\r\n\r\n    return `${hasLeadingWhitespace ? ' ' : ''}${normalized}${hasTrailingWhitespace ? ' ' : ''}`;\r\n  }\r\n\r\n  private _writeCommentNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    writeContext.indentedTextWriter.write('<!--' + designItem.content + '-->');\r\n  }\r\n\r\n  private _writeElementNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n//    const cs = getComputedStyle(designItem.element);\r\n//cs.whiteSpace === 'pre'\r\n//isInPre\r\n//is in inline?\r\n\r\n    const currentElementDisplayType = getElementDisplaytype(<HTMLElement>designItem.element);\r\n    writeContext.lastElementDisplayType = currentElementDisplayType;\r\n    writeContext.indentedTextWriter.write('<' + designItem.name);\r\n    this.writeAttributes(writeContext, designItem);\r\n    this.writeStyles(writeContext, designItem);\r\n    writeContext.indentedTextWriter.write('>');\r\n\r\n    let contentSingleTextNode=false;\r\n    if (designItem.hasChildren) {\r\n      const children = designItem.children();\r\n      contentSingleTextNode = designItem.childCount === 1 && designItem.firstChild.nodeType === NodeType.TextNode;\r\n      let previousContainerDisplayType = writeContext.containerDisplayType;\r\n      writeContext.containerDisplayType = this.getContainerType(<HTMLElement>designItem.element);\r\n      if (contentSingleTextNode) {\r\n        this._writeInternal(writeContext, designItem.firstChild);\r\n      } else {\r\n        if (writeContext.containerDisplayType !== ElementContainerType.inline)\r\n          writeContext.indentedTextWriter.levelRaise();\r\n\r\n        this._writeDesignItemList(currentElementDisplayType, writeContext, children)\r\n\r\n        if (writeContext.containerDisplayType !== ElementContainerType.inline)\r\n          writeContext.indentedTextWriter.levelShrink();\r\n      }\r\n      writeContext.containerDisplayType = previousContainerDisplayType;\r\n    } else if (designItem.hasContent) {\r\n      writeContext.indentedTextWriter.write(DomConverter.normalizeContentValue(designItem.content));\r\n    }\r\n\r\n    if (!DomConverter.IsSelfClosingElement(designItem.name)) {\r\n      if (currentElementDisplayType === ElementDisplayType.block && designItem.hasChildren && !contentSingleTextNode) {\r\n        this._writeNewlineAndIntend(writeContext);\r\n      }\r\n      //write newline & intend ???\r\n      writeContext.indentedTextWriter.write('</' + designItem.name + '>');\r\n      if (currentElementDisplayType !== ElementDisplayType.none) {\r\n        writeContext.lastElementDisplayType = currentElementDisplayType;\r\n      }\r\n    }\r\n  }\r\n\r\n  private _writeDesignItemList(currentElementDisplayType: ElementDisplayType, writeContext: IWriteContext, children: Iterable<IDesignItem>) {\r\n    for (const c of children) {\r\n      if (writeContext.lastElementDisplayType == null) {\r\n        //first entry, do nothing\r\n      }\r\n      else if (writeContext.containerDisplayType === ElementContainerType.inline) {\r\n        // Inline containers are whitespace-sensitive. Do not add beautification whitespace.\r\n      }\r\n      else if (writeContext.containerDisplayType === ElementContainerType.complex)\r\n        this._writeNewlineAndIntend(writeContext);\r\n      else if (writeContext.lastElementDisplayType !== ElementDisplayType.inline /*|| currentElementDisplayType !== ElementDisplayType.inline*/)\r\n        this._writeNewlineAndIntend(writeContext);\r\n      this._writeInternal(writeContext, c);\r\n    }\r\n  }\r\n\r\n  private _writeNewlineAndIntend(writeContext: IWriteContext) {\r\n    writeContext.indentedTextWriter.writeNewline();\r\n    writeContext.indentedTextWriter.writeIndent();\r\n  }\r\n\r\n  private _writeInternal(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    const start = writeContext.indentedTextWriter.position;\r\n\r\n    if (designItem.nodeType === NodeType.TextNode)\r\n      this._writeTextNode(writeContext, designItem);\r\n    else if (designItem.nodeType === NodeType.Comment)\r\n      this._writeCommentNode(writeContext, designItem);\r\n    else if (designItem.nodeType === NodeType.Element)\r\n      this._writeElementNode(writeContext, designItem);\r\n\r\n    if (writeContext.designItemsAssignmentList) {\r\n      writeContext.designItemsAssignmentList.set(designItem, { start: start, length: writeContext.indentedTextWriter.position - start - 1 });\r\n    }\r\n  }\r\n\r\n  getContainerType(element: HTMLElement): ElementContainerType {\r\n    const display = (element.ownerDocument.defaultView ?? window).getComputedStyle(element).display;\r\n    if (display === 'inline')\r\n      return ElementContainerType.inline;\r\n    if (display === 'block' || display === \"inline-block\" || display == '')\r\n      return ElementContainerType.block;\r\n    return ElementContainerType.complex;\r\n  }\r\n\r\n  write(indentedTextWriter: IndentedTextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean) {\r\n    const context: IWriteContext = { indentedTextWriter, options: this.options, lastElementDisplayType: null, containerDisplayType: ElementContainerType.block };\r\n    this._writeDesignItemList(ElementDisplayType.inline, context, designItems);\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/HtmlWriterService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { ITextWriter } from '../../helper/ITextWriter.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { isEmptyTextNode, isInline, isInlineAfter } from '../../helper/ElementHelper.js';\r\nimport { AbstractHtmlWriterService } from './AbstractHtmlWriterService.js';\r\nimport { IHtmlWriterOptions } from './IHtmlWriterOptions.js';\r\n\r\nexport class HtmlWriterService extends AbstractHtmlWriterService {\r\n  constructor(options?: IHtmlWriterOptions) {\r\n    super(options);\r\n  }\r\n\r\n  private _conditionalyWriteIndent(indentedTextWriter: ITextWriter, designItem: IDesignItem, preserveInlineWhitespace: boolean) {\r\n    if (preserveInlineWhitespace)\r\n      return;\r\n\r\n    if ((designItem.element instanceof designItem.window.HTMLElement && !isInlineAfter(designItem.element)) ||\r\n      (designItem.element.previousElementSibling instanceof designItem.window.HTMLElement && !isInline(designItem.element.previousElementSibling)) ||\r\n      (designItem.element.previousElementSibling == null && !this._isInlineElement(designItem.element.parentElement) && (designItem.element.previousSibling == null || isEmptyTextNode(designItem.element.previousSibling))) ||\r\n      (designItem.element instanceof designItem.window.SVGElement)\r\n    )\r\n      indentedTextWriter.writeIndent();\r\n  }\r\n\r\n  private _conditionalyWriteIndentBefore(indentedTextWriter: ITextWriter, designItem: IDesignItem, preserveInlineWhitespace: boolean) {\r\n    if (preserveInlineWhitespace)\r\n      return;\r\n\r\n    if ((designItem.element.previousElementSibling instanceof designItem.window.HTMLElement && !isInline(designItem.element.previousElementSibling)) ||\r\n      (designItem.element.previousElementSibling == null && !this._isInlineElement(designItem.element.parentElement) && (designItem.element.previousSibling == null || isEmptyTextNode(designItem.element.previousSibling))) ||\r\n      (designItem.element instanceof designItem.window.SVGElement)\r\n    )\r\n      indentedTextWriter.writeIndent();\r\n  }\r\n\r\n  private _conditionalyWriteNewline(indentedTextWriter: ITextWriter, designItem: IDesignItem, preserveInlineWhitespace: boolean) {\r\n    if (preserveInlineWhitespace)\r\n      return;\r\n\r\n    if ((designItem.element instanceof designItem.window.HTMLElement && !isInlineAfter(designItem.element)) ||\r\n      (designItem.element.nextElementSibling instanceof designItem.window.HTMLElement && !isInline(designItem.element.nextElementSibling)) ||\r\n      (designItem.element instanceof designItem.window.SVGElement)\r\n    )\r\n      indentedTextWriter.writeNewline();\r\n  }\r\n\r\n  write(indentedTextWriter: ITextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean, updatePositions: boolean = false) {\n    if (updatePositions)\n      designItems[0]?.instanceServiceContainer.designItemDocumentPositionService?.clearSourceParts();\n    for (const d of designItems) {\n      this.internalWrite(indentedTextWriter, d, updatePositions, false);\n    }\n  }\r\n\r\n  //TODO: special case for style/script nodes, keep whitespace\r\n  private internalWrite(indentedTextWriter: ITextWriter, designItem: IDesignItem, updatePositions: boolean, preserveInlineWhitespace: boolean) {\r\n    let start = indentedTextWriter.position;\r\n    let end = indentedTextWriter.position;\r\n    const currentPreserveInlineWhitespace = preserveInlineWhitespace || this._isInlineContainer(designItem);\r\n\r\n    if (designItem.nodeType == NodeType.TextNode) {\r\n      if (isEmptyTextNode(designItem.element) &&\r\n        ((designItem.element.previousSibling instanceof designItem.window.HTMLElement && !isInlineAfter(designItem.element.previousSibling)) ||\r\n          (designItem.element.nextSibling instanceof designItem.window.HTMLElement && !isInline(designItem.element.nextSibling)))) {\r\n      } else\r\n        this.writeTextNode(indentedTextWriter, designItem, true, true, currentPreserveInlineWhitespace);\r\n      end = indentedTextWriter.position;\r\n    } else if (designItem.nodeType == NodeType.Comment) {\r\n      this._conditionalyWriteIndent(indentedTextWriter, designItem, currentPreserveInlineWhitespace);\r\n      start = indentedTextWriter.position;\r\n      indentedTextWriter.write('<!--' + designItem.content + '-->');\r\n      end = indentedTextWriter.position;\r\n      this._conditionalyWriteNewline(indentedTextWriter, designItem, currentPreserveInlineWhitespace);\r\n    } else {\r\n      this._conditionalyWriteIndentBefore(indentedTextWriter, designItem, currentPreserveInlineWhitespace);\r\n      start = indentedTextWriter.position;\r\n      indentedTextWriter.write('<' + designItem.name);\r\n\r\n      this.writeAttributes(indentedTextWriter, designItem, updatePositions);\n      this.writeStyles(indentedTextWriter, designItem);\r\n\r\n      indentedTextWriter.write('>');\r\n\r\n      let contentSingleTextNode = false;\r\n      if (designItem.hasChildren) {\r\n        const children = designItem.children();\r\n        contentSingleTextNode = designItem.childCount === 1 && designItem.firstChild.nodeType === NodeType.TextNode;\r\n        if (contentSingleTextNode) {\r\n          const notrim = designItem.name == 'script' || designItem.name == 'style' || designItem.name == 'pre';\r\n          this.writeTextNode(indentedTextWriter, designItem, false, !notrim, currentPreserveInlineWhitespace);\r\n        } else {\r\n          if (!currentPreserveInlineWhitespace && (designItem.element instanceof designItem.window.HTMLElement && !isInlineAfter(designItem.element) || (designItem.element instanceof designItem.window.SVGElement))) {\r\n            indentedTextWriter.writeNewline();\r\n            indentedTextWriter.levelRaise();\r\n          }\r\n          for (const c of children) {\r\n            this.internalWrite(indentedTextWriter, c, updatePositions, currentPreserveInlineWhitespace);\r\n            let childSingleTextNode = c.childCount === 1 && c.firstChild.nodeType === NodeType.TextNode;\r\n            if (childSingleTextNode)\r\n              if (!indentedTextWriter.isLastCharNewline())\r\n                this._conditionalyWriteNewline(indentedTextWriter, c, currentPreserveInlineWhitespace);\r\n          }\r\n          if (!currentPreserveInlineWhitespace && (designItem.element instanceof designItem.window.HTMLElement && !isInlineAfter(designItem.element) || (designItem.element instanceof designItem.window.SVGElement))) {\r\n            indentedTextWriter.levelShrink();\r\n            if (!indentedTextWriter.isLastCharNewline())\r\n              indentedTextWriter.writeNewline();\r\n            indentedTextWriter.writeIndent();\r\n          }\r\n        }\r\n      } else if (designItem.hasContent) {\r\n        indentedTextWriter.write(DomConverter.normalizeContentValue(designItem.content));\r\n        //this._conditionalyWriteNewline(indentedTextWriter, designItem);\r\n      }\r\n\r\n      end = indentedTextWriter.position;\r\n      if (!DomConverter.IsSelfClosingElement(designItem.name))\r\n        indentedTextWriter.write('</' + designItem.name + '>');\r\n      end = indentedTextWriter.position;\r\n      //if (!contentSingleTextNode)\r\n      if (!currentPreserveInlineWhitespace && !indentedTextWriter.isLastCharNewline() && (!designItem.parent || !isInlineAfter(<HTMLElement>designItem.parent.element)))\r\n        this._conditionalyWriteNewline(indentedTextWriter, designItem, currentPreserveInlineWhitespace);\r\n    }\r\n\r\n    if (updatePositions && designItem.instanceServiceContainer.designItemDocumentPositionService) {\r\n      designItem.instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: start, length: end - start });\r\n    }\r\n  }\r\n\r\n  private writeTextNode(indentedTextWriter: ITextWriter, designItem: IDesignItem, indentAndNewline: boolean, trim = true, preserveInlineWhitespace = false) {\r\n    let start = indentedTextWriter.position;\r\n    let end = indentedTextWriter.position;\r\n\r\n    let content = DomConverter.normalizeContentValue(designItem.content);\r\n    if (preserveInlineWhitespace || this._hasInlineParent(designItem))\r\n      content = this._normalizeInlineTextContent(content);\r\n    else if (trim)\r\n      content = content.trim();\r\n    if (content) {\r\n      if (indentAndNewline)\r\n        this._conditionalyWriteIndent(indentedTextWriter, designItem, preserveInlineWhitespace);\r\n      indentedTextWriter.write(content);\r\n      if (indentAndNewline)\r\n        this._conditionalyWriteNewline(indentedTextWriter, designItem, preserveInlineWhitespace);\r\n    }\r\n    end = indentedTextWriter.position;\r\n\r\n    for (const d of designItem.children())\r\n      designItem.instanceServiceContainer.designItemDocumentPositionService.setPosition(d, { start: start, length: end - start });\r\n  }\r\n\r\n  private _hasInlineParent(designItem: IDesignItem) {\r\n    return designItem.parent?.element instanceof designItem.window.HTMLElement && isInline(designItem.parent.element);\r\n  }\r\n\r\n  private _normalizeInlineTextContent(content: string) {\r\n    if (!content?.trim())\r\n      return '';\r\n\r\n    const hasLeadingWhitespace = /^\\s/.test(content);\r\n    const hasTrailingWhitespace = /\\s$/.test(content);\r\n    const normalized = content.replaceAll(/[\\t\\r\\n ]+/g, ' ').trim();\r\n\r\n    return `${hasLeadingWhitespace ? ' ' : ''}${normalized}${hasTrailingWhitespace ? ' ' : ''}`;\r\n  }\r\n\r\n  private _isInlineContainer(designItem: IDesignItem) {\r\n    return designItem.nodeType === NodeType.Element && designItem.element instanceof designItem.window.HTMLElement && isInline(designItem.element);\r\n  }\r\n\r\n  private _isInlineElement(element: Element | null) {\r\n    return element instanceof HTMLElement && isInline(element);\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/IHtmlWriterOptions.ts",
    "content": "export interface IHtmlWriterOptions {\r\n  beautifyOutput?: boolean;\r\n  compressCssToShorthandProperties?: boolean;\r\n  writeDesignerProperties?: boolean;\r\n  parseJsonInAttributes?: boolean;\r\n  jsonWriteMode?: 'min' | 'beauty';\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/IHtmlWriterService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ITextWriter } from '../../helper/ITextWriter.js';\r\nimport { IHtmlWriterOptions } from './IHtmlWriterOptions.js';\r\n\r\nexport interface IHtmlWriterService {\n  options: IHtmlWriterOptions;\n  /**\n   * Enables serializing the root design item when the designer has no child items.\n   * Default HTML writers should leave this unset because root containers are usually\n   * editor scaffolding, not document content. Text-based document writers can set it\n   * to true when document-level metadata stored on the root item must still be written.\n   */\n  supportsRootItemWrite?: boolean;\n  write(textWriter: ITextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean, updatePositions?: boolean);\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/IStringPosition.ts",
    "content": "export interface IStringPosition {\r\n  start: number;\r\n  length: number;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/htmlWriterService/SimpleHtmlWriterService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IHtmlWriterService } from './IHtmlWriterService.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { IndentedTextWriter } from '../../helper/IndentedTextWriter.js';\r\nimport { CssCombiner } from '../../helper/CssCombiner.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { PropertiesHelper } from '../propertiesService/services/PropertiesHelper.js';\nimport { ElementDisplayType, getElementDisplaytype } from '../../helper/ElementHelper.js';\nimport { IHtmlWriterOptions } from './IHtmlWriterOptions.js';\nimport { appendCssImportant } from '../../helper/CssImportant.js';\n\r\nexport enum ElementContainerType {\r\n  block,\r\n  complex\r\n}\r\n\r\nexport interface IWriteContext {\r\n  options: IHtmlWriterOptions;\r\n  indentedTextWriter: IndentedTextWriter;\r\n  lastElementDisplayType: ElementDisplayType | null;\r\n  containerDisplayType: ElementContainerType;\r\n}\r\n\r\nexport class SimpleHtmlWriterService implements IHtmlWriterService {\r\n  public options: IHtmlWriterOptions;\r\n\r\n  constructor(options?: IHtmlWriterOptions) {\r\n    this.options = options ?? {};\r\n    this.options.beautifyOutput ??= true;\r\n    this.options.compressCssToShorthandProperties ??= true;\r\n    this.options.writeDesignerProperties ??= true;\r\n    this.options.parseJsonInAttributes ??= true;\r\n    this.options.jsonWriteMode ??= 'min';\r\n  }\r\n\r\n  protected writeAttributes(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    if (designItem.hasAttributes) {\r\n      for (const a of designItem.attributes()) {\r\n        writeContext.indentedTextWriter.write(' ');\r\n        if (typeof a[1] === 'string') {\r\n          if (a[1] === \"\")\r\n            writeContext.indentedTextWriter.write(a[0]);\r\n          else\r\n            writeContext.indentedTextWriter.write(a[0] + '=\"' + DomConverter.normalizeAttributeValue(a[1]) + '\"');\r\n        }\r\n        else if (!a[1])\r\n          writeContext.indentedTextWriter.write(a[0]);\r\n        else {\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  protected writeStyles(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    if (designItem.hasStyles) {\r\n      writeContext.indentedTextWriter.write(' style=\"');\r\n      const styleEntries = [...designItem.styles()];\n      const hasImportantStyle = styleEntries.some(x => designItem.isStyleImportant(x[0]));\n      let styles: Iterable<[name: string, value: string]> = styleEntries;\n      if (writeContext.options.compressCssToShorthandProperties && !hasImportantStyle)\n        styles = CssCombiner.combine(new Map(styleEntries));\n      for (const s of styles) {\n        if (s[0]) {\n          writeContext.indentedTextWriter.write(PropertiesHelper.camelToDashCase(s[0]) + ':' + DomConverter.normalizeAttributeValue(appendCssImportant(s[1], designItem.isStyleImportant(s[0]))) + ';');\n        }\n      }\n      writeContext.indentedTextWriter.write('\"');\r\n    }\r\n  }\r\n\r\n  private _writeTextNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    writeContext.lastElementDisplayType = ElementDisplayType.inline;\r\n    let content = DomConverter.normalizeContentValue(designItem.content);\r\n    writeContext.indentedTextWriter.write(content);\r\n  }\r\n\r\n  private _writeCommentNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    writeContext.indentedTextWriter.write('<!--' + designItem.content + '-->');\r\n  }\r\n\r\n  private _writeElementNode(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    const currentElementDisplayType = getElementDisplaytype(<HTMLElement>designItem.element);\r\n    writeContext.lastElementDisplayType = currentElementDisplayType;\r\n    writeContext.indentedTextWriter.write('<' + designItem.name);\r\n    this.writeAttributes(writeContext, designItem);\r\n    this.writeStyles(writeContext, designItem);\r\n    writeContext.indentedTextWriter.write('>');\r\n\r\n    if (designItem.hasChildren) {\r\n      const children = designItem.children();\r\n      this._writeDesignItemList(currentElementDisplayType, writeContext, children);\r\n    } else if (designItem.hasContent) {\r\n      writeContext.indentedTextWriter.write(DomConverter.normalizeContentValue(designItem.content));\r\n    }\r\n\r\n    if (!DomConverter.IsSelfClosingElement(designItem.name)) {\r\n      writeContext.indentedTextWriter.write('</' + designItem.name + '>');\r\n      if (currentElementDisplayType !== ElementDisplayType.none) {\r\n        writeContext.lastElementDisplayType = currentElementDisplayType;\r\n      }\r\n    }\r\n  }\r\n\r\n  private _writeDesignItemList(currentElementDisplayType: ElementDisplayType, writeContext: IWriteContext, children: Iterable<IDesignItem>) {\r\n    for (const c of children) {\r\n      this._writeInternal(writeContext, c);\r\n    }\r\n  }\r\n\r\n  private _writeInternal(writeContext: IWriteContext, designItem: IDesignItem) {\r\n    if (designItem.nodeType === NodeType.TextNode)\r\n      this._writeTextNode(writeContext, designItem);\r\n    else if (designItem.nodeType === NodeType.Comment)\r\n      this._writeCommentNode(writeContext, designItem);\r\n    else if (designItem.nodeType === NodeType.Element)\r\n      this._writeElementNode(writeContext, designItem);\r\n  }\r\n\r\n  write(indentedTextWriter: IndentedTextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean) {\r\n    const context: IWriteContext = { indentedTextWriter, options: this.options, lastElementDisplayType: null, containerDisplayType: ElementContainerType.block };\r\n    this._writeDesignItemList(ElementDisplayType.inline, context, designItems);\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/initializationService/IIntializationService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\n// This is called for every root DesignItem added to the designer canvas. It's not called for the items children.\r\nexport interface IIntializationService {\r\n  init(designItem: IDesignItem): void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/instanceService/DefaultInstanceService.ts",
    "content": "import { IInstanceService } from './IInstanceService.js';\r\nimport { IElementDefinition } from '../elementsService/IElementDefinition.js';\r\nimport { IDesignerInstance } from './IDesignerInstance.js';\r\nimport type { ServiceContainer } from '../ServiceContainer.js';\r\nimport type { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DesignItem } from '../../item/DesignItem.js';\r\nimport { encodeXMLChars } from '../../helper/XmlHelper.js';\r\nimport { newElementFromString } from '../../helper/ElementHelper.js';\r\nimport { isFirefox } from '../../helper/Browser.js';\r\n\r\nexport class DefaultInstanceService implements IInstanceService {\r\n  async getElement(definition: IElementDefinition, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<IDesignItem> {\r\n    if (definition.import) {\r\n      let importUri = definition.import;\r\n      if (importUri[0] === '.')\r\n        importUri = (window.location.origin + window.location.pathname).split('/').slice(0, -1).join('/') + '/' + importUri;\r\n      //@ts-ignore\r\n      if (window.importShim) {\r\n        //@ts-ignore\r\n        importShim(importUri).then((x) => {\r\n          let ctor = customElements.get(definition.tag)\r\n          if (!ctor && definition.className && x[definition.className])\r\n            customElements.define(definition.tag, x[definition.className])\r\n          //TODO: refresh all extensions\r\n        });\r\n      }\r\n      else {\r\n        import(importUri).then((x) => {\r\n          let ctor = customElements.get(definition.tag)\r\n          if (!ctor && definition.className && x[definition.className])\r\n            customElements.define(definition.tag, x[definition.className])\r\n          //TODO: refresh all extensions\r\n        });\r\n      }\r\n      //removed await here, feels better to not wait for the elemnt is loaded, maybe this needs to be configurable\r\n      if (instanceServiceContainer.designContext.imports.indexOf(importUri) <= 0)\r\n        instanceServiceContainer.designContext.imports.push(importUri);\r\n    }\r\n\r\n    let attr = '';\r\n    if (definition.defaultAttributes) {\r\n      for (let a in definition.defaultAttributes) {\r\n        let value = definition.defaultAttributes[a];\r\n        try {\r\n          if (typeof value === 'object')\r\n            attr += ' ' + a + '=\"' + encodeXMLChars(JSON.stringify(definition.defaultAttributes[a])) + '\"';\r\n          else\r\n            attr += ' ' + a + '=\"' + encodeXMLChars(definition.defaultAttributes[a]) + '\"';\r\n        } catch (e) {\r\n          console.warn(e);\r\n        }\r\n      }\r\n    }\r\n\r\n    const elementString = '<' + definition.tag + attr + '></' + definition.tag + '>';\r\n\r\n    const element = <HTMLElement>newElementFromString(elementString, instanceServiceContainer.designerCanvas.rootDesignItem.document);\r\n    (<IDesignerInstance><any>element)._inNodeProjectsDesignerView = true;\r\n    if (definition.defaultWidth)\r\n      element.style.width = definition.defaultWidth;\r\n    if (definition.defaultHeight)\r\n      element.style.height = definition.defaultHeight;\r\n    element.style.position = 'absolute'\r\n\r\n    if (definition.defaultStyles) {\r\n      for (let s in definition.defaultStyles)\r\n        element.style[s] = definition.defaultStyles[s];\r\n    }\r\n\r\n    if (definition.defaultContent) {\r\n      if (typeof definition.defaultContent === \"string\") {\r\n        let doc: Document;\r\n        //@ts-ignore\r\n        if (instanceServiceContainer.designerCanvas.rootDesignItem.window.Document.parseHTMLUnsafe && !isFirefox) {\r\n          //@ts-ignore\r\n          doc = instanceServiceContainer.designerCanvas.rootDesignItem.window.Document.parseHTMLUnsafe(definition.defaultContent);\r\n        } else {\r\n          const parser = new instanceServiceContainer.designerCanvas.rootDesignItem.window.DOMParser();\r\n          //@ts-ignore\r\n          doc = parser.parseFromString(definition.defaultContent, 'text/html', { includeShadowRoots: true });\r\n        }\r\n\r\n        element.append(...doc.head.childNodes);\r\n        element.append(...doc.body.childNodes);\r\n      } else {\r\n        element.appendChild(definition.defaultContent);\r\n      }\r\n    }\r\n\r\n    let designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\r\n    return designItem;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/instanceService/IDesignerInstance.ts",
    "content": "export interface IDesignerInstance {\r\n  _inNodeProjectsDesignerView: boolean;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/instanceService/IInstanceService.ts",
    "content": "import { IService } from '../IService.js';\r\nimport { IElementDefinition } from '../elementsService/IElementDefinition.js';\r\nimport type { ServiceContainer } from '../ServiceContainer.js';\r\nimport type { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\nexport interface IInstanceService extends IService {\r\n    getElement(definition: IElementDefinition, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer) : Promise<IDesignItem>\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/manifestParsers/IOldCustomElementsManifest.ts",
    "content": "export interface IOldCustomElementsManifest {\r\n  version: string;\r\n  tags: IOldCustomElementsManifestTag[];\r\n}\r\n\r\nexport interface IOldCustomElementsManifestTag {\r\n  name: string;\r\n  path: string;\r\n  description?: string;\r\n  attributes?: IOldCustomElementsManifestAttribute[];\r\n  properties?: IOldCustomElementsManifestProperty[];\r\n}\r\n\r\n\r\nexport interface IOldCustomElementsManifestAttribute {\r\n  name: string;\r\n  description?: string;\r\n  type: string;\r\n  default?: string;\r\n}\r\n\r\nexport interface IOldCustomElementsManifestProperty {\r\n  name: string;\r\n  attribute?: string;\r\n  description?: string;\r\n  type: string;\r\n  default?: string;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/manifestParsers/OldCustomElementsManifestLoader.ts",
    "content": "import { LazyLoader } from '@node-projects/base-custom-webcomponent';\r\nimport { IOldCustomElementsManifest } from './IOldCustomElementsManifest.js';\r\nimport { ServiceContainer } from '../ServiceContainer.js';\r\nimport { IElementDefinition } from '../elementsService/IElementDefinition.js';\r\nimport { PreDefinedElementsService } from '../elementsService/PreDefinedElementsService.js';\r\nimport { IJsonPropertyDefinition } from '../propertiesService/services/IJsonPropertyDefinition.js';\r\nimport { IJsonPropertyDefinitions } from '../propertiesService/services/IJsonPropertyDefinitions.js';\r\nimport { ListPropertiesService } from '../propertiesService/services/ListPropertiesService.js';\r\n\r\nexport class OldCustomElementsManifestLoader {\r\n  public static async loadManifest(serviceContainer: ServiceContainer, nodeModule: string, options?: { name?: string, dontLoadWidgets?: boolean, dontLoadProperties?: boolean }) {\r\n    const nodePath = './node_modules/';\r\n    const packageJson = JSON.parse(await LazyLoader.LoadText(nodePath + nodeModule + '/package.json'));\r\n    let jsModule = nodePath + nodeModule + '/' + packageJson.main;\r\n    if (packageJson.module)\r\n      jsModule = nodePath + nodeModule + '/' + packageJson.module;\r\n    const manifest = <IOldCustomElementsManifest>JSON.parse(await LazyLoader.LoadText(nodePath + nodeModule + '/' + packageJson.customElementsManifest));\r\n    let name = nodeModule;\r\n    if (options && options.name)\r\n      name = options.name;\r\n\r\n    if (!options || !options.dontLoadWidgets) {\r\n      const elementDefinitions: IElementDefinition[] = [];\r\n      for (const tag of manifest.tags) {\r\n        const elementDefinition: IElementDefinition = { tag: tag.name, import: jsModule, description: tag.description }\r\n        elementDefinitions.push(elementDefinition);\r\n      }\r\n      const service = new PreDefinedElementsService(name, { elements: elementDefinitions });\r\n      serviceContainer.register('elementsService', service);\r\n    }\r\n\r\n    if (!options || !options.dontLoadProperties) {\r\n      const propertyDefinitions: IJsonPropertyDefinitions = {};\r\n      for (const tag of manifest.tags) {\r\n        const attributes: IJsonPropertyDefinition[] = []\r\n        for (const attr of tag.attributes) {\r\n          let propertyDefinition: IJsonPropertyDefinition = { name: attr.name, default: attr.default, description: attr.description }\r\n          if (attr.type && attr.type.startsWith('\"')) {\r\n            propertyDefinition.type = 'list';\r\n            propertyDefinition.values = attr.type.split('|');\r\n          } else {\r\n            propertyDefinition.type = attr.type;\r\n          }\r\n          attributes.push(propertyDefinition);\r\n        }\r\n        propertyDefinitions[tag.name] = attributes;\r\n      }\r\n      const service = new ListPropertiesService(propertyDefinitions);\r\n      serviceContainer.register('propertyService', service);\r\n    }\r\n\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/manifestParsers/WebcomponentManifestParserService.ts",
    "content": "import { BindingTarget } from '../../item/BindingTarget.js';\r\nimport { IElementDefinition } from '../elementsService/IElementDefinition.js';\r\nimport { IElementsService } from '../elementsService/IElementsService.js';\r\nimport { IPropertiesService, RefreshMode } from '../propertiesService/IPropertiesService.js';\r\nimport { IProperty } from '../propertiesService/IProperty.js';\r\nimport { PropertyType } from '../propertiesService/PropertyType.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { AbstractPropertiesService } from '../propertiesService/services/AbstractPropertiesService.js';\r\nimport { removeLeading, removeTrailing } from '../../helper/Helper.js';\r\nimport { WebcomponentManifestPropertiesService } from '../propertiesService/services/WebcomponentManifestPropertiesService.js';\r\nimport { IEvent } from '../eventsService/IEvent.js';\r\nimport { EventsService } from '../eventsService/EventsService.js';\r\n\r\nexport class WebcomponentManifestParserService extends AbstractPropertiesService implements IElementsService, IPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.none;\r\n  }\r\n\r\n  private _name: string;\r\n  get name() { return this._name; }\r\n\r\n  private _packageData: any;\r\n  private _elementList: IElementDefinition[];\r\n  private _propertiesList: Record<string, IProperty[]>;\r\n  private __eventsList: Record<string, IEvent[]>;\r\n  private _resolveStored: ((value: IElementDefinition[]) => void)[];\r\n  private _rejectStored: ((errorCode: number) => void)[];\r\n  private _importPrefix = '';\r\n\r\n  constructor(name: string, fileOrObject: string | object, importPrefix = '') {\r\n    super();\r\n    this._name = name;\r\n    this._importPrefix = importPrefix;\r\n    if (typeof fileOrObject === 'string') {\r\n      this._importPrefix = this._importPrefix ?? fileOrObject.split('/').slice(0, -1).join('/');\r\n\r\n\r\n      let importOfFile: Promise<any>;\r\n      //@ts-ignore\r\n      if (window.importShim)\r\n        //@ts-ignore\r\n        importOfFile = importShim(fileOrObject, { with: { type: 'json' } });\r\n      else\r\n        //@ts-ignore\r\n        importOfFile = import(fileOrObject, { with: { type: 'json' } });\r\n\r\n      importOfFile.then(module => {\r\n        this._packageData = module.default;\r\n        this._parseManifest();\r\n      }).catch(err => {\r\n        if (this._rejectStored) {\r\n          this._rejectStored.forEach(x => x(err));\r\n          this._resolveStored = null;\r\n          this._rejectStored = null;\r\n        }\r\n      });\r\n    } else {\r\n      this._packageData = fileOrObject;\r\n      this._parseManifest();\r\n    }\r\n  }\r\n\r\n  private _parseManifest() {\r\n    this._elementList = [];\r\n    this._propertiesList = {};\r\n    for (let m of this._packageData.modules) {\r\n      for (let e of m.exports) {\r\n        if (e.kind == 'custom-element-definition') {\r\n          this._elementList.push({ tag: e.name, import: removeTrailing(this._importPrefix, '/') + '/' + removeLeading(m.path, '/') });\r\n          let properties: IProperty[] = [];\r\n          let declaration = m.declarations.find(x => x.name == e.declaration.name);\r\n          for (let d of declaration.members) {\r\n            if (d.kind == 'field') {\r\n              let pType = PropertyType.property;\r\n              if (declaration.attributes)\r\n                pType = declaration.attributes.find(x => x.fieldName == d.name) != null ? PropertyType.propertyAndAttribute : PropertyType.property;\r\n              const p = WebcomponentManifestPropertiesService.manifestClassPropertyTypeToEditorPropertyType(d.type?.text, d.type?.editor);\r\n              properties.push({ name: d.name, service: this, propertyType: pType, type: p[0], values: p[1], description: d.description });\r\n            }\r\n          }\r\n          this._propertiesList[e.name] = properties;\r\n          if (declaration.events) {\r\n            let events: IEvent[] = [];\r\n            for (let e of declaration.events) {\r\n              events.push({ name: e.name })\r\n            }\r\n            if (events.length)\r\n              this.__eventsList[e.name] = events;\r\n          }\r\n        }\r\n      }\r\n      if (this._resolveStored) {\r\n        this._resolveStored.forEach(x => x(this._elementList));\r\n        this._resolveStored = null;\r\n        this._rejectStored = null;\r\n      }\r\n    }\r\n  }\r\n\r\n  async getElements(): Promise<IElementDefinition[]> {\r\n    if (this._packageData)\r\n      return Promise.resolve(this._elementList);\r\n    if (!this._resolveStored) {\r\n      this._resolveStored = [];\r\n      this._rejectStored = [];\r\n    }\r\n    return new Promise((resolve, reject) => { this._resolveStored.push(resolve); this._rejectStored.push(reject); });\r\n  }\r\n\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    if (this._elementList)\r\n      return this._elementList.find(x => x.tag == designItem.name) != null\r\n    return false\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[]> {\r\n    return this._propertiesList[designItem.name];\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return this._propertiesList[designItem.name].find(x => x.name == name);\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    return this._propertiesList[designItem.name].find(x => x.name == property.name).propertyType == PropertyType.attribute ? BindingTarget.attribute : BindingTarget.property\r\n  }\r\n\r\n  public isHandledElementFromEventsService(designItem: IDesignItem): boolean {\r\n    if (this.__eventsList)\r\n      return this.__eventsList[designItem.name] != null\r\n    return false\r\n  }\r\n\r\n  public getPossibleEvents(designItem: IDesignItem): IEvent[] {\r\n    return [...this.__eventsList[designItem.name], ...EventsService._simpleMouseEvents, ...EventsService._pointerEvents, ...EventsService._allElements, ...EventsService._focusableEvents];\r\n  }\r\n\r\n  public getEvent(designItem: IDesignItem, name: string): IEvent {\r\n    let evt = this.getPossibleEvents(designItem).find(x => x.name == name);\r\n    return evt ?? { name, propertyName: 'on' + name, eventObjectName: 'Event' };\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/miniatureViewService/IMiniatureViewService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\n\nexport interface IMiniatureViewService {\n    provideMiniatureView(designerCanvas: IDesignerCanvas): Promise<Node>;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/miniatureViewService/MiniatureViewService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\nimport { IMiniatureViewService } from \"./IMiniatureViewService.js\";\n\nexport class MiniatureViewService implements IMiniatureViewService {\n    public async provideMiniatureView(designerCanvas: IDesignerCanvas): Promise<Node> {\n        let el = document.createDocumentFragment();\n        for (const e of designerCanvas.rootDesignItem.children()) {\n            el.appendChild(e.element.cloneNode(true));\n        }\n        return el;\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/modelCommandService/DefaultModelCommandService.ts",
    "content": "import { CommandType } from \"../../../commandHandling/CommandType.js\";\r\nimport { IUiCommand } from \"../../../commandHandling/IUiCommand.js\";\r\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { IModelCommandService } from \"./IModelCommandService.js\";\r\nimport { ArrangeHelper } from \"../../helper/ArrangeHelper.js\";\r\nimport { Orientation } from \"../../../enums/Orientation.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\n\r\nexport class DefaultModelCommandService implements IModelCommandService {\r\n  canExecuteCommand(designerCanvas: IDesignerCanvas, command: IUiCommand, designItems?: IDesignItem[]): boolean {\r\n    if (command.type == CommandType.moveBackward ||\r\n      command.type == CommandType.moveForward ||\r\n      command.type == CommandType.moveToBack ||\r\n      command.type == CommandType.moveToFront)\r\n      return designerCanvas.instanceServiceContainer.selectionService.primarySelection != null && !designerCanvas.instanceServiceContainer.selectionService.primarySelection.isRootItem;\r\n    if (command.type == CommandType.arrangeBottom ||\r\n      command.type == CommandType.arrangeCenter ||\r\n      command.type == CommandType.arrangeLeft ||\r\n      command.type == CommandType.arrangeMiddle ||\r\n      command.type == CommandType.arrangeRight ||\r\n      command.type == CommandType.arrangeTop ||\r\n      command.type == CommandType.unifyHeight ||\r\n      command.type == CommandType.unifyWidth)\r\n      return designerCanvas.instanceServiceContainer.selectionService.selectedElements.length > 1;\r\n    if (command.type == CommandType.rotateCounterClockwise ||\r\n      command.type == CommandType.rotateClockwise ||\r\n      command.type == CommandType.mirrorHorizontal ||\r\n      command.type == CommandType.mirrorVertical)\r\n      return designerCanvas.instanceServiceContainer.selectionService.selectedElements.length > 0 && !designerCanvas.instanceServiceContainer.selectionService.primarySelection.isRootItem;\r\n    return null;\r\n  }\r\n\r\n  async executeCommand(designerCanvas: IDesignerCanvas, command: IUiCommand, designItems?: IDesignItem[]) {\r\n    designItems = designItems ?? [...designerCanvas.instanceServiceContainer.selectionService.selectedElements];\r\n    const primary = designItems[0]  ;\r\n    \r\n    if (command.type == CommandType.moveBackward) {\r\n      let idx = primary.parent.indexOf(primary) - 1;\r\n      if (idx >= 0)\r\n        primary.parent.insertChild(primary, idx);\r\n    } else if (command.type == CommandType.moveForward) {\r\n      let idx = primary.parent.indexOf(primary) + 1;\r\n      if (idx < primary.parent.childCount)\r\n        primary.parent.insertChild(primary, idx);\r\n    } else if (command.type == CommandType.moveToBack) {\r\n      primary.parent.insertChild(primary, 0);\r\n    } else if (command.type == CommandType.moveToFront) {\r\n      primary.parent.insertChild(primary);\r\n    } else if (command.type == CommandType.arrangeTop) {\r\n      ArrangeHelper.arrangeElements(Orientation.TOP, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.arrangeRight) {\r\n      ArrangeHelper.arrangeElements(Orientation.RIGHT, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.arrangeLeft) {\r\n      ArrangeHelper.arrangeElements(Orientation.LEFT, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.arrangeBottom) {\r\n      ArrangeHelper.arrangeElements(Orientation.BOTTOM, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.arrangeCenter) {\r\n      ArrangeHelper.arrangeElements(Orientation.HORIZONTAL_CENTER, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.arrangeMiddle) {\r\n      ArrangeHelper.arrangeElements(Orientation.VERTICAL_CENTER, designerCanvas, designItems);\r\n    } else if (command.type == CommandType.unifyHeight) {\r\n      const grp = primary.openGroup('unifyHeight');\r\n      const height = primary.getStyle('height');\r\n      for (let s of designItems) {\r\n        s.setStyle('height', height);\r\n      }\r\n      grp.commit();\r\n    } else if (command.type == CommandType.unifyWidth) {\r\n      const grp = primary.openGroup('unifyWidth');\r\n      const width = primary.getStyle('width');\r\n      for (let s of designItems) {\r\n        s.setStyle('width', width);\r\n      }\r\n      grp.commit();\r\n    } else if (command.type == CommandType.rotateCounterClockwise) {\r\n      const grp = primary.openGroup('rotateCounterClockwise');\r\n      var trf = primary.getStyle('transform');\r\n      let degree = 0;\r\n      let rotation = \"\";\r\n      if (trf != null) {\r\n        try {\r\n          if (trf.includes('-'))\r\n            degree = parseInt(trf.match(/\\d+/)[0]) * -1;\r\n          else\r\n            degree = parseInt(trf.match(/\\d+/)[0]);\r\n\r\n          rotation = \"rotate(\" + (degree - 90) + \"deg)\";\r\n        }\r\n        catch {\r\n          rotation = \"rotate(-90deg)\"\r\n        }\r\n      }\r\n      else {\r\n        rotation = \"rotate(-90deg)\";\r\n      }\r\n      for (let s of designItems) {\r\n        s.setStyle('transform', rotation);\r\n      }\r\n      grp.commit();\r\n    } else if (command.type == CommandType.rotateClockwise) {\r\n      const grp = primary.openGroup('rotateClockwise');\r\n      var trf = primary.getStyle('transform');\r\n      let degree = 0;\r\n      let rotation = \"\";\r\n      if (trf != null) {\r\n        try {\r\n          if (trf.includes('-'))\r\n            degree = parseInt(trf.match(/\\d+/)[0]) * -1;\r\n          else\r\n            degree = parseInt(trf.match(/\\d+/)[0]);\r\n\r\n          rotation = \"rotate(\" + (degree + 90) + \"deg)\";\r\n        }\r\n        catch {\r\n          rotation = \"rotate(90deg)\"\r\n        }\r\n      }\r\n      else {\r\n        rotation = \"rotate(90deg)\";\r\n      }\r\n      for (let s of designItems) {\r\n        s.setStyle('transform', rotation);\r\n      }\r\n      grp.commit();\r\n    } else if (command.type == CommandType.mirrorHorizontal) {\r\n      const grp = primary.openGroup('mirrorHorizontal');\r\n      for (let s of designItems) {\r\n        s.setStyle('transform', 'scaleX(-1)');\r\n      }\r\n      grp.commit();\r\n    } else if (command.type == CommandType.mirrorVertical) {\r\n      const grp = primary.openGroup('mirrorVertical');\r\n      for (let s of designItems) {\r\n        s.setStyle('transform', 'scaleY(-1)');\r\n      }\r\n      grp.commit();\r\n    } else\r\n      return null;\r\n\r\n    return true;\r\n  }\r\n}\r\n\r\n//TODO: combine transforms, could be easy, add new transform, get the matrix and convert back to simple ones (if possible)"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/modelCommandService/IModelCommandService.ts",
    "content": "import { IUiCommand } from '../../../commandHandling/IUiCommand.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { IService } from '../IService.js';\r\n\r\nexport interface IModelCommandService extends IService {\r\n  canExecuteCommand(designerCanvas: IDesignerCanvas, command: IUiCommand, designItems?: IDesignItem[]): boolean | null\r\n  executeCommand(designerCanvas: IDesignerCanvas, commandType: IUiCommand, designItems?: IDesignItem[])\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/multiplayerService/IMultiplayerService.ts",
    "content": "import { IPoint } from \"../../../interfaces/IPoint\";\r\n\r\nexport interface IMultiplayerService {\r\n    signOn(userInfo: userInfo);\r\n    //submitState\r\n}\r\n\r\nexport type userInfo = {\r\n    name: string;\r\n    color: string;\r\n}\r\n\r\nexport type userContext = {\r\n    name: string;\r\n}\r\n\r\nexport type cursor = {\r\n    point: IPoint\r\n    state: 'none' | 'pointing' | 'chat'\r\n    document: string\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/multiplayerService/MultiplayerService.ts",
    "content": "import { TypedEvent } from \"@node-projects/base-custom-webcomponent\";\r\nimport { userInfo } from \"./IMultiplayerService\";\r\n\r\nexport class MultiplayerService {\r\n\r\n    constructor(getUserInfo: () => userInfo, sendMessage: (message) => void, messageReceived: () => any) {\r\n    }\r\n\r\n    \r\n    cursorsChanged: TypedEvent<string>;\r\n\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/AbsolutePlacementService.ts",
    "content": "import type { IPoint } from '../../../interfaces/IPoint.js';\r\nimport type { IPlacementService } from './IPlacementService.js';\r\nimport type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { combineTransforms, extractTranslationFromDOMMatrix } from '../../helper/TransformHelper.js';\r\nimport { filterChildPlaceItems, getDesignItemCurrentPos, placeDesignItem, transformOffsetByInverseLinearMatrix } from '../../helper/LayoutHelper.js';\nimport { DesignerCanvas } from '../../widgets/designerView/designerCanvas.js';\r\nimport { ExtensionType } from '../../widgets/designerView/extensions/ExtensionType.js';\r\nimport { straightenLine } from '../../helper/PathDataPolyfill.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { hasCommandKey } from '../../helper/KeyboardHelper.js';\r\nimport { filterNonElementItems } from './DefaultPlacementService.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents, getElementZoomFactor } from '../../helper/ElementHelper.js';\r\nimport { getGeometryReader } from '../../widgets/designerView/extensions/svg/geometry/GeometryReaderFactory.js';\r\n\r\nfunction usesSvgGeometryPlacement(designItem: IDesignItem) {\r\n  const element = designItem.element;\r\n  return element instanceof SVGGraphicsElement && !(element instanceof SVGSVGElement) && !!getGeometryReader(element);\r\n}\r\n\r\nexport class AbsolutePlacementService implements IPlacementService {\r\n\r\n  //TODO: change move of elements to allow switch between transform and real move\r\n\r\n  serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem) {\r\n    if (item != null && item.getComputedStyle()?.position == 'absolute')\r\n      return true;\r\n    if (containerStyle.display === 'grid' || containerStyle.display === 'inline-grid' ||\r\n      containerStyle.display === 'flex' || containerStyle.display === 'inline-flex')\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  isEnterableContainer(container: IDesignItem) {\r\n    if (DomConverter.IsSelfClosingElement(container.element.localName))\r\n      return false;\r\n    if (!container.isRootItem && container.element.shadowRoot && container.element.shadowRoot.querySelector('slot') == null)\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  canEnter(container: IDesignItem, items: IDesignItem[]) {\r\n    if (container.instanceServiceContainer.designerCanvas.readOnly)\r\n      return false;\r\n    if (!this.isEnterableContainer(container))\r\n      return false;\r\n    if (!items.every(x => !x.element.contains(container.element) && x !== container))\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  canLeave(container: IDesignItem, items: IDesignItem[]) {\r\n    return true;\r\n  }\r\n\r\n  getElementOffset(container: IDesignItem, designItem?: IDesignItem): IPoint {\r\n    return container.instanceServiceContainer.designerCanvas.getNormalizedElementCoordinates(container.element);\r\n  }\r\n\r\n  private calculateTrack(event: MouseEvent, designerCanvas: IDesignerCanvas, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, item: IDesignItem): IPoint {\r\n    let trackX = newPoint.x - startPoint.x;\r\n    let trackY = newPoint.y - startPoint.y;\r\n\r\n    if (!hasCommandKey(event)) {\r\n      if (designerCanvas.alignOnGrid) {\r\n        let p = getDesignItemCurrentPos(item, 'position');\r\n        p.x = p.x % designerCanvas.gridSize;\r\n        p.y = p.y % designerCanvas.gridSize;\r\n        trackX = Math.round(trackX / designerCanvas.gridSize) * designerCanvas.gridSize - p.x;\r\n        trackY = Math.round(trackY / designerCanvas.gridSize) * designerCanvas.gridSize - p.y;\r\n      }\r\n      else if (designerCanvas.alignOnSnap) {\r\n        let rect = getBoundingClientRectAlsoForDisplayContents(item.element);\r\n        let newPos = designerCanvas.snapLines.snapToPosition({ x: (newPoint.x - offsetInControl.x), y: (newPoint.y - offsetInControl.y) }, { width: rect.width / designerCanvas.scaleFactor, height: rect.height / designerCanvas.scaleFactor }, { x: trackX > 0 ? 1 : -1, y: trackY > 0 ? 1 : -1 })\r\n        if (newPos.x !== null) {\r\n          trackX = newPos.x - Math.round(startPoint.x) + Math.round(offsetInControl.x);\r\n        } else {\r\n          trackX = Math.round(trackX);\r\n        }\r\n        if (newPos.y !== null) {\r\n          trackY = newPos.y - Math.round(startPoint.y) + Math.round(offsetInControl.y);\r\n        } else {\r\n          trackY = Math.round(trackY);\r\n        }\r\n      }\r\n    }\r\n    return { x: trackX, y: trackY };\r\n  }\r\n\r\n  placePoint(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]): IPoint {\r\n    let trackX = newPoint.x;\r\n    let trackY = newPoint.y;\r\n\r\n    if (!hasCommandKey(event)) {\r\n      if (designerCanvas.alignOnGrid) {\r\n        trackX = Math.round(trackX / designerCanvas.gridSize) * designerCanvas.gridSize;\r\n        trackY = Math.round(trackY / designerCanvas.gridSize) * designerCanvas.gridSize;\r\n      }\r\n      else if (designerCanvas.alignOnSnap) {\r\n        let newPos = designerCanvas.snapLines.snapToPosition({ x: newPoint.x - offsetInControl.x, y: newPoint.y - offsetInControl.y }, null, { x: trackX > 0 ? 1 : -1, y: trackY > 0 ? 1 : -1 })\r\n        if (newPos.x !== null) {\r\n          trackX = newPos.x;\r\n        } else {\r\n          trackX = Math.round(trackX);\r\n        }\r\n        if (newPos.y !== null) {\r\n          trackY = newPos.y;\r\n        } else {\r\n          trackY = Math.round(trackY);\r\n        }\r\n      }\r\n    }\r\n\r\n    return { x: trackX, y: trackY };\r\n  }\r\n\r\n  startPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n  }\r\n\r\n  place(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    //TODO: this should revert all undo actions while active\r\n    //maybe a undo actions returns itself or an id so it could be changed?\r\n    let track = this.calculateTrack(event, designerCanvas, startPoint, offsetInControl, newPoint, items[0]);\r\n\r\n    if (event.shiftKey) {\r\n      track = straightenLine({ x: 0, y: 0 }, track, 90);\r\n    }\r\n    let filteredItems = filterChildPlaceItems(filterNonElementItems(items));\r\n    for (const designItem of filteredItems) {\r\n      const canvas = designItem.instanceServiceContainer.designerCanvas.canvas;\r\n      const quad = designItem.parent.element.getBoxQuads({ relativeTo: canvas, iframes: designItem.instanceServiceContainer.designerCanvas.iframes })[0];\r\n      let transformedPoint: DOMPoint = designItem.parent.element.convertPointFromNode(new DOMPoint(track.x + quad.p1.x, track.y + quad.p1.y), <HTMLElement>canvas, { iframes: designItem.instanceServiceContainer.designerCanvas.iframes });\r\n\r\n      const cs = getComputedStyle(designItem.element);\r\n      let m = new DOMMatrix();\r\n      if (cs.rotate != 'none' && cs.rotate) {\r\n        m = m.multiply(new DOMMatrix('rotate(' + cs.rotate.replace(' ', ',') + ')'));\r\n      }\r\n      if (cs.scale != 'none' && cs.scale) {\r\n        m = m.multiply(new DOMMatrix('scale(' + cs.scale.replace(' ', ',') + ')'));\r\n      }\r\n      transformedPoint = m.inverse().transformPoint(transformedPoint);\r\n\r\n      const zoom = getElementZoomFactor(designItem.element);\r\n      if (zoom !== 1) {\r\n        // CSS zoom scales translate(), so drag previews must be converted back to local units.\r\n        transformedPoint = new DOMPoint(transformedPoint.x / zoom, transformedPoint.y / zoom);\r\n      }\r\n\r\n      const translationMatrix = new DOMMatrix().translate(transformedPoint.x, transformedPoint.y);\r\n      combineTransforms((<HTMLElement>designItem.element), designItem.getStyle('transform'), translationMatrix.toString());\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', false);\r\n  }\r\n\r\n  moveElements(designItems: IDesignItem[], position: IPoint, absolute: boolean) {\r\n    //TODO: Check if we set left or right\r\n    //TODO: Use CSS units\r\n\r\n    for (let d of designItems) {\r\n      if (position.x)\r\n        d.setStyle('left', parseInt((<HTMLElement>d.element).style.left) - position.x + 'px');\r\n      if (position.y)\r\n        d.setStyle('top', parseInt((<HTMLElement>d.element).style.top) - position.y + 'px');\r\n    }\r\n    designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshExtensions(designItems);\r\n    designItems[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(designItems, 'place', true);\r\n  }\r\n\r\n  enterContainer(container: IDesignItem, items: IDesignItem[], mode: 'normal' | 'drop') {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    for (let i of filteredItems) {\r\n      if (mode == 'drop')\r\n        i.setStyle('position', 'absolute');\r\n      container.insertChild(i);\r\n\r\n      if (i.lastContainerSize) {\r\n        if (!i.hasStyle('width'))\r\n          i.setStyle('width', i.lastContainerSize.width + 'px');\r\n        if (!i.hasStyle('height'))\r\n          i.setStyle('height', i.lastContainerSize.height + 'px');\r\n      }\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n\r\n  leaveContainer(container: IDesignItem, items: IDesignItem[]) {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n\r\n   finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    for (const designItem of filteredItems) {\r\n      let translation: DOMPoint = extractTranslationFromDOMMatrix(new DOMMatrix((<HTMLElement>designItem.element).style.transform));\r\n      const stylesMapOffset: DOMPoint = extractTranslationFromDOMMatrix(new DOMMatrix(designItem.getStyle('transform') ?? ''));\r\n      (<HTMLElement>designItem.element).style.transform = designItem.getStyle('transform') ?? '';\r\n      let track = { x: translation.x, y: translation.y };\r\n\r\n      const cs = getComputedStyle(designItem.element);\r\n      let m = new DOMMatrix();\r\n      if (cs.rotate != 'none' && cs.rotate) {\r\n        m = m.multiply(new DOMMatrix('rotate(' + cs.rotate.replace(' ', ',') + ')'));\r\n      }\r\n      if (cs.scale != 'none' && cs.scale) {\r\n        m = m.multiply(new DOMMatrix('scale(' + cs.scale.replace(' ', ',') + ')'));\r\n      }\r\n      if (!usesSvgGeometryPlacement(designItem)) {\r\n        track = m.transformPoint(track);\r\n      }\r\n\r\n      let placementOffset = { x: track.x - stylesMapOffset.x, y: track.y - stylesMapOffset.y };\n      if (usesSvgGeometryPlacement(designItem)) {\n        placementOffset = transformOffsetByInverseLinearMatrix(placementOffset, new DOMMatrix(designItem.getStyle('transform') ?? ''));\n      }\n\n      placeDesignItem(container, designItem, placementOffset, 'position');\n    }\r\n\r\n    for (const item of items) {\r\n      (<DesignerCanvas>designerCanvas).extensionManager.removeExtension(item, ExtensionType.Placement);\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/AlwaysAbsolutePlacementService.ts",
    "content": "import type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { AbsolutePlacementService } from './AbsolutePlacementService.js';\r\n\r\nexport class AlwaysAbsolutePlacementService extends AbsolutePlacementService {\r\n  override serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem) {\r\n    return true;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/DefaultPlacementService.ts",
    "content": "import type { IPoint } from '../../../interfaces/IPoint.js';\r\nimport type { IPlacementService } from './IPlacementService.js';\r\nimport type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DomConverter } from '../../widgets/designerView/DomConverter.js';\r\nimport { combineTransforms, extractTranslationFromDOMMatrix } from '../../helper/TransformHelper.js';\r\nimport { filterChildPlaceItems, getDesignItemCurrentPos, placeDesignItem, transformOffsetByInverseLinearMatrix } from '../../helper/LayoutHelper.js';\nimport { DesignerCanvas } from '../../widgets/designerView/designerCanvas.js';\r\nimport { ExtensionType } from '../../widgets/designerView/extensions/ExtensionType.js';\r\nimport { straightenLine } from '../../helper/PathDataPolyfill.js';\r\nimport { hasCommandKey } from '../../helper/KeyboardHelper.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents, getElementZoomFactor } from '../../helper/ElementHelper.js';\r\nimport { getGeometryReader } from '../../widgets/designerView/extensions/svg/geometry/GeometryReaderFactory.js';\r\n\r\nfunction usesSvgGeometryPlacement(designItem: IDesignItem) {\r\n  const element = designItem.element;\r\n  return element instanceof SVGGraphicsElement && !(element instanceof SVGSVGElement) && !!getGeometryReader(element);\r\n}\r\n\r\nexport function filterNonElementItems(items: IDesignItem[]) {\r\n  const filterdPlaceItems: IDesignItem[] = [];\r\n  next:\r\n  for (let i of items) {\r\n    if (i.nodeType === NodeType.TextNode || i.nodeType === NodeType.Comment)\r\n      continue\r\n    filterdPlaceItems.push(i);\r\n  }\r\n  return filterdPlaceItems;\r\n}\r\n\r\nexport class DefaultPlacementService implements IPlacementService {\r\n\r\n  serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem) {\r\n    if (item != null && item.getComputedStyle()?.position == 'absolute')\r\n      return true;\r\n    if (containerStyle.display === 'grid' || containerStyle.display === 'inline-grid' ||\r\n      containerStyle.display === 'flex' || containerStyle.display === 'inline-flex')\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  isEnterableContainer(container: IDesignItem) {\r\n    if (DomConverter.IsSelfClosingElement(container.element.localName))\r\n      return false;\r\n    if (!container.isRootItem && container.element.shadowRoot && container.element.shadowRoot.querySelector('slot') == null)\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  canEnter(container: IDesignItem, items: IDesignItem[]) {\r\n    if (container.instanceServiceContainer.designerCanvas.readOnly)\r\n      return false;\r\n    if (!this.isEnterableContainer(container))\r\n      return false;\r\n    if (!items.every(x => !x.element.contains(container.element) && x !== container))\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  canLeave(container: IDesignItem, items: IDesignItem[]) {\r\n    return true;\r\n  }\r\n\r\n  getElementOffset(container: IDesignItem, designItem?: IDesignItem): IPoint {\r\n    return container.instanceServiceContainer.designerCanvas.getNormalizedElementCoordinates(container.element);\r\n  }\r\n\r\n  private calculateTrack(event: MouseEvent, placementView: IDesignerCanvas, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, item: IDesignItem): IPoint {\r\n    let trackX = newPoint.x - startPoint.x;\r\n    let trackY = newPoint.y - startPoint.y;\r\n\r\n    if (!hasCommandKey(event)) {\r\n      if (placementView.alignOnGrid) {\r\n        let p = getDesignItemCurrentPos(item, 'position');\r\n        p.x = p.x % placementView.gridSize;\r\n        p.y = p.y % placementView.gridSize;\r\n        trackX = Math.round(trackX / placementView.gridSize) * placementView.gridSize - p.x;\r\n        trackY = Math.round(trackY / placementView.gridSize) * placementView.gridSize - p.y;\r\n      }\r\n      else if (placementView.alignOnSnap) {\r\n        let rect = getBoundingClientRectAlsoForDisplayContents(item.element);\r\n        let newPos = placementView.snapLines.snapToPosition({ x: (newPoint.x - offsetInControl.x), y: (newPoint.y - offsetInControl.y) }, { width: rect.width / placementView.scaleFactor, height: rect.height / placementView.scaleFactor }, { x: trackX > 0 ? 1 : -1, y: trackY > 0 ? 1 : -1 })\r\n        if (newPos.x !== null) {\r\n          trackX = newPos.x - Math.round(startPoint.x) + Math.round(offsetInControl.x);\r\n        } else {\r\n          trackX = Math.round(trackX);\r\n        }\r\n        if (newPos.y !== null) {\r\n          trackY = newPos.y - Math.round(startPoint.y) + Math.round(offsetInControl.y);\r\n        } else {\r\n          trackY = Math.round(trackY);\r\n        }\r\n      }\r\n    }\r\n    return { x: trackX, y: trackY };\r\n  }\r\n\r\n  placePoint(event: MouseEvent, placementView: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]): IPoint {\r\n    let trackX = newPoint.x;\r\n    let trackY = newPoint.y;\r\n\r\n    if (!hasCommandKey(event)) {\r\n      if (placementView.alignOnGrid) {\r\n        trackX = Math.round(trackX / placementView.gridSize) * placementView.gridSize;\r\n        trackY = Math.round(trackY / placementView.gridSize) * placementView.gridSize;\r\n      }\r\n      else if (placementView.alignOnSnap) {\r\n        let newPos = placementView.snapLines.snapToPosition({ x: newPoint.x - offsetInControl.x, y: newPoint.y - offsetInControl.y }, null, { x: trackX > 0 ? 1 : -1, y: trackY > 0 ? 1 : -1 })\r\n        if (newPos.x !== null) {\r\n          trackX = newPos.x;\r\n        } else {\r\n          trackX = Math.round(trackX);\r\n        }\r\n        if (newPos.y !== null) {\r\n          trackY = newPos.y;\r\n        } else {\r\n          trackY = Math.round(trackY);\r\n        }\r\n      }\r\n    }\r\n\r\n    return { x: trackX, y: trackY };\r\n  }\r\n\r\n  startPlace(event: MouseEvent, placementView: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n  }\r\n\r\n  place(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    //TODO: this should revert all undo actions while active\r\n    //maybe a undo actions returns itself or an id so it could be changed?\r\n    let track = this.calculateTrack(event, designerCanvas, startPoint, offsetInControl, newPoint, items[0]);\r\n\r\n    if (event.shiftKey) {\r\n      track = straightenLine({ x: 0, y: 0 }, track, 90);\r\n    }\r\n    let filteredItems = filterChildPlaceItems(filterNonElementItems(items));\r\n    for (const designItem of filteredItems) {\r\n      const canvas = designItem.instanceServiceContainer.designerCanvas.canvas;\r\n      const quad = designItem.parent.element.getBoxQuads({ relativeTo: canvas, iframes: designItem.instanceServiceContainer.designerCanvas.iframes })[0];\r\n      let transformedPoint: DOMPoint = designItem.parent.element.convertPointFromNode(new DOMPoint(track.x + quad.p1.x, track.y + quad.p1.y), <HTMLElement>canvas, { iframes: designItem.instanceServiceContainer.designerCanvas.iframes });\r\n\r\n      const cs = getComputedStyle(designItem.element);\r\n      let m = new DOMMatrix();\r\n      if (cs.rotate != 'none' && cs.rotate) {\r\n        m = m.multiply(new DOMMatrix('rotate(' + cs.rotate.replace(' ', ',') + ')'));\r\n      }\r\n      if (cs.scale != 'none' && cs.scale) {\r\n        m = m.multiply(new DOMMatrix('scale(' + cs.scale.replace(' ', ',') + ')'));\r\n      }\r\n      transformedPoint = m.inverse().transformPoint(transformedPoint);\r\n\r\n      const zoom = getElementZoomFactor(designItem.element);\r\n      if (zoom !== 1) {\r\n        // CSS zoom scales translate(), so drag previews must be converted back to local units.\r\n        transformedPoint = new DOMPoint(transformedPoint.x / zoom, transformedPoint.y / zoom);\r\n      }\r\n\r\n      const translationMatrix = new DOMMatrix().translate(transformedPoint.x, transformedPoint.y);\r\n      combineTransforms((<HTMLElement>designItem.element), designItem.getStyle('transform'), translationMatrix.toString());\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', false);\r\n  }\r\n\r\n  moveElements(designItems: IDesignItem[], position: IPoint, absolute: boolean) {\r\n    //TODO: Check if we set left or right\r\n    //TODO: Use CSS units\r\n\r\n    for (let d of designItems) {\r\n      if (position.x)\r\n        d.setStyle('left', parseInt((<HTMLElement>d.element).style.left) - position.x + 'px');\r\n      if (position.y)\r\n        d.setStyle('top', parseInt((<HTMLElement>d.element).style.top) - position.y + 'px');\r\n    }\r\n    designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshExtensions(designItems);\r\n    designItems[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(designItems, 'place', true);\r\n  }\r\n\r\n  enterContainer(container: IDesignItem, items: IDesignItem[], mode: 'normal' | 'drop') {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    for (let i of filteredItems) {\r\n      if (mode == 'drop')\r\n        i.setStyle('position', 'absolute');\r\n      container.insertChild(i);\r\n\r\n      if (i.lastContainerSize) {\r\n        if (!i.hasStyle('width'))\r\n          i.setStyle('width', i.lastContainerSize.width + 'px');\r\n        if (!i.hasStyle('height'))\r\n          i.setStyle('height', i.lastContainerSize.height + 'px');\r\n      }\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n\r\n  leaveContainer(container: IDesignItem, items: IDesignItem[]) {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n\r\n  finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    let filteredItems = filterChildPlaceItems(items);\r\n    for (const designItem of filteredItems) {\r\n      let translation: DOMPoint = extractTranslationFromDOMMatrix(new DOMMatrix((<HTMLElement>designItem.element).style.transform));\r\n      const stylesMapOffset: DOMPoint = extractTranslationFromDOMMatrix(new DOMMatrix(designItem.getStyle('transform') ?? ''));\r\n      (<HTMLElement>designItem.element).style.transform = designItem.getStyle('transform') ?? '';\r\n      let track = { x: translation.x, y: translation.y };\r\n\r\n      const cs = getComputedStyle(designItem.element);\r\n      let m = new DOMMatrix();\r\n      if (cs.rotate != 'none' && cs.rotate) {\r\n        m = m.multiply(new DOMMatrix('rotate(' + cs.rotate.replace(' ', ',') + ')'));\r\n      }\r\n      if (cs.scale != 'none' && cs.scale) {\r\n        m = m.multiply(new DOMMatrix('scale(' + cs.scale.replace(' ', ',') + ')'));\r\n      }\r\n      if (!usesSvgGeometryPlacement(designItem)) {\r\n        track = m.transformPoint(track);\r\n      }\r\n\r\n      let placementOffset = { x: track.x - stylesMapOffset.x, y: track.y - stylesMapOffset.y };\n      if (usesSvgGeometryPlacement(designItem)) {\n        placementOffset = transformOffsetByInverseLinearMatrix(placementOffset, new DOMMatrix(designItem.getStyle('transform') ?? ''));\n      }\n\n      placeDesignItem(container, designItem, placementOffset, 'position');\n    }\r\n\r\n    for (const item of items) {\r\n      (<DesignerCanvas>designerCanvas).extensionManager.removeExtension(item, ExtensionType.Placement);\r\n    }\r\n    items[0].instanceServiceContainer.designerCanvas?.raiseDesignItemsChanged(filteredItems, 'place', true);\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/FlexBoxPlacementService.ts",
    "content": "import { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { IRect } from '../../../interfaces/IRect.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../helper/ElementHelper.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DesignerCanvas } from '../../widgets/designerView/designerCanvas.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { DefaultPlacementService } from './DefaultPlacementService.js';\r\nimport { IPlacementService } from './IPlacementService.js';\r\n\r\nexport class FlexBoxPlacementService implements IPlacementService {\r\n\r\n  private _basePlacementService;\r\n\r\n  public constructor(basePlacementService: IPlacementService) {\r\n    this._basePlacementService = basePlacementService ?? new DefaultPlacementService();\r\n  }\r\n\r\n  enterContainer(container: IDesignItem, items: IDesignItem[], mode: 'normal' | 'drop') {\r\n    for (let i of items) {\r\n      container.insertChild(i);\r\n\r\n      i.removeStyle(\"position\");\r\n      i.removeStyle(\"left\");\r\n      i.removeStyle(\"top\");\r\n      i.removeStyle(\"right\");\r\n      i.removeStyle(\"transform\");\r\n    }\r\n  }\r\n\r\n  leaveContainer(container: IDesignItem, items: IDesignItem[]) {\r\n    for (let i of items) {\r\n      if (!i.lastContainerSize) {\r\n        const rect = getBoundingClientRectAlsoForDisplayContents(i.element);\r\n        i.lastContainerSize = { width: rect.width, height: rect.height };\r\n      }\r\n    }\r\n  }\r\n\r\n  serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem) {\r\n    if (containerStyle.display == 'flex' || containerStyle.display == 'inline-flex') {\r\n      if (item != null && item.getComputedStyle()?.position == 'absolute')\r\n        return false;\r\n      return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  isEnterableContainer(container: IDesignItem) {\r\n    return this._basePlacementService.isEnterableContainer(container);\r\n  }\r\n\r\n  canEnter(container: IDesignItem, items: IDesignItem[]) {\r\n    if (container.instanceServiceContainer.designerCanvas.readOnly)\r\n      return false;\r\n    return this._basePlacementService.canEnter(container, items);\r\n  }\r\n\r\n  canLeave(container: IDesignItem, items: IDesignItem[]) {\r\n    return true;\r\n  }\r\n\r\n  getElementOffset(container: IDesignItem, designItem?: IDesignItem): IPoint {\r\n    return getBoundingClientRectAlsoForDisplayContents(container.element);\r\n  }\r\n\r\n  placePoint(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]): IPoint {\r\n    const defaultPlacementService = container.serviceContainer.getLastServiceWhere('containerService', x => x instanceof DefaultPlacementService);\r\n    return defaultPlacementService.placePoint(event, designerCanvas, container, startPoint, offsetInControl, newPoint, items);\r\n  }\r\n\r\n  startPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n  }\r\n\r\n  place(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    const pos = (<IDesignerCanvas><unknown>designerCanvas).getNormalizedEventCoordinates(event);\r\n    const style = getComputedStyle(container.element);\r\n    const childrenWithPos: [IDesignItem, IRect][] = Array.from(container.children()).filter(x => !x.isEmptyTextNode).map(x => [x, designerCanvas.getNormalizedElementCoordinates(x.element)]);\r\n    if (style.flexDirection == 'row' || style.flexDirection == 'row-reverse') {\r\n      childrenWithPos.sort(x => x[1].x);\r\n      let elBefore: [IDesignItem, IRect] = null;\r\n      for (let c of childrenWithPos) {\r\n        if (c[1].x + c[1].width / 2 < pos.x) {\r\n          elBefore = c;\r\n          if (style.flexDirection == 'row-reverse')\r\n            break;\r\n        }\r\n      }\r\n\r\n      let posBefore = childrenWithPos.indexOf(elBefore);\r\n      let posDrag = childrenWithPos.indexOf(childrenWithPos.find(x => x[0] == items[0]));\r\n      if (elBefore && elBefore[0] != items[0]) {\r\n        if (style.flexDirection == 'row-reverse' && posBefore - 1 === posDrag)\r\n          return;\r\n        if (style.flexDirection == 'row' && posBefore + 1 === posDrag)\r\n          return;\r\n        const sel = [...container.instanceServiceContainer.selectionService.selectedElements];\r\n        let cg = items[0].openGroup('move in flexbox');\r\n        if (items[0].parent)\r\n          items[0].remove();\r\n        if (style.flexDirection == 'row-reverse')\r\n          elBefore[0].insertAdjacentElement(items[0], 'beforebegin');\r\n        else\r\n          elBefore[0].insertAdjacentElement(items[0], 'afterend');\r\n        cg.commit();\r\n        container.instanceServiceContainer.selectionService.setSelectedElements(sel);\r\n      } else if (elBefore == null) {\r\n        if (posDrag == 0)\r\n          return;\r\n        const sel = [...container.instanceServiceContainer.selectionService.selectedElements];\r\n        let cg = items[0].openGroup('move in flexbox');\r\n        if (items[0].parent)\r\n          items[0].remove();\r\n        if (style.flexDirection == 'row-reverse')\r\n          container.insertChild(items[0]);\r\n        else\r\n          container.insertChild(items[0], 0);\r\n        cg.commit();\r\n        container.instanceServiceContainer.selectionService.setSelectedElements(sel);\r\n      }\r\n    } else if (style.flexDirection == 'column' || style.flexDirection == 'column-reverse') {\r\n      childrenWithPos.sort(x => x[1].y);\r\n      let elBefore: [IDesignItem, IRect] = null;\r\n      for (let c of childrenWithPos) {\r\n        if (c[1].y + c[1].height / 2 < pos.y) {\r\n          elBefore = c;\r\n          if (style.flexDirection == 'column-reverse')\r\n            break;\r\n        }\r\n      }\r\n      let posBefore = childrenWithPos.indexOf(elBefore);\r\n      let posDrag = childrenWithPos.indexOf(childrenWithPos.find(x => x[0] == items[0]));\r\n      if (elBefore && elBefore[0] != items[0]) {\r\n        if (style.flexDirection == 'column-reverse' && posBefore - 1 === posDrag)\r\n          return;\r\n        if (style.flexDirection == 'column' && posBefore + 1 === posDrag)\r\n          return;\r\n        const sel = [...container.instanceServiceContainer.selectionService.selectedElements];\r\n        let cg = items[0].openGroup('move in flexbox');\r\n        if (items[0].parent)\r\n          items[0].remove();\r\n        if (style.flexDirection == 'column-reverse')\r\n          elBefore[0].insertAdjacentElement(items[0], 'beforebegin');\r\n        else\r\n          elBefore[0].insertAdjacentElement(items[0], 'afterend');\r\n        cg.commit();\r\n        container.instanceServiceContainer.selectionService.setSelectedElements(sel);\r\n      } else if (elBefore == null) {\r\n        if (posDrag == 0)\r\n          return;\r\n        const sel = [...container.instanceServiceContainer.selectionService.selectedElements];\r\n        let cg = items[0].openGroup('move in flexbox');\r\n        if (items[0].parent)\r\n          items[0].remove();\r\n        if (style.flexDirection == 'column-reverse')\r\n          container.insertChild(items[0]);\r\n        else\r\n          container.insertChild(items[0], 0);\r\n        cg.commit();\r\n        container.instanceServiceContainer.selectionService.setSelectedElements(sel);\r\n      }\r\n    }\r\n\r\n    (<DesignerCanvas>designerCanvas).extensionManager.refreshAllExtensions([container]);\r\n  }\r\n\r\n  finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n  }\r\n\r\n  moveElements(designItems: IDesignItem[], position: IPoint, absolute: boolean) {\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/GridPlacementService.ts",
    "content": "import type { IPoint } from '../../../interfaces/IPoint.js';\r\nimport type { IPlacementService } from './IPlacementService.js';\r\nimport type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { calculateGridInformation, getElementGridInformation, getGridCellFromPoint } from '../../helper/GridHelper.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { DesignerCanvas } from '../../widgets/designerView/designerCanvas.js';\r\nimport { DefaultPlacementService } from './DefaultPlacementService.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../helper/ElementHelper.js';\r\n\r\n\r\nexport class GridPlacementService implements IPlacementService {\r\n\r\n  private _basePlacementService;\r\n\r\n  public constructor(basePlacementService: IPlacementService) {\r\n    this._basePlacementService = basePlacementService ?? new DefaultPlacementService();\r\n  }\r\n\r\n  enterContainer(container: IDesignItem, items: IDesignItem[], mode: 'normal' | 'drop') {\r\n    for (let i of items) {\r\n      container.insertChild(i);\r\n\r\n      i.removeStyle(\"position\");\r\n      i.removeStyle(\"left\");\r\n      i.removeStyle(\"top\");\r\n      i.removeStyle(\"right\");\r\n      i.removeStyle(\"width\");\r\n      i.removeStyle(\"height\");\r\n      i.removeStyle(\"transform\");\r\n    }\r\n  }\r\n\r\n  leaveContainer(container: IDesignItem, items: IDesignItem[]) {\r\n    for (let i of items) {\r\n      if (!i.lastContainerSize) {\r\n        const rect = getBoundingClientRectAlsoForDisplayContents(i.element);\r\n        i.lastContainerSize = { width: rect.width, height: rect.height };\r\n      }\r\n    }\r\n  }\r\n\r\n  serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem) {\r\n    if (containerStyle.display == 'grid' || containerStyle.display == 'inline-grid') {\r\n      if (item != null && item.getComputedStyle()?.position == 'absolute')\r\n        return false;\r\n      return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  isEnterableContainer(container: IDesignItem) {\r\n    return this._basePlacementService.isEnterableContainer(container);\r\n  }\r\n\r\n  canEnter(container: IDesignItem, items: IDesignItem[]) {\r\n    if (container.instanceServiceContainer.designerCanvas.readOnly)\r\n      return false;\r\n    return this._basePlacementService.canEnter(container, items);\r\n  }\r\n\r\n  canLeave(container: IDesignItem, items: IDesignItem[]) {\r\n    return true;\r\n  }\r\n\r\n  getElementOffset(container: IDesignItem, designItem?: IDesignItem): IPoint {\r\n    return getBoundingClientRectAlsoForDisplayContents(container.element);\r\n  }\r\n\r\n  placePoint(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]): IPoint {\r\n    const defaultPlacementService = container.serviceContainer.getLastServiceWhere('containerService', x => x instanceof DefaultPlacementService);\r\n    return defaultPlacementService.placePoint(event, designerCanvas, container, startPoint, offsetInControl, newPoint, items);\r\n  }\r\n\r\n  startPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n  }\r\n\r\n  place(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    const gridInformation = calculateGridInformation(container);\r\n    const pos = designerCanvas.getNormalizedEventCoordinates(event);\r\n    //pos.x -= offsetInControl.x;\r\n    //pos.y -= offsetInControl.y;\r\n    const hoveredCell = getGridCellFromPoint(container, pos, gridInformation);\r\n    if (hoveredCell) {\r\n      const { row, column, cell } = hoveredCell;\r\n      let info = getElementGridInformation(<HTMLElement>items[0].element);\r\n      if (cell.name) {\r\n        (<HTMLElement>items[0].element).style.gridColumn = '';\r\n        (<HTMLElement>items[0].element).style.gridRow = '';\r\n        (<HTMLElement>items[0].element).style.gridArea = cell.name;\r\n      } else {\r\n        (<HTMLElement>items[0].element).style.gridArea = '';\r\n        if (info.colSpan <= 1) {\r\n          (<HTMLElement>items[0].element).style.gridColumn = '' + (column + 1);\r\n          (<HTMLElement>items[0].element).style.gridRow = '' + (row + 1);\r\n        } else {\r\n          (<HTMLElement>items[0].element).style.gridColumnStart = '' + (column + 1);\r\n          (<HTMLElement>items[0].element).style.gridRowStart = '' + (row + 1);\r\n          (<HTMLElement>items[0].element).style.gridColumnEnd = '' + (column + info.colSpan + 1);\r\n          (<HTMLElement>items[0].element).style.gridRowEnd = '' + (row + info.rowSpan + 1);\r\n        }\r\n      }\r\n    }\r\n    (<DesignerCanvas>designerCanvas).extensionManager.refreshAllExtensions([container]);\r\n  }\r\n\r\n  finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\r\n    const gridInformation = calculateGridInformation(container);\r\n    const pos = designerCanvas.getNormalizedEventCoordinates(event);\r\n    //pos.x -= offsetInControl.x;\r\n    //pos.y -= offsetInControl.y;\r\n\r\n    const hoveredCell = getGridCellFromPoint(container, pos, gridInformation);\r\n    if (hoveredCell) {\r\n      const { row, column, cell } = hoveredCell;\r\n      let info = getElementGridInformation(<HTMLElement>items[0].element);\r\n      //Grid Area is shorthand for grid row/column, to make undo work correctly we need to set befor and after clear\r\n      if (cell.name) {\r\n        items[0].setStyle('grid-area', cell.name);\r\n        items[0].removeStyle('grid-row-start');\r\n        items[0].removeStyle('grid-row-end');\r\n        items[0].removeStyle('grid-column-start');\r\n        items[0].removeStyle('grid-column-end');\r\n        items[0].removeStyle('grid-column');\r\n        items[0].removeStyle('grid-row');\r\n        items[0].setStyle('grid-area', cell.name);\r\n      } else {\r\n        if (info.colSpan <= 1) {\r\n          items[0].removeStyle('grid-area');\r\n          items[0].removeStyle('grid-row-start');\r\n          items[0].removeStyle('grid-row-end');\r\n          items[0].removeStyle('grid-column-start');\r\n          items[0].removeStyle('grid-column-end');\r\n          items[0].setStyle('grid-column', '' + (column + 1));\r\n          items[0].setStyle('grid-row', '' + (row + 1));\r\n        }\r\n        else {\r\n          items[0].removeStyle('grid-area');\r\n          items[0].removeStyle('grid-column');\r\n          items[0].removeStyle('grid-row');\r\n          items[0].setStyle('grid-column-start', '' + (column + 1));\r\n          items[0].setStyle('grid-row-start', '' + (row + 1));\r\n          items[0].setStyle('grid-column-end', '' + (column + info.colSpan + 1));\r\n          items[0].setStyle('grid-row-end', '' + (row + info.rowSpan + 1));\r\n        }\r\n      }\r\n    }\r\n    (<DesignerCanvas>designerCanvas).extensionManager.refreshAllExtensions([container]);\r\n  }\r\n\r\n  moveElements(designItems: IDesignItem[], position: IPoint, absolute: boolean) {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/IPlacementService.ts",
    "content": "import { IService } from '../IService.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\n\r\nexport interface IPlacementService extends IService {\r\n  startPlacementAllowed?(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, items: IDesignItem[]): boolean;\r\n  serviceForContainer(container: IDesignItem, containerStyle: CSSStyleDeclaration, item?: IDesignItem): boolean;\r\n  isEnterableContainer(container: IDesignItem): boolean;\r\n  canEnter(container: IDesignItem, items: IDesignItem[]): boolean;\r\n  canLeave(container: IDesignItem, items: IDesignItem[]): boolean;\r\n  enterContainer(container: IDesignItem, items: IDesignItem[], mode: 'normal' | 'drop');\r\n  leaveContainer(container: IDesignItem, items: IDesignItem[]);\r\n  getElementOffset(container: IDesignItem, designItem?: IDesignItem): IPoint;\r\n  placePoint(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]): IPoint;\r\n  startPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]);\r\n  place(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]);\r\n  finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]);\r\n  moveElements(designItems: IDesignItem[], position: IPoint, absolute: boolean);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/ISnaplinesProviderService.ts",
    "content": "import { IService } from '../IService.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\n\r\nexport interface ISnaplinesProviderService extends IService {\r\n  provideSnaplines(containerItem: IDesignItem, ignoredItems: IDesignItem[]): {\r\n    outerRect: DOMRect,\r\n    positionsH: [number, IRect][],\r\n    positionsMiddleH: [number, IRect][],\r\n    positionsV: [number, IRect][],\r\n    positionsMiddleV: [number, IRect][]\r\n  };\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/placementService/SnaplinesProviderService.ts",
    "content": "import { IRect } from '../../../interfaces/IRect.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../helper/ElementHelper.js';\r\nimport type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ISnaplinesProviderService } from './ISnaplinesProviderService.js';\r\n\r\nexport const provideSnaplinesWithDistance = 'provideSnaplinesWithDistance';\r\nexport const provideSnaplinesWithDistanceDistance = 'provideSnaplinesWithDistanceDistance';\r\n\r\nexport class SnaplinesProviderService implements ISnaplinesProviderService {\r\n  provideSnaplines(containerItem: IDesignItem, ignoredItems: IDesignItem[]) {\r\n    {\r\n      const canvas = containerItem.instanceServiceContainer.designerCanvas;\r\n      const ignMap = new Map<Element, IDesignItem>(ignoredItems.map(i => [i.element, i]));\r\n      const outerRect = getBoundingClientRectAlsoForDisplayContents(containerItem.element);\r\n\r\n      const provideWithDist = canvas.instanceServiceContainer.designContext.extensionOptions[provideSnaplinesWithDistance] !== false;\r\n      const provideWithDistDist = canvas.instanceServiceContainer.designContext.extensionOptions[provideSnaplinesWithDistanceDistance] ?? 5;\r\n\r\n      const positionsH: [number, IRect][] = [];\r\n      const positionsMiddleH: [number, IRect][] = [];\r\n      const positionsV: [number, IRect][] = [];\r\n      const positionsMiddleV: [number, IRect][] = [];\r\n\r\n      const tw = containerItem.document.createTreeWalker(containerItem.usableContainer, NodeFilter.SHOW_ELEMENT);\r\n      let n: Element = <Element>tw.nextNode();\r\n      while (n != null) {\r\n        if (ignMap.has(<Element>n)) {\r\n          n = <Element>tw.nextSibling();\r\n        } else {\r\n          const p = getBoundingClientRectAlsoForDisplayContents((<Element>n));\r\n          const pLeft = (p.x - outerRect.x) / canvas.scaleFactor;\r\n          const pMidH = (p.x - outerRect.x + p.width / 2) / canvas.scaleFactor;\r\n          const pRight = (p.x - outerRect.x + p.width) / canvas.scaleFactor;\r\n          const pTop = (p.y - outerRect.y) / canvas.scaleFactor;\r\n          const pMidV = (p.y - outerRect.y + p.height / 2) / canvas.scaleFactor;\r\n          const pBottom = (p.y - outerRect.y + p.height) / canvas.scaleFactor;\r\n          const transformedP: IRect = { x: pLeft + outerRect.x, y: pTop + outerRect.y, width: p.width / canvas.scaleFactor, height: p.height / canvas.scaleFactor };\r\n\r\n          if (provideWithDist)\r\n            positionsH.push([pLeft - provideWithDistDist, transformedP]);\r\n          positionsH.push([pLeft, transformedP]);\r\n          positionsMiddleH.push([pMidH, transformedP]);\r\n          positionsH.push([pRight, transformedP]);\r\n          if (provideWithDist)\r\n            positionsH.push([pRight + provideWithDistDist, transformedP]);\r\n\r\n          if (provideWithDist)\r\n            positionsV.push([pTop - provideWithDistDist, transformedP]);\r\n          positionsV.push([pTop, transformedP]);\r\n          positionsMiddleV.push([pMidV, transformedP]);\r\n          positionsV.push([pBottom, transformedP]);\r\n          if (provideWithDist)\r\n            positionsV.push([pBottom + provideWithDistDist, transformedP]);\r\n\r\n          n = <Element>tw.nextNode();\r\n        }\r\n      }\r\n      positionsH.push([0, { x: 0, y: 0, width: 0, height: 0 }]);\r\n      positionsH.sort((a, b) => a[0] - b[0]);\r\n      positionsMiddleH.sort((a, b) => a[0] - b[0]);\r\n      positionsV.push([0, { x: 0, y: 0, width: 0, height: 0 }])\r\n      positionsV.sort((a, b) => a[0] - b[0]);\r\n      positionsMiddleV.sort((a, b) => a[0] - b[0]);\r\n\r\n      return { outerRect, positionsH, positionsMiddleH, positionsV, positionsMiddleV }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/pngCreatorService/DisplayMediaPngWriterService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\nimport { Screenshot } from \"../../helper/Screenshot.js\";\nimport { requestAnimationFramePromise, sleep } from \"../../helper/Helper.js\";\nimport { IPngCreatorService } from \"./IPngCreatorService.js\";\nimport { DesignerCanvas } from \"../../widgets/designerView/designerCanvas.js\";\n\nexport class DisplayMediaPngWriterService implements IPngCreatorService {\n    async takePng(designItems: IDesignItem[], options?: { margin?: number, removeSelection?: boolean }): Promise<Uint8Array> {\n        if (!designItems || designItems.length === 0) {\n            return null;\n        }\n\n        const designerCanvas = designItems[0].instanceServiceContainer.designerCanvas;\n        const selectionService = designItems[0].instanceServiceContainer.selectionService;\n        const oldZoomFactor = designerCanvas.zoomFactor;\n        const oldPos = designerCanvas.canvasOffset;\n        const oldSelected = selectionService.selectedElements;\n\n        try {\n            await Screenshot.enableScreenshots();\n\n            (<DesignerCanvas>designerCanvas).disableBackgroud();\n            designerCanvas.zoomFactor = 1;\n            if (options?.removeSelection) {\n                selectionService.setSelectedElements([]);\n            }\n            designerCanvas.canvasOffset = { x: 0, y: 0 };\n            await requestAnimationFramePromise();\n\n            let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n            for (const item of designItems) {\n                const rect = designerCanvas.getNormalizedElementCoordinates(item.element);\n                minX = Math.min(minX, rect.x);\n                minY = Math.min(minY, rect.y);\n                maxX = Math.max(maxX, rect.x + rect.width);\n                maxY = Math.max(maxY, rect.y + rect.height);\n            }\n\n            const margin = options?.margin ?? 0;\n            minX -= margin;\n            minY -= margin;\n            maxX += margin;\n            maxY += margin;\n\n            const totalWidth = Math.ceil(maxX - minX);\n            const totalHeight = Math.ceil(maxY - minY);\n\n            // Use the outer viewport element for screenshots so that the crop\n            // coordinates are not affected by the canvasOffset CSS transform.\n            // Its dimensions reflect the actual visible capture area, which may\n            // differ from canvas.offsetWidth when designerWidth/Height != 100%.\n            const viewportElement = (<DesignerCanvas>designerCanvas).outercanvas2;\n            const viewportW = viewportElement.offsetWidth;\n            const viewportH = viewportElement.offsetHeight;\n\n            // Inset by 1 CSS pixel on each edge to avoid border artifacts when stitching tiles\n            const borderInset = 1;\n            const effectiveW = viewportW - 2 * borderInset;\n            const effectiveH = viewportH - 2 * borderInset;\n\n            const numTilesX = Math.ceil(totalWidth / effectiveW);\n            const numTilesY = Math.ceil(totalHeight / effectiveH);\n\n            const dpr = window.devicePixelRatio || 1;\n            const captureW = Math.ceil(viewportW * dpr);\n            const captureH = Math.ceil(viewportH * dpr);\n            const insetPx = Math.ceil(borderInset * dpr);\n            const effectiveCaptureW = captureW - 2 * insetPx;\n            const effectiveCaptureH = captureH - 2 * insetPx;\n\n            const finalCanvas = document.createElement('canvas');\n            finalCanvas.width = Math.ceil(totalWidth * dpr);\n            finalCanvas.height = Math.ceil(totalHeight * dpr);\n            const finalCtx = finalCanvas.getContext('2d');\n\n            let sleepTime = 1000;\n\n            for (let iy = 0; iy < numTilesY; iy++) {\n                for (let ix = 0; ix < numTilesX; ix++) {\n                    const tileX = minX + ix * effectiveW;\n                    const tileY = minY + iy * effectiveH;\n\n                    // Shift by borderInset so the 1px border falls outside the effective region\n                    designerCanvas.canvasOffset = { x: -(tileX - borderInset), y: -(tileY - borderInset) };\n                    // Wait for CSS transform to apply and video stream to capture the updated frame\n                    await sleep(sleepTime);\n                    sleepTime = 300;\n\n                    const dataUrl = await Screenshot.takeScreenshot(\n                        viewportElement,\n                        captureW,\n                        captureH\n                    );\n\n                    const img = await this._loadImage(dataUrl);\n                    // Draw only the inner region, skipping the 1px border on all sides\n                    finalCtx.drawImage(\n                        img,\n                        insetPx, insetPx, effectiveCaptureW, effectiveCaptureH,\n                        ix * effectiveCaptureW, iy * effectiveCaptureH, effectiveCaptureW, effectiveCaptureH\n                    );\n                }\n            }\n\n            const blob = await new Promise<Blob>(resolve => finalCanvas.toBlob(resolve, 'image/png'));\n            const arrayBuffer = await blob.arrayBuffer();\n            return new Uint8Array(arrayBuffer);\n        } finally {\n            designerCanvas.zoomFactor = oldZoomFactor;\n            designerCanvas.canvasOffset = oldPos;\n            (<DesignerCanvas>designerCanvas).enableBackground();\n            if (options?.removeSelection) {\n                selectionService.setSelectedElements(oldSelected);\n            }\n        }\n    }\n\n    private _loadImage(dataUrl: string): Promise<HTMLImageElement> {\n        return new Promise((resolve, reject) => {\n            const img = new Image();\n            img.onload = () => resolve(img);\n            img.onerror = reject;\n            img.src = dataUrl;\n        });\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/pngCreatorService/ElectronPngWriterService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\nimport { requestAnimationFramePromise, sleep } from \"../../helper/Helper.js\";\nimport { IPngCreatorService } from \"./IPngCreatorService.js\";\nimport { DesignerCanvas } from \"../../widgets/designerView/designerCanvas.js\";\n\n/**\n * PNG writer service for Electron that uses BrowserWindow.capturePage()\n * instead of getDisplayMedia(). No recording rect is shown and no user\n * permission prompt is needed.\n *\n * The constructor accepts a capturePageFn callback that should invoke\n * BrowserWindow.capturePage() (via IPC from the renderer) and return\n * a data-URL of the captured image.\n *\n * Example usage from an Electron renderer process:\n *\n *   const { ipcRenderer } = require('electron');\n *\n *   const capturePageFn = async (rect) => {\n *     // main process calls: mainWindow.webContents.capturePage(rect)\n *     return await ipcRenderer.invoke('capture-page', rect);\n *   };\n *\n *   serviceContainer.register('pngCreatorService',\n *     new ElectronPngWriterService(capturePageFn));\n */\nexport class ElectronPngWriterService implements IPngCreatorService {\n    private _capturePageFn: (rect: { x: number, y: number, width: number, height: number }) => Promise<string>;\n\n    /**\n     * @param capturePageFn  A function that captures a portion of the current\n     *   BrowserWindow and returns a data-URL (e.g. \"data:image/png;base64,…\").\n     *   The rect is in CSS pixels relative to the page (matching Electron's\n     *   capturePage rectangle).\n     */\n    constructor(capturePageFn: (rect: { x: number, y: number, width: number, height: number }) => Promise<string>) {\n        this._capturePageFn = capturePageFn;\n    }\n\n    async takePng(designItems: IDesignItem[], options?: { margin?: number, removeSelection?: boolean }): Promise<Uint8Array> {\n        if (!designItems || designItems.length === 0) {\n            return null;\n        }\n\n        const designerCanvas = designItems[0].instanceServiceContainer.designerCanvas;\n        const selectionService = designItems[0].instanceServiceContainer.selectionService;\n        const oldZoomFactor = designerCanvas.zoomFactor;\n        const oldPos = designerCanvas.canvasOffset;\n        const oldSelected = selectionService.selectedElements;\n\n        try {\n            (<DesignerCanvas>designerCanvas).disableBackgroud();\n            designerCanvas.zoomFactor = 1;\n            if (options?.removeSelection) {\n                selectionService.setSelectedElements([]);\n            }\n            designerCanvas.canvasOffset = { x: 0, y: 0 };\n            await requestAnimationFramePromise();\n\n            let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n            for (const item of designItems) {\n                const rect = designerCanvas.getNormalizedElementCoordinates(item.element);\n                minX = Math.min(minX, rect.x);\n                minY = Math.min(minY, rect.y);\n                maxX = Math.max(maxX, rect.x + rect.width);\n                maxY = Math.max(maxY, rect.y + rect.height);\n            }\n\n            const margin = options?.margin ?? 0;\n            minX -= margin;\n            minY -= margin;\n            maxX += margin;\n            maxY += margin;\n\n            const totalWidth = Math.ceil(maxX - minX);\n            const totalHeight = Math.ceil(maxY - minY);\n\n            // Use the outer viewport element to determine the capture rectangle\n            // so that coordinates are not affected by the canvasOffset CSS transform.\n            // Its dimensions reflect the actual visible capture area, which may\n            // differ from canvas.offsetWidth when designerWidth/Height != 100%.\n            const viewportElement = (<DesignerCanvas>designerCanvas).outercanvas2;\n            const viewportW = viewportElement.offsetWidth;\n            const viewportH = viewportElement.offsetHeight;\n\n            // Inset by 1 CSS pixel on each edge to avoid border artifacts when stitching tiles\n            const borderInset = 1;\n            const effectiveW = viewportW - 2 * borderInset;\n            const effectiveH = viewportH - 2 * borderInset;\n\n            const numTilesX = Math.ceil(totalWidth / effectiveW);\n            const numTilesY = Math.ceil(totalHeight / effectiveH);\n\n            const dpr = window.devicePixelRatio || 1;\n            const captureW = Math.ceil(viewportW * dpr);\n            const captureH = Math.ceil(viewportH * dpr);\n            const insetPx = Math.ceil(borderInset * dpr);\n            const effectiveCaptureW = captureW - 2 * insetPx;\n            const effectiveCaptureH = captureH - 2 * insetPx;\n\n            const finalCanvas = document.createElement('canvas');\n            finalCanvas.width = Math.ceil(totalWidth * dpr);\n            finalCanvas.height = Math.ceil(totalHeight * dpr);\n            const finalCtx = finalCanvas.getContext('2d');\n\n            for (let iy = 0; iy < numTilesY; iy++) {\n                for (let ix = 0; ix < numTilesX; ix++) {\n                    const tileX = minX + ix * effectiveW;\n                    const tileY = minY + iy * effectiveH;\n\n                    // Shift by borderInset so the 1px border falls outside the effective region\n                    designerCanvas.canvasOffset = { x: -(tileX - borderInset), y: -(tileY - borderInset) };\n                    // Wait for CSS transform to apply and the renderer to paint the new frame\n                    await requestAnimationFramePromise();\n                    await sleep(100);\n\n                    const vpRect = viewportElement.getBoundingClientRect();\n                    const dataUrl = await this._capturePageFn({\n                        x: Math.round(vpRect.left),\n                        y: Math.round(vpRect.top),\n                        width: Math.round(vpRect.width),\n                        height: Math.round(vpRect.height)\n                    });\n\n                    const img = await this._loadImage(dataUrl);\n                    // Draw only the inner region, skipping the 1px border on all sides\n                    finalCtx.drawImage(\n                        img,\n                        insetPx, insetPx, effectiveCaptureW, effectiveCaptureH,\n                        ix * effectiveCaptureW, iy * effectiveCaptureH, effectiveCaptureW, effectiveCaptureH\n                    );\n                }\n            }\n\n            const blob = await new Promise<Blob>(resolve => finalCanvas.toBlob(resolve, 'image/png'));\n            const arrayBuffer = await blob.arrayBuffer();\n            return new Uint8Array(arrayBuffer);\n        } finally {\n            designerCanvas.zoomFactor = oldZoomFactor;\n            designerCanvas.canvasOffset = oldPos;\n            (<DesignerCanvas>designerCanvas).enableBackground();\n            if (options?.removeSelection) {\n                selectionService.setSelectedElements(oldSelected);\n            }\n        }\n    }\n\n    private _loadImage(dataUrl: string): Promise<HTMLImageElement> {\n        return new Promise((resolve, reject) => {\n            const img = new Image();\n            img.onload = () => resolve(img);\n            img.onerror = reject;\n            img.src = dataUrl;\n        });\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/pngCreatorService/IPngCreatorService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\n\nexport interface IPngCreatorService {\n    takePng(designItems: IDesignItem[], options?: { margin?: number, removeSelection?: boolean }): Promise<Uint8Array>\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/DefaultEditorTypeService.ts",
    "content": "import { IEditorTypeService } from \"./IEditorTypeService\";\nimport { ColorInput } from \"../../controls/ColorEditor.js\";\n\nexport class DefaultEditorTypeService implements IEditorTypeService {\n    getEditor(type: string, additional: { changedCallback: (newValue: any) => void, [key: string]: any }): { element: HTMLElement, getValue: () => any, setValue: (value: any) => void } {\n        if (type === 'color' || type === 'css-color') {\n            const input = document.createElement('node-projects-color-input') as ColorInput;\n            input.addEventListener('change', () => additional.changedCallback(input.value));\n            return {\n                element: input,\n                getValue: () => input.value,\n                setValue: (value: any) => { input.value = value; }\n            };\n        }\n\n        const input = document.createElement('input');\n        input.type = type;\n        if (additional.min !== undefined) input.min = additional.min;\n        if (additional.max !== undefined) input.max = additional.max;\n        if (additional.step !== undefined) input.step = additional.step;\n        input.addEventListener('change', (event) => additional.changedCallback((event.target as HTMLInputElement).value));\n        return {\n            element: input,\n            getValue: () => input.value,\n            setValue: (value: any) => { input.value = value; }\n        };\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/DefaultPropertyEditorTypesService.ts",
    "content": "import { IPropertyEditorTypesService } from './IPropertyEditorTypesService.js';\r\nimport { IProperty } from './IProperty.js';\r\nimport { IPropertyEditor } from './IPropertyEditor.js';\r\nimport { ColorPropertyEditor } from './propertyEditors/ColorPropertyEditor.js';\r\nimport { DatePropertyEditor } from './propertyEditors/DatePropertyEditor.js';\r\nimport { JsonPropertyEditor } from './propertyEditors/JsonPropertyEditor.js';\r\nimport { NumberPropertyEditor } from './propertyEditors/NumberPropertyEditor.js';\r\nimport { SelectPropertyEditor } from './propertyEditors/SelectPropertyEditor.js';\r\nimport { TextPropertyEditor } from './propertyEditors/TextPropertyEditor.js';\r\nimport { BooleanPropertyEditor } from './propertyEditors/BooleanPropertyEditor.js';\r\nimport { ImageButtonListPropertyEditor } from './propertyEditors/ImageButtonListPropertyEditor.js';\r\nimport { ThicknessPropertyEditor } from \"./propertyEditors/ThicknessPropertyEditor.js\";\r\nimport { FontPropertyEditor } from './propertyEditors/FontPropertyEditor.js';\r\nimport { UnitPropertyEditor } from './propertyEditors/UnitPropertyEditor.js';\nimport { isUnitPropertyType } from './propertyEditors/UnitPropertyEditorConfig.js';\nimport { PropertyType } from './PropertyType.js';\nimport { CssPropertyEditor } from './propertyEditors/CssPropertyEditor.js';\n\nexport class DefaultPropertyEditorTypesService implements IPropertyEditorTypesService {\n  getEditorForProperty(property: IProperty): IPropertyEditor {\n    if (property.propertyType === PropertyType.cssValue)\n      return new CssPropertyEditor(property, p => this._getEditorForProperty(p));\n\n    return this._getEditorForProperty(property);\n  }\n\n  private _getEditorForProperty(property: IProperty): IPropertyEditor {\n    if (property.createEditor)\n      return property.createEditor(property);\n\r\n    if (isUnitPropertyType(property.type, property.name))\r\n      return new UnitPropertyEditor(property);\r\n\r\n    switch (<string><any>property.type) {\r\n      case \"json\":\r\n        {\r\n          return new JsonPropertyEditor(property);\r\n        }\r\n      case \"css-color\":\r\n      case \"color\":\r\n        {\r\n          return new ColorPropertyEditor(property);\r\n        }\r\n      case \"font\":\r\n        {\r\n          return new FontPropertyEditor(property);\r\n        }\r\n      case \"date\":\r\n        {\r\n          return new DatePropertyEditor(property);\r\n        }\r\n      case \"number\":\r\n        {\r\n          return new NumberPropertyEditor(property);\r\n        }\r\n      case \"list\":\r\n        {\r\n          return new SelectPropertyEditor(property);\r\n        }\r\n      case \"enum\":\r\n        {\r\n          return new SelectPropertyEditor(property);\r\n        }\r\n      case \"boolean\":\r\n        {\r\n          return new BooleanPropertyEditor(property);\r\n        }\r\n      case \"img-list\":\r\n        {\r\n          return new ImageButtonListPropertyEditor(property);\r\n        }\r\n      case \"thickness\":\r\n        {\r\n          return new ThicknessPropertyEditor(property);\r\n        }\r\n      case \"string\":\r\n      default:\r\n        {\r\n          return new TextPropertyEditor(property);\r\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IEditorTypeService.ts",
    "content": "export interface IEditorTypeService {\r\n   getEditor(type: string, additional: { changedCallback: (newValue: any) => void }): { element: HTMLElement, getValue: () => any, setValue: (value: any) => void }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IPropertiesService.ts",
    "content": "import { IProperty } from './IProperty.js';\r\nimport { IService } from '../IService.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ValueType } from './ValueType.js';\r\nimport { BindingTarget } from '../../item/BindingTarget.js';\r\nimport { IBinding } from '../../item/IBinding.js';\r\nimport { IPropertyGroup } from './IPropertyGroup.js';\r\nimport { IContextMenuItem } from '../../helper/contextMenu/IContextMenuItem.js';\r\n\r\nexport enum RefreshMode {\r\n  none,\r\n  full,\r\n  fullOnValueChange,\r\n  fullOnClassChange\r\n}\r\n\r\nexport interface IPropertiesService extends IService {\r\n  getRefreshMode(designItem: IDesignItem): RefreshMode;\r\n\r\n  isHandledElement(designItem: IDesignItem): boolean;\r\n  getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]>;\r\n  getProperty(designItem: IDesignItem, name: string): Promise<IProperty>;\r\n  getBinding(designItems: IDesignItem[], property: IProperty): IBinding\r\n  getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget;\r\n\r\n  getPropertyNameSuggestions?(designItems: IDesignItem[]): string[];\r\n\r\n  setValue(designItems: IDesignItem[], property: IProperty, value: any): Promise<void>;\r\n  previewValue?(designItems: IDesignItem[], property: IProperty, value: any): Promise<void>;\r\n  removePreviewValue?(designItems: IDesignItem[], property: IProperty): Promise<void>;\r\n  clearValue(designItems: IDesignItem[], property: IProperty, clearType: 'all' | 'binding' | 'value');\r\n  isSet(designItems: IDesignItem[], property: IProperty): ValueType;\r\n  getValue(designItems: IDesignItem[], property: IProperty): any;\r\n  getUnsetValue(designItems: IDesignItem[], property: IProperty): any;\r\n\r\n  getContextMenu(designItems: IDesignItem[], property: IProperty): IContextMenuItem[];\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IProperty.ts",
    "content": "import { IPropertiesService } from './IPropertiesService.js';\r\nimport { IPropertyEditor } from './IPropertyEditor.js';\r\nimport { PropertyType } from './PropertyType.js';\r\nimport type { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\nexport interface IProperty {\r\n  name: string;\r\n  renamable?: boolean;\n  displayName?: string; // nused in property grid\n  hideLabel?: boolean; // property grid renders editor across the label and editor columns\n  propertyName?: string; // normaly camelCased name of property (also used for css properties)\n  group?: string;\r\n  attributeName?: string; // normaly dash seperated name of property\r\n  description?: string;\r\n  type?: 'addNew' | 'json' | 'color' | 'date' | 'number' | 'list' | 'enum' | 'boolean' | 'img-lis' | 'thickness' | 'css-length' | 'length' | 'angle' | 'time' | 'frequency' | 'flex' | 'resolution' | 'string' | string; // -> string, number, list, color, thickness, css numeric types\r\n  default?: any; //what was this for? remove???\r\n  min?: number;\r\n  max?: number;\r\n  step?: number;\r\n  readonly?: boolean;\r\n  values?: string[]; // list selectable values\r\n  units?: string[]; // selectable units for editors that support unit changes\r\n  unitSteps?: Record<string, number>;\r\n  numericValueDecimalPlaces?: number; // rounding used by numeric unit conversions\r\n  numericValueConverter?: (value: number, fromUnit: string, toUnit: string, property: IProperty, numericType: string, numberText?: string, rawValue?: string, designItems?: IDesignItem[]) => string | number | null | undefined;\r\n  enumValues?: [name: string, value: string | number][]; // list selectable enum values\r\n  createEditor?: (property: IProperty) => IPropertyEditor;\r\n  value?: any;\r\n  service: IPropertiesService;\r\n  defaultValue?: any;\r\n  example?: string;\r\n  propertyType: PropertyType; // => needed for pg-grid. for example property only types can only be used for binding, css property types could never bind two way...\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IPropertyEditor.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IProperty } from './IProperty.js';\r\nimport { ValueType } from './ValueType.js';\r\n\r\nexport interface IPropertyEditor {\r\n  readonly element: Element;\r\n  property: IProperty;\r\n  designItems: IDesignItem[];\r\n  designItemsChanged(designItems: IDesignItem[]);\r\n  refreshValue(valueType: ValueType, value: any);\r\n  refreshValueWithoutNotification(valueType: ValueType, value: any);\r\n}\r\n\r\nexport interface IPropertyEditorT<T extends Element> extends IPropertyEditor {\r\n  readonly element: T;\r\n  property: IProperty;\r\n  designItems: IDesignItem[];\r\n  designItemsChanged(designItems: IDesignItem[]);\r\n  refreshValue(valueType: ValueType, value: any);\r\n  refreshValueWithoutNotification(valueType: ValueType, value: any);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IPropertyEditorTypesService.ts",
    "content": "import { IProperty } from './IProperty.js';\r\nimport { IPropertyEditor } from './IPropertyEditor.js';\r\n\r\nexport interface IPropertyEditorTypesService {\r\n  getEditorForProperty(property: IProperty): IPropertyEditor | null;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IPropertyGroup.ts",
    "content": "import { IProperty } from './IProperty.js';\r\n\r\nexport interface IPropertyGroup {\r\n  name: string;\r\n  description?: string;\r\n  properties: IProperty[]\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/IPropertyGroupsService.ts",
    "content": "\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IPropertiesService } from './IPropertiesService.js';\r\n\r\nexport interface IPropertyGroupsService {\r\n    getPropertygroups(designItems: IDesignItem[]): { name: string, propertiesService: IPropertiesService }[]\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/PropertyGroupsService.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { IPropertiesService } from './IPropertiesService.js';\r\nimport { IPropertyGroupsService } from './IPropertyGroupsService.js';\r\nimport { AttachedPropertiesService } from './services/AttachedPropertiesService.js';\r\nimport { AttributesPropertiesService } from './services/AttributesPropertiesService.js';\r\nimport { CommonPropertiesService } from './services/CommonPropertiesService.js';\r\nimport { CssCurrentPropertiesService } from './services/CssCurrentPropertiesService.js';\r\nimport { CssCustomPropertiesService } from './services/CssCustomPropertiesService.js';\r\nimport { CssPropertiesService } from './services/CssPropertiesService.js';\r\n\r\nexport class PropertyGroupsService implements IPropertyGroupsService {\r\n    protected _attachedPropertiesService = new AttachedPropertiesService();\r\n\r\n    protected _rootPgList: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'styles', propertiesService: new CssCurrentPropertiesService() },\r\n        { name: 'css vars', propertiesService: new CssCustomPropertiesService() },\r\n        { name: 'layout', propertiesService: new CssPropertiesService(\"layout\") },\r\n    ];\r\n\r\n    protected _pgList: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'properties', propertiesService: null },\r\n        { name: 'attached', propertiesService: this._attachedPropertiesService },\r\n        { name: 'attributes', propertiesService: new AttributesPropertiesService() },\r\n        { name: 'common', propertiesService: new CommonPropertiesService() },\r\n        { name: 'styles', propertiesService: new CssCurrentPropertiesService() },\r\n        { name: 'css vars', propertiesService: new CssCustomPropertiesService() },\r\n        { name: 'layout', propertiesService: new CssPropertiesService(\"layout\") },\r\n    ];\r\n\r\n    protected _svgPgList: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'properties', propertiesService: null },\r\n        { name: 'attached', propertiesService: this._attachedPropertiesService },\r\n        { name: 'attributes', propertiesService: new AttributesPropertiesService() },\r\n        { name: 'common', propertiesService: new CommonPropertiesService() },\r\n        { name: 'styles', propertiesService: new CssCurrentPropertiesService() },\r\n        { name: 'css vars', propertiesService: new CssCustomPropertiesService() },\r\n        { name: 'layout', propertiesService: new CssPropertiesService(\"layout\") },\r\n        { name: 'svg', propertiesService: new CssPropertiesService(\"svg\") },\r\n    ];\r\n\r\n    protected _svgChildPgList: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'properties', propertiesService: null },\r\n        { name: 'attached', propertiesService: this._attachedPropertiesService },\r\n        { name: 'attributes', propertiesService: new AttributesPropertiesService() },\r\n        { name: 'common', propertiesService: new CommonPropertiesService() },\r\n        { name: 'styles', propertiesService: new CssCurrentPropertiesService() },\r\n        { name: 'css vars', propertiesService: new CssCustomPropertiesService() },\r\n        { name: 'layout', propertiesService: new CssPropertiesService(\"layoutSvgChild\") },\r\n    ];\r\n\r\n    protected _gridChild: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'gridChild', propertiesService: new CssPropertiesService(\"gridChild\") },\r\n    ];\r\n\r\n    protected _grid: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'grid', propertiesService: new CssPropertiesService(\"grid\") },\r\n    ];\r\n\r\n    protected _flexChild: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'flexChild', propertiesService: new CssPropertiesService(\"flexChild\") },\r\n    ];\r\n\r\n    protected _flex: { name: string; propertiesService: IPropertiesService; }[] = [\r\n        { name: 'flex', propertiesService: new CssPropertiesService(\"flex\") },\r\n    ];\r\n\r\n    getPropertygroups(designItems: IDesignItem[]): { name: string; propertiesService: IPropertiesService; }[] {\r\n        if (designItems == null || designItems.length == 0)\r\n            return [];\r\n        if (designItems[0].nodeType == NodeType.TextNode || designItems[0].nodeType == NodeType.Comment)\r\n            return [];\r\n        if (designItems[0].isRootItem) {\r\n            const style = designItems[0].getComputedStyle();\r\n            let lst = this._rootPgList;\r\n            if (style.display.includes('grid'))\r\n                lst = [...lst, ...this._grid];\r\n            else if (style.display.includes('flex'))\r\n                lst = [...lst, ...this._flex];\r\n            return lst;\r\n        }\r\n\r\n        this._svgChildPgList[0].propertiesService = this._svgPgList[0].propertiesService = this._pgList[0].propertiesService = designItems[0].serviceContainer.getLastServiceWhere('propertyService', x => x.isHandledElement(designItems[0]));\r\n\r\n        let lst = this._pgList;\r\n        if (designItems[0].element instanceof designItems[0].window.SVGElement) {\r\n            if (designItems[0].element instanceof designItems[0].window.SVGSVGElement) {\r\n                lst = this._svgPgList;\r\n            } else {\r\n                lst = this._svgChildPgList;\r\n            }\r\n        }\r\n\r\n        const style = designItems[0].getComputedStyle();\r\n        if (style.display.includes('grid'))\r\n            lst = [...lst, ...this._grid];\r\n        else if (style.display.includes('flex'))\r\n            lst = [...lst, ...this._flex];\r\n\r\n        if (designItems[0].parent) {\r\n            const parentStyle = designItems[0].parent.getComputedStyle();\r\n            if (parentStyle.display.includes('grid'))\r\n                lst = [...lst, ...this._gridChild];\r\n            else if (parentStyle.display.includes('flex'))\r\n                lst = [...lst, ...this._flexChild];\r\n        }\r\n        return lst;\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/PropertyMutationHandling.ts",
    "content": "export type AttributeMutationRecordLike = Pick<MutationRecord, 'type' | 'attributeName' | 'oldValue'> & {\n  target: unknown;\n};\n\ntype AttributeTargetLike = { getAttribute?: (qualifiedName: string) => string | null; };\n\nfunction getMutationAttributeValue(mutation: AttributeMutationRecordLike): string | null {\n  const target = mutation.target as AttributeTargetLike;\n  if (typeof target.getAttribute !== 'function' || !mutation.attributeName)\n    return null;\n  return target.getAttribute(mutation.attributeName);\n}\n\nfunction getStyleDeclarationNames(styleText: string | null, filter?: (name: string) => boolean): Set<string> {\n  const names = new Set<string>();\n  if (!styleText)\n    return names;\n\n  let name = '';\n  let inValue = false;\n  let quote: string = null;\n  for (let i = 0; i < styleText.length; i++) {\n    const char = styleText[i];\n\n    if (quote) {\n      if (char === '\\\\') {\n        i++;\n        continue;\n      }\n      if (char === quote)\n        quote = null;\n      continue;\n    }\n\n    if (char === '\\'' || char === '\"') {\n      quote = char;\n      continue;\n    }\n\n    if (!inValue) {\n      if (char === ':') {\n        inValue = true;\n        continue;\n      }\n      if (char === ';') {\n        name = '';\n        continue;\n      }\n      name += char;\n      continue;\n    }\n\n    if (char === ';') {\n      const trimmedName = name.trim();\n      if (trimmedName && (!filter || filter(trimmedName)))\n        names.add(trimmedName);\n      name = '';\n      inValue = false;\n    }\n  }\n\n  if (inValue) {\n    const trimmedName = name.trim();\n    if (trimmedName && (!filter || filter(trimmedName)))\n      names.add(trimmedName);\n  } else {\n    const trimmedName = name.trim();\n    if (!name)\n      return names;\n    if (!filter || filter(trimmedName))\n      names.add(trimmedName);\n  }\n  return names;\n}\n\nfunction areSetsEqual(left: Set<string>, right: Set<string>): boolean {\n  if (left.size !== right.size)\n    return false;\n  for (const value of left) {\n    if (!right.has(value))\n      return false;\n  }\n  return true;\n}\n\nexport function didAttributePresenceChange(mutation: AttributeMutationRecordLike): boolean {\n  if (mutation.type !== 'attributes' || !mutation.attributeName)\n    return false;\n\n  const currentValue = getMutationAttributeValue(mutation);\n  return mutation.oldValue == null || currentValue == null;\n}\n\nexport function didStyleDeclarationSetChange(mutation: AttributeMutationRecordLike, filter?: (name: string) => boolean): boolean {\n  if (mutation.type !== 'attributes' || mutation.attributeName !== 'style')\n    return false;\n\n  const currentValue = getMutationAttributeValue(mutation);\n  const oldNames = getStyleDeclarationNames(mutation.oldValue, filter);\n  const currentNames = getStyleDeclarationNames(currentValue, filter);\n  return !areSetsEqual(oldNames, currentNames);\n}\n\nexport function didCustomStyleDeclarationSetChange(mutation: AttributeMutationRecordLike): boolean {\n  return didStyleDeclarationSetChange(mutation, name => name.startsWith('--'));\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/PropertyType.ts",
    "content": "export enum PropertyType {\n    property = 'property',\n    attribute = 'attribute',\n    propertyAndAttribute = 'propertyAndAttribute',\n    cssValue = 'cssvalue',\n\n    complex = 'complex', // editor is special and could write multiple properties\n    add = 'add' // editor allows to add a new one\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/ValueType.ts",
    "content": "export enum ValueType {\r\n  'none' = 'none',\r\n  'all' = 'all',\r\n  'some' = 'some',\r\n  'bound' = 'bound',\r\n  'fromStylesheet' = 'fromStylesheet'\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/AnglePropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class AnglePropertyEditor extends BasePropertyEditor<HTMLInputElement> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement('input');\r\n    element.type = \"number\";\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n    element.min = <string><any>property.min ?? '0';\r\n    element.max = <string><any>property.max ?? '360';\r\n    element.step = <string><any>property.step;\r\n    element.onchange = (e) => this._valueChanged(element.value == '' ? null : (element.valueAsNumber + 'deg'));\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.valueAsNumber = value === undefined ? null : parseFloat(value);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/BasePropertyEditor.ts",
    "content": "import { IPropertyEditorT } from '../IPropertyEditor.js';\r\nimport { IProperty } from '../IProperty.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\n\r\nexport abstract class BasePropertyEditor<T extends HTMLElement> implements IPropertyEditorT<T> {\r\n\r\n  public element: T;\r\n  public property: IProperty;\r\n  public designItems: IDesignItem[];\r\n\r\n  protected disableChangeNotification: boolean = false;\r\n\r\n  constructor(property: IProperty) {\r\n    this.property = property;\r\n  }\r\n\r\n  protected async _previewValueChanged(newValue) {\r\n    if (!this.disableChangeNotification) {\r\n      if (this.designItems && this.designItems.length)\r\n        await this.property.service.previewValue?.(this.designItems, this.property, newValue);\r\n    }\r\n  }\r\n\r\n  protected async _removePreviewValue() {\r\n    if (!this.disableChangeNotification) {\r\n      if (this.designItems && this.designItems.length)\r\n        await this.property.service.removePreviewValue?.(this.designItems, this.property);\r\n    }\r\n  }\r\n\r\n  protected async _valueChanged(newValue) {\r\n    if (!this.disableChangeNotification) {\r\n      if (this.designItems && this.designItems.length) {\r\n        const cg = this.designItems[0].openGroup(\"set property: \" + this.property.name);\r\n        if (newValue == null)\r\n          this.property.service.clearValue(this.designItems, this.property, 'value');\r\n        else\r\n          await this.property.service.setValue(this.designItems, this.property, newValue);\r\n        cg.commit();\r\n      }\r\n    }\r\n  }\r\n\r\n  public designItemsChanged(designItems: IDesignItem[]) {\r\n    this.designItems = designItems;\r\n  }\r\n\r\n  abstract refreshValue(valueType: ValueType, value: any);\r\n\r\n  public refreshValueWithoutNotification(valueType: ValueType, value: any) {\r\n    if (valueType == ValueType.none)\r\n      this.element.classList.add('unset-value');\r\n    else\r\n      this.element.classList.remove('unset-value');\r\n    this.disableChangeNotification = true;\r\n    try {\r\n      this.refreshValue(valueType, value);\r\n    } catch (err) {\r\n      console.error(err);\r\n    }\r\n    this.disableChangeNotification = false;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/BooleanPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class BooleanPropertyEditor extends BasePropertyEditor<HTMLInputElement> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement('input');\r\n    element.type = \"checkbox\";\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n    element.onchange = (e) => this._valueChanged(element.checked);\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.checked = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/ColorPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { ColorInput } from '../../../controls/ColorEditor.js';\r\n\r\nexport class ColorPropertyEditor extends BasePropertyEditor<ColorInput> {\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement('node-projects-color-input') as ColorInput;\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n    element.onchange = async (e) => {\r\n      await this.property.service.removePreviewValue?.(this.designItems, this.property);\r\n      this._valueChanged(element.value);\r\n    };\r\n    element.oninput = async (e) => {\r\n      await this.property.service.previewValue?.(this.designItems, this.property, element.value);\r\n    };\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    if (!value)\r\n      this.element.value = 'rgba(0, 0, 0, 1)';\r\n    else\r\n      this.element.value = value;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/CssPropertyEditor.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\nimport { appendCssImportant, splitCssImportant } from '../../../helper/CssImportant.js';\nimport { IPropertiesService } from '../IPropertiesService.js';\nimport { IProperty } from '../IProperty.js';\nimport { IPropertyEditor } from '../IPropertyEditor.js';\nimport { ValueType } from '../ValueType.js';\n\nexport class CssPropertyEditor implements IPropertyEditor {\n  public element: HTMLDivElement;\n  public property: IProperty;\n  public designItems: IDesignItem[];\n\n  private _innerEditor: IPropertyEditor;\n  private _importantButton: HTMLButtonElement;\n  private _value: any;\n  private _disableChangeNotification = false;\n\n  constructor(property: IProperty, createInnerEditor: (property: IProperty) => IPropertyEditor) {\n    this.property = property;\n\n    const innerProperty = {\n      ...property,\n      service: this._createServiceProxy(property.service)\n    };\n    this._innerEditor = createInnerEditor(innerProperty);\n\n    this._importantButton = document.createElement('button');\n    this._importantButton.type = 'button';\n    this._importantButton.textContent = '!';\n    this._importantButton.title = '!important';\n    this._importantButton.disabled = property.readonly === true;\n    this._importantButton.style.width = '18px';\n    this._importantButton.style.height = '18px';\n    this._importantButton.style.margin = '-2px 0 0 -2px';\n    this._importantButton.style.padding = '0';\n    this._importantButton.style.border = '1px solid var(--input-border-color, #596c7a)';\n    this._importantButton.style.background = 'transparent';\n    this._importantButton.style.color = 'lightslategray';\n    this._importantButton.style.fontWeight = 'bold';\n    this._importantButton.style.lineHeight = '16px';\n    this._importantButton.style.cursor = 'pointer';\n    this._importantButton.onclick = async () => {\n      if (this._disableChangeNotification || !this.designItems?.length || this._value == null || this._value === '')\n        return;\n\n      this._setImportant(!this._isImportant());\n      await this.property.service.setValue(this.designItems, this.property, this._appendImportant(this._value));\n      this.designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshAllExtensions(this.designItems);\n    };\n\n    this.element = document.createElement('div');\n    this.element.style.display = 'grid';\n    this.element.style.gridTemplateColumns = 'minmax(0, 1fr) 18px';\n    this.element.style.alignItems = 'center';\n    this.element.style.gap = '2px';\n    this.element.appendChild(this._innerEditor.element);\n    this.element.appendChild(this._importantButton);\n  }\n\n  private _createServiceProxy(service: IPropertiesService): IPropertiesService {\n    const proxy = Object.create(service);\n    proxy.setValue = async (designItems, property, value) =>\n      service.setValue(designItems, this.property, this._appendImportant(value));\n    proxy.previewValue = async (designItems, property, value) =>\n      service.previewValue?.(designItems, this.property, this._appendImportant(value));\n    proxy.removePreviewValue = async (designItems, property) =>\n      service.removePreviewValue?.(designItems, this.property);\n    proxy.clearValue = (designItems, property, clearType) =>\n      service.clearValue(designItems, this.property, clearType);\n    return proxy;\n  }\n\n  private _appendImportant(value: any) {\n    if (typeof value !== 'string')\n      return value;\n\n    this._value = value;\n    return appendCssImportant(value, this._isImportant());\n  }\n\n  public designItemsChanged(designItems: IDesignItem[]) {\n    this.designItems = designItems;\n    this._innerEditor.designItemsChanged(designItems);\n  }\n\n  private _refreshImportant(valueType: ValueType, value: any) {\n    const parsedValue = typeof value === 'string' ? splitCssImportant(value) : { value, important: false };\n    this._value = parsedValue.value;\n    this._setImportant(parsedValue.important);\n    this._importantButton.disabled = this.property.readonly === true || valueType == ValueType.none || parsedValue.value == null || parsedValue.value === '';\n    this._importantButton.style.cursor = this._importantButton.disabled ? 'default' : 'pointer';\n  }\n\n  private _isImportant() {\n    return this._importantButton.dataset.checked === 'true';\n  }\n\n  private _setImportant(important: boolean) {\n    this._importantButton.dataset.checked = important ? 'true' : 'false';\n    this._importantButton.style.background = important ? 'orange' : 'transparent';\n    this._importantButton.style.color = important ? 'black' : 'lightslategray';\n  }\n\n  refreshValue(valueType: ValueType, value: any) {\n    this._refreshImportant(valueType, value);\n    this._innerEditor.refreshValue(valueType, this._value);\n  }\n\n  public refreshValueWithoutNotification(valueType: ValueType, value: any) {\n    if (valueType == ValueType.none)\n      this.element.classList.add('unset-value');\n    else\n      this.element.classList.remove('unset-value');\n\n    this._disableChangeNotification = true;\n    try {\n      this._refreshImportant(valueType, value);\n      this._innerEditor.refreshValueWithoutNotification(valueType, this._value);\n    } catch (err) {\n      console.error(err);\n    }\n    this._disableChangeNotification = false;\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/DatePropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class DatePropertyEditor extends BasePropertyEditor<HTMLInputElement> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n   \r\n    let element = document.createElement('input');\r\n    element.type = \"datetime-local\";\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n    element.onchange = (e) => this._valueChanged(element.value);\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    if (!value)\r\n      this.element.value = null;\r\n    else\r\n      this.element.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/DefaultPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { ServiceContainer } from '../../ServiceContainer.js';\r\nimport { IEditorTypeService } from '../IEditorTypeService.js';\r\n\r\nexport class TextPropertyEditor extends BasePropertyEditor<HTMLElement> {\r\n\r\n  editor: ReturnType<IEditorTypeService['getEditor']>;\r\n\r\n  constructor(property: IProperty, serviceContainer: ServiceContainer) {\r\n    super(property);\r\n\r\n    const editor = serviceContainer.forSomeServicesTillResult('editorTypeService',\r\n      x => x.getEditor(property.type, {\r\n        changedCallback: (newValue) => this._valueChanged(newValue)\r\n      }));\r\n    this.editor = editor;\r\n    this.element = editor.element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.editor.setValue(value);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/FontPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class FontPropertyEditor extends BasePropertyEditor<HTMLSelectElement> {\r\n\r\n  static fontList: string[];\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement(\"select\");\r\n    if (property.readonly)\r\n      element.disabled = true;\r\n    this.element = element;\r\n\r\n    FontPropertyEditor.addFontsToSelect(element);\r\n    this.element.onchange = (e) => this._valueChanged(this.element.value);\r\n  }\r\n\r\n  static addFontsToSelect(select: HTMLSelectElement) {\r\n    if (FontPropertyEditor.fontList) {\r\n      FontPropertyEditor.parseFontList(select);\r\n      //@ts-ignore\r\n    } else if (window.queryLocalFonts) {\r\n      //@ts-ignore\r\n      window.queryLocalFonts().then(x => {\r\n        //@ts-ignore\r\n        FontPropertyEditor.fontList = [...new Set(x.map(y => y.family))];\r\n        FontPropertyEditor.parseFontList(select);\r\n      })\r\n    } else {\r\n      FontPropertyEditor.fontList = [\"Verdana\", \"Arial\", \"Tahoma\", \"Trebuchet MS\", \"Times New Roman\", \"Georgia\", \"Garamond\", \"Courier New\", \"Brush Script MT\"];\r\n      FontPropertyEditor.parseFontList(select);\r\n    }\r\n  }\r\n\r\n  private static parseFontList(select: HTMLSelectElement) {\r\n    for (let v of FontPropertyEditor.fontList) {\r\n      let option = document.createElement(\"option\");\r\n      option.value = <any>v;\r\n      option.text = v;\r\n      select.appendChild(option);\r\n    }\r\n\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/ImageButtonListPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { ImageButtonListSelector } from \"../../../controls/ImageButtonListSelector.js\";\r\nimport { PropertiesHelper } from \"../services/PropertiesHelper.js\";\r\nimport { assetsPath } from \"../../../../Constants.js\";\r\n\r\nexport class ImageButtonListPropertyEditor extends BasePropertyEditor<ImageButtonListSelector> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    const selector = new ImageButtonListSelector()\r\n    selector.property = property.name;\r\n    selector.unsetValue = property.defaultValue;\r\n    const propName = PropertiesHelper.camelToDashCase(property.name);\r\n    if (property.type == 'enum') {\r\n      for (let v of property.enumValues) {\r\n        let button = document.createElement(\"button\");\r\n        button.dataset.value = <string>v[1];\r\n        let img = document.createElement(\"img\");\r\n        img.title = <string>v[1];\r\n        img.src = assetsPath + 'images/chromeDevtools/' + propName + '-' + v[1] + '-icon.svg';\r\n        button.appendChild(img);\r\n        selector.appendChild(button);\r\n      }\r\n    } else {\r\n      for (let v of property.values) {\r\n        let button = document.createElement(\"button\");\r\n        button.dataset.value = v;\r\n        let img = document.createElement(\"img\");\r\n        img.title = v;\r\n        img.src = assetsPath + 'images/chromeDevtools/' + propName + '-' + v + '-icon.svg';\r\n        button.appendChild(img);\r\n        selector.appendChild(button);\r\n      }\r\n    }\r\n    selector.addEventListener('value-changed', (e) => this._valueChanged(selector.value));\r\n    this.element = selector;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/JsonPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { html } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class JsonPropertyEditor extends BasePropertyEditor<HTMLDivElement> {\r\n\r\n  static template = html`\r\n    <div style=\"display: flex;\">\r\n      <input id=\"input\" type=\"text\">\r\n      <button style=\"width: 30px;\">...</button>\r\n    </div>\r\n  `;\r\n\r\n  _input: HTMLInputElement;\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let el = <HTMLDivElement>JsonPropertyEditor.template.content.cloneNode(true);\r\n    this._input = <HTMLInputElement>(<DocumentFragment><any>el).getElementById('input')\r\n    if (property.readonly)\r\n      this._input.readOnly = true;\r\n    this._input.onchange = (e) => this._valueChanged(this._input.value);\r\n    this.element = el;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this._input.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/JsonPropertyPopupEditor.ts",
    "content": "import { html, BaseCustomWebComponentConstructorAppend } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class JsonPropertyPopupEditor extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override template = html`\r\n    <div style=\"display: flex;\">\r\n      <input id=\"input\" type=\"text\">\r\n      <button style=\"width: 30px;\">...</button>\r\n    </div>\r\n  `;\r\n\r\n  constructor() {\r\n    super();\r\n\r\n    //codeview\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/NumberPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class NumberPropertyEditor extends BasePropertyEditor<HTMLInputElement> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n   \r\n    let element = document.createElement('input');\r\n    element.type = \"number\";\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n    element.min = <string><any>property.min;\r\n    element.max = <string><any>property.max;\r\n    element.step = <string><any>property.step;\r\n    element.onchange = (e) => this._valueChanged(element.value == '' ? null : element.valueAsNumber);\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.value = value === undefined ? null : value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/SelectPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class SelectPropertyEditor extends BasePropertyEditor<HTMLDivElement> {\r\n\r\n  elementSelect: HTMLSelectElement;\r\n  elementInput: HTMLInputElement;\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement(\"div\");\r\n\r\n    let elementSel = document.createElement(\"select\");\r\n    if (property.type == 'enum') {\r\n      for (let v of property.enumValues) {\r\n        let option = document.createElement(\"option\");\r\n        option.value = <any>v[1];\r\n        option.text = v[0];\r\n        elementSel.appendChild(option);\r\n      }\r\n    } else {\r\n      for (let v of property.values) {\r\n        let option = document.createElement(\"option\");\r\n        option.value = v;\r\n        option.text = v;\r\n        elementSel.appendChild(option);\r\n      }\r\n    }\r\n    if (property.readonly)\r\n      elementSel.disabled = true;\r\n    elementSel.onchange = (e) => this._valueChanged(elementSel.value);\r\n\r\n    let elementInput = document.createElement(\"input\");\r\n    elementInput.style.display = 'none'\r\n    elementInput.onchange = (e) => this._valueChanged(elementInput.value);\r\n\r\n    this.elementSelect = elementSel;\r\n    this.elementInput = elementInput;\r\n    element.appendChild(this.elementSelect);\r\n    element.appendChild(this.elementInput);\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.elementSelect.style.display = 'block';\r\n    this.elementInput.style.display = 'none';\r\n    this.elementSelect.value = value;\r\n    if (this.elementSelect.value != value && value) {\r\n      this.elementInput.value = value;\r\n      this.elementSelect.style.display = 'none';\r\n      this.elementInput.style.display = 'block';\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/TextPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\n\r\nexport class TextPropertyEditor extends BasePropertyEditor<HTMLInputElement> {\r\n\r\n  private static _nextListId = 0;\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    let element = document.createElement('input');\r\n    element.type = \"text\";\r\n    if (property.readonly)\r\n      element.readOnly = true;\r\n\r\n    if (property.values?.length) {\r\n      const listId = `wcd-text-property-editor-values-${TextPropertyEditor._nextListId++}`;\r\n      const datalist = document.createElement('datalist');\r\n      datalist.id = listId;\r\n\r\n      for (const value of property.values) {\r\n        const option = document.createElement('option');\r\n        option.value = value;\r\n        datalist.appendChild(option);\r\n      }\r\n\r\n      element.setAttribute('list', listId);\r\n      element.appendChild(datalist);\r\n    }\r\n\r\n    element.onchange = (e) => this._valueChanged(element.value);\r\n    element.onfocus = (e) => {\r\n      element.selectionStart = 0;\r\n      element.selectionEnd = element.value?.length;\r\n    }\r\n    this.element = element;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    if (value == null)\r\n      this.element.value = \"\";\r\n    else\r\n      this.element.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/ThicknessPropertyEditor.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { ThicknessEditor } from '../../../controls/ThicknessEditor.js';\r\n\r\nexport class ThicknessPropertyEditor extends BasePropertyEditor<ThicknessEditor> {\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n\r\n    const selector = new ThicknessEditor()\r\n    selector.property = property.name;\r\n    \r\n    selector.valueLeftChanged.on((e) => this._valueChanged(e.newValue));\r\n    this.element = selector;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this.element.valueLeft = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/UnitPropertyEditor.ts",
    "content": "import { NumericStyleInput } from '../../../controls/NumericStyleInput.js';\nimport { IDesignItem } from '../../../item/IDesignItem.js';\nimport { IProperty } from '../IProperty.js';\nimport { BasePropertyEditor } from './BasePropertyEditor.js';\nimport { ValueType } from '../ValueType.js';\nimport { getCssNumericEditorConfig } from './UnitPropertyEditorConfig.js';\n\nexport class UnitPropertyEditor extends BasePropertyEditor<NumericStyleInput> {\n\n  constructor(property: IProperty) {\n    super(property);\n\n    const config = getCssNumericEditorConfig(property);\n    const selector = new NumericStyleInput();\n    selector.units = config?.units ?? property.units ?? [];\n    selector.fixedValues = config?.fixedValues ?? property.values;\n    selector.unitValueConverter = args => config?.convertValue({ ...args, designItems: this.designItems });\n    selector.unitSteps = config?.unitSteps ?? property.unitSteps ?? {};\n    selector.step = property.step;\n    selector.min = property.min;\n    selector.max = property.max;\n    selector.readOnly = property.readonly;\n    selector.valueChanged.on(async (e) => this._valueChanged(e.newValue === '' ? null : e.newValue));\n    selector.valuePreviewChanged.on(async (e) => this._previewValueChanged(e.newValue === '' ? null : e.newValue));\n    selector.valuePreviewFinished.on(async (e) => {\n      await this._removePreviewValue();\n      if (e.wasCancelled)\n        return;\n      await this._valueChanged(e.newValue === '' ? null : e.newValue);\n    });\n    this.element = selector;\n  }\n\n  public override designItemsChanged(designItems: IDesignItem[]) {\n    super.designItemsChanged(designItems);\n  }\n\n  refreshValue(valueType: ValueType, value: any) {\n    this.element.value = value == null ? '' : String(value);\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/UnitPropertyEditorConfig.ts",
    "content": "import type { IDesignItem } from '../../../item/IDesignItem.js';\nimport type { IProperty } from '../IProperty.js';\n\nexport type UnitPropertyType = 'css-length' | 'css-angle' | 'css-time' | 'css-frequency' | 'css-flex' | 'css-resolution' | 'css-scale' | 'svg-length';\n\nexport type UnitConversionResult = string | number | null | undefined;\n\nexport type UnitConversionContext = {\n  property: IProperty,\n  numericType: UnitPropertyType,\n  designItems?: IDesignItem[],\n  value: number,\n  numberText: string,\n  rawValue: string,\n  fromUnit: string,\n  toUnit: string\n};\n\nexport type UnitEditorConfig = {\n  numericType: UnitPropertyType,\n  units: string[],\n  fixedValues: string[],\n  unitSteps: Record<string, number>,\n  convertValue: (context: Omit<UnitConversionContext, 'property' | 'numericType'>) => string\n};\n\nconst cssNumericKeywordValues = ['initial', 'inherit', 'unset'];\n\nexport const defaultCssNumericUnits: Record<UnitPropertyType, string[]> = {\n  \"css-length\": ['px', '%', 'em', 'rem', 'vw', 'vh', 'vmin', 'vmax', 'pt', 'pc', 'in', 'cm', 'mm', 'q', 'ch', 'ex'],\n  \"css-angle\": ['deg', 'grad', 'rad', 'turn'],\n  \"css-time\": ['ms', 's'],\n  \"css-frequency\": ['hz', 'khz'],\n  \"css-flex\": ['fr'],\n  \"css-resolution\": ['dpi', 'dpcm', 'dppx', 'x'],\n  \"css-scale\": ['', '%'],\n  \"svg-length\": ['', '%']\n};\n\nconst editorTypes = Object.keys(defaultCssNumericUnits)\n\nexport const defaultCssNumericUnitSteps: Record<UnitPropertyType, Record<string, number>> = {\n  \"css-length\": { em: 0.1, rem: 0.1, 'in': 0.01, cm: 0.1, ch: 0.1, ex: 0.1 },\n  \"css-angle\": { rad: 0.01, turn: 0.1 },\n  \"css-time\": { ms: 10, s: 0.1 },\n  \"css-frequency\": { khz: 0.1 },\n  \"css-flex\": { fr: 0.1 },\n  \"css-resolution\": { dppx: 0.1, x: 0.1 },\n  \"css-scale\": { '': 0.1, '%': 10 },\n  \"svg-length\": { '': 1, '%': 10 }\n};\n\nconst absoluteLengthUnitInPx = new Map<string, number>([\n  ['px', 1],\n  ['in', 96],\n  ['cm', 96 / 2.54],\n  ['mm', 96 / 25.4],\n  ['q', 96 / 101.6],\n  ['pt', 96 / 72],\n  ['pc', 16]\n]);\n\nconst angleUnitInDeg = new Map<string, number>([\n  ['deg', 1],\n  ['grad', 0.9],\n  ['rad', 180 / Math.PI],\n  ['turn', 360]\n]);\n\nconst timeUnitInMs = new Map<string, number>([\n  ['ms', 1],\n  ['s', 1000]\n]);\n\nconst frequencyUnitInHz = new Map<string, number>([\n  ['hz', 1],\n  ['khz', 1000]\n]);\n\nconst resolutionUnitInDpi = new Map<string, number>([\n  ['dpi', 1],\n  ['dpcm', 2.54],\n  ['dppx', 96],\n  ['x', 96]\n]);\n\nconst horizontalPercentageReferencePropertyNames = new Set([\n  'width',\n  'minWidth',\n  'maxWidth',\n  'inlineSize',\n  'minInlineSize',\n  'maxInlineSize',\n  'left',\n  'right',\n  'columnGap',\n  'gap',\n  'flexBasis',\n  'perspective'\n]);\n\nconst verticalPercentageReferencePropertyNames = new Set([\n  'height',\n  'minHeight',\n  'maxHeight',\n  'top',\n  'bottom',\n  'blockSize',\n  'minBlockSize',\n  'maxBlockSize',\n  'rowGap'\n]);\n\nconst horizontalMeasuredSizePropertyNames = new Set([\n  'width',\n  'minWidth',\n  'maxWidth',\n  'inlineSize',\n  'minInlineSize',\n  'maxInlineSize'\n]);\n\nconst verticalMeasuredSizePropertyNames = new Set([\n  'height',\n  'minHeight',\n  'maxHeight',\n  'blockSize',\n  'minBlockSize',\n  'maxBlockSize'\n]);\n\nfunction formatNumericStyleInputNumber(value: number, maxDecimalPlaces: number = 4): string {\n  if (!Number.isFinite(value))\n    return '0';\n  const factor = 10 ** Math.max(0, maxDecimalPlaces);\n  const roundedValue = Math.round(value * factor) / factor;\n  return Object.is(roundedValue, -0) ? '0' : `${roundedValue}`;\n}\n\nfunction combineNumericStyleInputValue(numberText: string, unit: string): string {\n  const trimmedNumberText = numberText?.trim() ?? '';\n  if (!trimmedNumberText)\n    return '';\n  return trimmedNumberText + (unit ?? '');\n}\n\nfunction parseNumericStyleInputValue(value?: string | null) {\n  const text = value?.trim() ?? '';\n  if (!text)\n    return { kind: 'empty' } as const;\n\n  const match = text.match(/^([+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+))(?:([a-z%]+))?$/i);\n  if (!match)\n    return { kind: 'text', text } as const;\n\n  const numericValue = Number(match[1]);\n  if (Number.isNaN(numericValue))\n    return { kind: 'text', text } as const;\n\n  return {\n    kind: 'numeric' as const,\n    numberText: match[1],\n    value: numericValue,\n    unit: match[2]?.toLowerCase() ?? ''\n  };\n}\n\nfunction dashToCamelCase(text?: string): string {\n  return text?.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase()) ?? '';\n}\n\nfunction camelToDashCase(text?: string): string {\n  return text?.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`) ?? '';\n}\n\nfunction getNumericValueDecimalPlaces(context: UnitConversionContext): number {\n  return context.property.numericValueDecimalPlaces ?? (context.fromUnit === '%' || context.toUnit === '%' ? 2 : 4);\n}\n\nfunction normalizeConvertedValue(result: UnitConversionResult, toUnit: string, decimalPlaces: number): string | null {\n  if (result == null)\n    return null;\n  if (typeof result === 'number')\n    return combineNumericStyleInputValue(formatNumericStyleInputNumber(result, decimalPlaces), toUnit);\n  return result;\n}\n\nfunction getPrimaryElement(context: UnitConversionContext): HTMLElement | null {\n  return <HTMLElement>context.designItems?.[0]?.element ?? null;\n}\n\nfunction isHTMLElementLike(value: unknown): value is HTMLElement {\n  if (!value)\n    return false;\n  if (typeof HTMLElement !== 'undefined' && value instanceof HTMLElement)\n    return true;\n  return typeof (value as { getBoundingClientRect?: unknown }).getBoundingClientRect === 'function';\n}\n\nfunction getParentReferenceElement(element: HTMLElement): HTMLElement | null {\n  if (!element)\n    return null;\n  if (element.parentElement)\n    return element.parentElement;\n  if (element.assignedSlot)\n    return element.assignedSlot;\n\n  const rootNode = element.getRootNode?.();\n  const host = (rootNode as { host?: unknown })?.host;\n  if (isHTMLElementLike(host))\n    return host;\n\n  const ownerDocument = element.ownerDocument;\n  return ownerDocument?.body ?? ownerDocument?.documentElement ?? null;\n}\n\nfunction getComputedPixelValue(value: string | null | undefined): number | null {\n  const numericValue = parseFloat(value ?? '');\n  if (Number.isNaN(numericValue))\n    return null;\n  return numericValue;\n}\n\nfunction readComputedCssValue(context: UnitConversionContext): string | null {\n  if (typeof getComputedStyle !== 'function')\n    return null;\n\n  const element = getPrimaryElement(context);\n  if (!element)\n    return null;\n\n  const computedStyle = getComputedStyle(element);\n  const propertyName = context.property.propertyName ?? context.property.name;\n  if (!propertyName)\n    return null;\n\n  const getPropertyValue = typeof (computedStyle as CSSStyleDeclaration & { getPropertyValue?: unknown }).getPropertyValue === 'function'\n    ? (name: string) => computedStyle.getPropertyValue(name)\n    : (_name: string) => '';\n\n  if (propertyName.startsWith('--'))\n    return getPropertyValue(propertyName)?.trim() ?? null;\n\n  const dashName = propertyName.includes('-') ? propertyName : camelToDashCase(propertyName);\n  const directValue = getPropertyValue(dashName)?.trim();\n  if (directValue)\n    return directValue;\n\n  const camelName = dashToCamelCase(propertyName);\n  return (computedStyle as CSSStyleDeclaration & Record<string, string>)[camelName]?.trim() ?? null;\n}\n\nfunction getConvertibleNumericContext(context: UnitConversionContext): UnitConversionContext | null {\n  const parsedRawValue = parseNumericStyleInputValue(context.rawValue);\n  if (parsedRawValue.kind === 'numeric') {\n    return {\n      ...context,\n      value: parsedRawValue.value,\n      numberText: parsedRawValue.numberText,\n      fromUnit: parsedRawValue.unit || context.fromUnit,\n      rawValue: combineNumericStyleInputValue(parsedRawValue.numberText, parsedRawValue.unit)\n    };\n  }\n\n  const computedValue = readComputedCssValue(context);\n  const parsedComputedValue = parseNumericStyleInputValue(computedValue);\n  if (parsedComputedValue.kind !== 'numeric') {\n    if (context.numericType !== 'css-length')\n      return null;\n\n    const measuredSizeInPx = getMeasuredSizeInPx(context);\n    if (measuredSizeInPx == null)\n      return null;\n\n    const numberText = formatNumericStyleInputNumber(measuredSizeInPx);\n    return {\n      ...context,\n      value: measuredSizeInPx,\n      numberText,\n      fromUnit: 'px',\n      rawValue: combineNumericStyleInputValue(numberText, 'px')\n    };\n  }\n\n  return {\n    ...context,\n    value: parsedComputedValue.value,\n    numberText: parsedComputedValue.numberText,\n    fromUnit: parsedComputedValue.unit,\n    rawValue: combineNumericStyleInputValue(parsedComputedValue.numberText, parsedComputedValue.unit)\n  };\n}\n\nfunction getViewportUnitInPx(unit: string): number | null {\n  if (typeof window === 'undefined')\n    return null;\n  switch (unit) {\n    case 'vw':\n      return window.innerWidth / 100;\n    case 'vh':\n      return window.innerHeight / 100;\n    case 'vmin':\n      return Math.min(window.innerWidth, window.innerHeight) / 100;\n    case 'vmax':\n      return Math.max(window.innerWidth, window.innerHeight) / 100;\n    default:\n      return null;\n  }\n}\n\nfunction getFontSizeReferenceInPx(context: UnitConversionContext, relativeToParent: boolean): number | null {\n  if (typeof getComputedStyle !== 'function')\n    return null;\n  const element = getPrimaryElement(context);\n  if (!element)\n    return null;\n\n  const referenceElement = relativeToParent ? (getParentReferenceElement(element) ?? element) : element;\n  return getComputedPixelValue(getComputedStyle(referenceElement).fontSize);\n}\n\nfunction getRootFontSizeInPx(): number | null {\n  if (typeof getComputedStyle !== 'function' || typeof document === 'undefined')\n    return null;\n  return getComputedPixelValue(getComputedStyle(document.documentElement).fontSize);\n}\n\nfunction getPercentageReferenceInPx(context: UnitConversionContext): number | null {\n  if (typeof getComputedStyle !== 'function')\n    return null;\n  const element = getPrimaryElement(context);\n  if (!element)\n    return null;\n\n  const propertyName = dashToCamelCase(context.property.propertyName ?? context.property.name);\n  if (propertyName === 'fontSize')\n    return getFontSizeReferenceInPx(context, true);\n  if (propertyName === 'lineHeight')\n    return getFontSizeReferenceInPx(context, false);\n\n  const referenceElement = getParentReferenceElement(element) ?? element;\n  const rect = referenceElement.getBoundingClientRect?.();\n  if (!rect)\n    return null;\n\n  if (verticalPercentageReferencePropertyNames.has(propertyName))\n    return rect.height;\n  if (horizontalPercentageReferencePropertyNames.has(propertyName))\n    return rect.width;\n  return rect.width;\n}\n\nfunction getMeasuredSizeInPx(context: UnitConversionContext): number | null {\n  const element = getPrimaryElement(context);\n  if (!element)\n    return null;\n\n  const rect = element.getBoundingClientRect?.();\n  if (!rect)\n    return null;\n\n  const propertyName = dashToCamelCase(context.property.propertyName ?? context.property.name);\n  if (horizontalMeasuredSizePropertyNames.has(propertyName))\n    return rect.width;\n  if (verticalMeasuredSizePropertyNames.has(propertyName))\n    return rect.height;\n\n  return null;\n}\n\nfunction getLengthUnitSizeInPx(context: UnitConversionContext, unit: string): number | null {\n  const normalizedUnit = unit?.toLowerCase() ?? '';\n  if (!normalizedUnit)\n    return null;\n\n  const absoluteUnitSize = absoluteLengthUnitInPx.get(normalizedUnit);\n  if (absoluteUnitSize != null)\n    return absoluteUnitSize;\n\n  if (normalizedUnit === '%') {\n    const percentageReference = getPercentageReferenceInPx(context);\n    return percentageReference == null ? null : percentageReference / 100;\n  }\n\n  if (normalizedUnit === 'em')\n    return getFontSizeReferenceInPx(context, dashToCamelCase(context.property.propertyName ?? context.property.name) === 'fontSize');\n  if (normalizedUnit === 'rem')\n    return getRootFontSizeInPx();\n  if (normalizedUnit === 'ex') {\n    const fontSize = getFontSizeReferenceInPx(context, false);\n    return fontSize == null ? null : fontSize / 2;\n  }\n  if (normalizedUnit === 'ch') {\n    const fontSize = getFontSizeReferenceInPx(context, false);\n    return fontSize == null ? null : fontSize / 2;\n  }\n\n  return getViewportUnitInPx(normalizedUnit);\n}\n\nexport function defaultConvertCssNumericUnitValue(context: UnitConversionContext): string {\n  const convertibleContext = getConvertibleNumericContext(context);\n  if (!convertibleContext)\n    return combineNumericStyleInputValue(context.numberText, context.toUnit);\n\n  const decimalPlaces = getNumericValueDecimalPlaces(context);\n\n  let convertedValue: number | null;\n  switch (convertibleContext.numericType) {\n    case 'css-length':\n      convertedValue = convertLengthValue(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit, convertibleContext);\n      break;\n    case 'css-angle':\n      convertedValue = convertUsingUnitTable(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit, angleUnitInDeg);\n      break;\n    case 'css-time':\n      convertedValue = convertUsingUnitTable(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit, timeUnitInMs);\n      break;\n    case 'css-frequency':\n      convertedValue = convertUsingUnitTable(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit, frequencyUnitInHz);\n      break;\n    case 'css-resolution':\n      convertedValue = convertUsingUnitTable(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit, resolutionUnitInDpi);\n      break;\n    case 'css-flex':\n      convertedValue = convertibleContext.value;\n      break;\n    case 'css-scale':\n      convertedValue = convertScaleValue(convertibleContext.value, convertibleContext.fromUnit, convertibleContext.toUnit);\n      break;\n  }\n\n  if (convertedValue == null)\n    return combineNumericStyleInputValue(convertibleContext.numberText, convertibleContext.toUnit);\n\n  return combineNumericStyleInputValue(formatNumericStyleInputNumber(convertedValue, decimalPlaces), convertibleContext.toUnit);\n}\n\nfunction convertLengthValue(value: number, fromUnit: string, toUnit: string, context: UnitConversionContext): number | null {\n  const normalizedFromUnit = fromUnit?.toLowerCase() ?? '';\n  const normalizedToUnit = toUnit?.toLowerCase() ?? '';\n  if (normalizedFromUnit !== '%' && normalizedToUnit === '%') {\n    const measuredSizeInPx = getMeasuredSizeInPx(context);\n    const percentUnitSize = getLengthUnitSizeInPx(context, normalizedToUnit);\n    if (measuredSizeInPx != null && percentUnitSize != null)\n      return measuredSizeInPx / percentUnitSize;\n  }\n\n  const fromUnitSize = getLengthUnitSizeInPx(context, fromUnit);\n  const toUnitSize = getLengthUnitSizeInPx(context, toUnit);\n  if (fromUnitSize == null || toUnitSize == null)\n    return null;\n  return value * fromUnitSize / toUnitSize;\n}\n\nfunction convertScaleValue(value: number, fromUnit: string, toUnit: string): number | null {\n  const normalizedFromUnit = fromUnit?.toLowerCase() ?? '';\n  const normalizedToUnit = toUnit?.toLowerCase() ?? '';\n\n  if (normalizedFromUnit === normalizedToUnit)\n    return value;\n\n  const fromFactor = normalizedFromUnit === '%' ? 0.01 : normalizedFromUnit === '' ? 1 : null;\n  const toFactor = normalizedToUnit === '%' ? 0.01 : normalizedToUnit === '' ? 1 : null;\n  if (fromFactor == null || toFactor == null)\n    return null;\n\n  return value * fromFactor / toFactor;\n}\n\nfunction convertUsingUnitTable(value: number, fromUnit: string, toUnit: string, table?: Map<string, number>): number | null {\n  const normalizedFromUnit = fromUnit?.toLowerCase() ?? '';\n  const normalizedToUnit = toUnit?.toLowerCase() ?? '';\n  if (!normalizedFromUnit || !normalizedToUnit || normalizedFromUnit === normalizedToUnit)\n    return value;\n  if (!table)\n    return null;\n\n  const fromFactor = table.get(normalizedFromUnit);\n  const toFactor = table.get(normalizedToUnit);\n  if (fromFactor == null || toFactor == null)\n    return null;\n\n  return value * fromFactor / toFactor;\n}\n\nexport function getUnitPropertyType(type?: string, propertyName?: string): UnitPropertyType | null {\n  if (editorTypes.includes(type ?? ''))\n    return <UnitPropertyType>type;\n  return null;\n}\n\nexport function isUnitPropertyType(type?: string, propertyName?: string): boolean {\n  return getUnitPropertyType(type, propertyName) != null;\n}\n\nexport function convertNumericUnitValue(context: UnitConversionContext): string {\n  const decimalPlaces = getNumericValueDecimalPlaces(context);\n  const overrideResult = normalizeConvertedValue(\n    context.property.numericValueConverter?.(context.value, context.fromUnit, context.toUnit, context.property, context.numericType, context.numberText, context.rawValue, context.designItems),\n    context.toUnit,\n    decimalPlaces\n  );\n  if (overrideResult != null)\n    return overrideResult;\n  return defaultConvertCssNumericUnitValue(context);\n}\n\nexport function getCssNumericKeywordValues(values?: string[]): string[] {\n  return [...new Set([...(values ?? []), ...cssNumericKeywordValues].filter(Boolean))];\n}\n\nexport function applyCssNumericPropertyDefaults(property: IProperty): IProperty {\n  const numericType = getUnitPropertyType(property.type, property.propertyName ?? property.name);\n  property.values = getCssNumericKeywordValues(property.values);\n  if (numericType) {\n    property.units ??= [...defaultCssNumericUnits[numericType]];\n    property.unitSteps ??= { ...defaultCssNumericUnitSteps[numericType] };\n    property.numericValueConverter ??= (value, fromUnit, toUnit, converterProperty, converterNumericType, numberText, rawValue, designItems) => defaultConvertCssNumericUnitValue({\n      property: converterProperty,\n      numericType: <UnitPropertyType>converterNumericType,\n      designItems,\n      value,\n      numberText: numberText ?? `${value}`,\n      rawValue: rawValue ?? `${value}${fromUnit}`,\n      fromUnit,\n      toUnit,\n    });\n  }\n  return property;\n}\n\nexport function getCssNumericEditorConfig(property: IProperty): UnitEditorConfig | null {\n  const numericType = getUnitPropertyType(property.type, property.name);\n  if (!numericType)\n    return null;\n\n  const defaultUnitSteps = defaultCssNumericUnitSteps[numericType] ?? {};\n\n  return {\n    numericType,\n    units: property.units?.length ? property.units : defaultCssNumericUnits[numericType],\n    fixedValues: getCssNumericKeywordValues(property.values),\n    unitSteps: { ...defaultUnitSteps, ...(property.unitSteps ?? {}) },\n    convertValue: context => convertNumericUnitValue({ ...context, property, numericType })\n  };\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/special/GridAssignedRowColumnPropertyEditor.ts",
    "content": "import { IProperty } from '../../IProperty.js';\r\nimport { BasePropertyEditor } from '../BasePropertyEditor.js';\r\nimport { ValueType } from '../../ValueType.js';\r\nimport { getElementGridInformation } from '../../../../helper/GridHelper.js';\r\n\r\nexport class GridAssignedRowColumnPropertyEditor extends BasePropertyEditor<HTMLDivElement> {\r\n\r\n  //TODO: multiple cell selection, grid area support, span support\r\n\r\n  _root: HTMLDivElement;\r\n\r\n  constructor(property: IProperty) {\r\n    super(property);\r\n    this._root = document.createElement('div');\r\n    this._root.style.display = 'grid';\r\n    this._root.style.padding = '4px 0px';\r\n    this._root.style.boxSizing = 'border-box';\r\n    this._root.style.minHeight = '50px';\r\n    this.element = this._root;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    this._root.innerHTML = \"\";\r\n    if (this.designItems != null && this.designItems.length) {\r\n      let container = this.designItems[0].element.parentElement;\r\n      if (container) {\r\n        let styleContainer = getComputedStyle(container);\r\n        let info = getElementGridInformation(<HTMLElement>this.designItems[0].element);\r\n        let style = getComputedStyle(this.designItems[0].element);\r\n        let cntCol = styleContainer.gridTemplateColumns.split(' ').length;\r\n        let cntRow = styleContainer.gridTemplateRows.split(' ').length;\r\n        this._root.style.gridTemplateColumns = 'repeat(' + cntCol + ', 1fr)';\r\n        this._root.style.gridTemplateRows = 'repeat(' + cntRow + ', 1fr)';\r\n        let rowStart = parseInt(style.gridRowStart);\r\n        let rowEnd = rowStart + info.rowSpan;\r\n        let colStart = parseInt(style.gridColumnStart);\r\n        let colEnd = colStart + info.colSpan;\r\n        for (let p = 1; p <= cntRow; p++) {\r\n          for (let n = 1; n <= cntCol; n++) {\r\n            const b = document.createElement('button');\r\n            b.style.minHeight = '10px';\r\n            b.onclick = () => {\r\n              let info = getElementGridInformation(<HTMLElement>this.designItems[0].element);\r\n              let grp = this.designItems[0].openGroup('Change grid row/column');\r\n              this.designItems[0].setStyle(\"grid-row\", p + ' / ' + (p + info.rowSpan));\r\n              this.designItems[0].setStyle(\"grid-column\", n + ' / ' + (n + info.colSpan));\r\n              grp.commit();\r\n            }\r\n            if (p >= rowStart && p < rowEnd && n >= colStart && n < colEnd)\r\n              b.style.backgroundColor = 'coral';\r\n            this._root.appendChild(b);\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/propertyEditors/special/MetricsPropertyEditor.ts",
    "content": "import { IProperty } from '../../IProperty.js';\nimport { BasePropertyEditor } from '../BasePropertyEditor.js';\nimport { ValueType } from '../../ValueType.js';\nimport { MetricsEditor, MetricsEditorValueChangedEventArgs } from '../../../../controls/MetricsEditor.js';\nimport { PropertyType } from '../../PropertyType.js';\n\nexport class MetricsPropertyEditor extends BasePropertyEditor<MetricsEditor> {\n\n  constructor(property: IProperty) {\n    super(property);\n\n    const selector = new MetricsEditor()\n    selector.property = property.name;\n    selector.style.height = 'auto';\n    selector.style.border = 'none';\n    selector.style.margin = '2px 0';\n    selector.addEventListener('value-changed', (event: CustomEvent<MetricsEditorValueChangedEventArgs>) => this._metricValueChanged(event.detail));\n    this.element = selector;\n  }\n\n  refreshValue(valueType: ValueType, value: any) {\n    this.element.refresh(this.designItems?.[0]?.element)\n  }\n\n  private async _metricValueChanged(detail: MetricsEditorValueChangedEventArgs) {\n    if (!detail?.property || !this.designItems?.length || this.disableChangeNotification)\n      return;\n\n    const cssProperty: IProperty = {\n      ...this.property,\n      name: detail.property,\n      propertyName: detail.property,\n      propertyType: PropertyType.cssValue,\n      type: 'css-length'\n    };\n\n    const value = detail.newValue == null || detail.newValue === '' || detail.newValue === this.element.unsetValue\n      ? null\n      : detail.newValue;\n\n    if (value == null)\n      this.property.service.clearValue(this.designItems, cssProperty, 'value');\n    else\n      await this.property.service.setValue(this.designItems, cssProperty, value);\n\n    this.designItems[0].instanceServiceContainer?.designerCanvas?.extensionManager?.refreshAllExtensions(this.designItems);\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/AbstractCssPropertiesService.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { CommonPropertiesService } from './CommonPropertiesService.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IProperty } from '../IProperty.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\nimport { applyCssNumericPropertyDefaults } from '../propertyEditors/UnitPropertyEditorConfig.js';\nimport { splitCssImportant } from '../../../helper/CssImportant.js';\n\r\nexport abstract class AbstractCssPropertiesService extends CommonPropertiesService {\r\n\r\n  protected _enrichCssProperty(property: IProperty): IProperty {\r\n    return applyCssNumericPropertyDefaults(property);\r\n  }\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.none;\r\n  }\r\n\r\n  async previewValue?(designItems: IDesignItem[], property: IProperty, value: any): Promise<void> {\r\n    let nm = property.propertyName ?? property.name;\r\n    if (!nm.startsWith('--'))\n      nm = PropertiesHelper.camelToDashCase(nm);\n    const parsedValue = typeof value === 'string' ? splitCssImportant(value) : { value, important: false };\n    for (let d of designItems) {\n      (<HTMLElement>d.element).style.setProperty(nm, parsedValue.value, parsedValue.important ? 'important' : '');\n    }\n  }\n\r\n  async removePreviewValue?(designItems: IDesignItem[], property: IProperty): Promise<void> {\r\n    let nm = property.propertyName ?? property.name;\r\n    if (!nm.startsWith('--'))\r\n      nm = PropertiesHelper.camelToDashCase(nm);\r\n    for (let d of designItems) {\r\n      if (d.hasStyle(nm))\n        (<HTMLElement>d.element).style.setProperty(nm, d.getStyle(nm), d.isStyleImportant(nm) ? 'important' : '');\n      else\n        (<HTMLElement>d.element).style.setProperty(nm, '');\n    }\r\n  }\r\n\r\n  constructor() {\r\n    super(false);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/AbstractPolymerLikePropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport abstract class AbstractPolymerLikePropertiesService extends AbstractPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.fullOnClassChange;\r\n  }\r\n\r\n  public override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (!this.isHandledElement(designItem))\r\n      return null;\r\n    if ((<any>designItem.element?.constructor)?.properties)\r\n      return this.parseProperties((<any>designItem.element.constructor).properties);\r\n    return null;\r\n  }\r\n\r\n  protected parseProperties(list: any): IPropertyGroup[] | IProperty[] {\r\n    let groups: IPropertyGroup[] | IProperty[] = [];\r\n\r\n    let names = Object.keys(list);\r\n    const grouped = Object.groupBy(names, x => list[x].group ?? '');\r\n\r\n    for (let grp in grouped) {\r\n      const pg: IPropertyGroup = { name: grp, properties: [] };\r\n      for (const name of grouped[grp]) {\r\n        const polymerProperty = list[name];\r\n        let type = polymerProperty;\r\n        let description = null;\r\n        let example = null;\r\n        let readonly = false;\r\n        let propertyType = PropertyType.propertyAndAttribute;\r\n        if (polymerProperty.type) {\r\n          type = polymerProperty.type;\r\n          description = polymerProperty.description;\r\n          example = polymerProperty.example;\r\n          readonly = polymerProperty.readonly;\r\n          propertyType = polymerProperty.readonly ? PropertyType.property : PropertyType.propertyAndAttribute;\r\n        }\r\n\r\n        if (type === String) {\r\n          let property: IProperty = { name, type: \"string\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (type === Object) {\r\n          let property: IProperty = { name, type: \"object\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (type === Number) {\r\n          let property: IProperty = { name, type: \"number\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (type === Date) {\r\n          let property: IProperty = { name, type: \"date\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (type === Boolean) {\r\n          let property: IProperty = { name, type: \"boolean\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (PropertiesHelper.isTypescriptEnum(type)) {\r\n          let property: IProperty = { name, type: \"enum\", enumValues: PropertiesHelper.getTypescriptEnumEntries(type), service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else if (typeof type === 'string') {\r\n          let property: IProperty = { name, type: type, service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        } else {\r\n          let property: IProperty = { name, type: \"string\", service: this, propertyType, description, example, readonly };\r\n          pg.properties.push(property);\r\n        }\r\n      }\r\n      if (pg.name == '')\r\n        //@ts-ignore\r\n        groups.push(...pg.properties);\r\n      else\r\n        //@ts-ignore\r\n        groups.push(pg);\r\n    }\r\n    return groups;\r\n  }\r\n\r\n  override getUnsetValue(designItems: IDesignItem[], property: IProperty) {\r\n    return designItems[0].element[property.propertyName ?? property.name];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/AbstractPropertiesService.ts",
    "content": "import { IPropertiesService, RefreshMode } from '../IPropertiesService.js';\r\nimport { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\r\nimport { IBinding } from '../../../item/IBinding.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { NodeType } from '../../../item/NodeType.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\nimport { newElementFromString } from '../../../helper/ElementHelper.js';\nimport { IContextMenuItem } from '../../../helper/contextMenu/IContextMenuItem.js';\nimport { appendCssImportant, splitCssImportant } from '../../../helper/CssImportant.js';\n\r\nexport abstract class AbstractPropertiesService implements IPropertiesService {\r\n\r\n  constructor(recreateElementsOnPropertyChange?: boolean) {\r\n    this._recreateElementsOnPropertyChange = recreateElementsOnPropertyChange;\r\n  }\r\n\r\n  protected _recreateElementsOnPropertyChange: boolean = false;\r\n\r\n  private static _stylesCache = new Map<IDesignItem, Set<string>>;\r\n  private _cssCacheClearTimer: NodeJS.Timeout;\r\n  private static _bindingsCache = new Map<IDesignItem, IBinding[]>;\r\n  private static _bindingsCacheClearTimer: NodeJS.Timeout;\r\n\r\n  abstract getRefreshMode(designItem: IDesignItem): RefreshMode;\r\n\r\n  abstract isHandledElement(designItem: IDesignItem): boolean;\r\n\r\n  protected _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n  }\r\n\r\n  async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    let properties = await this.getProperties(designItem);\r\n    if ('properties' in properties[0]) {\r\n      return (<IPropertyGroup[]>properties).flatMap(x => x.properties).find(x => x.name == name);\r\n    }\r\n    else\r\n      return (<IProperty[]>properties).find(x => x.name == name);\r\n  }\r\n\r\n  abstract getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]>;\r\n\r\n  async setValue(designItems: IDesignItem[], property: IProperty, value: any) {\r\n    const cg = designItems[0].openGroup(\"property changed: \" + property.name + \" to \" + value);\r\n    for (let d of designItems) {\r\n      if (!this.isHandledElement(d))\r\n        continue;\r\n      if (!this.getProperty(d, property.name))\r\n        continue;\r\n\r\n      if (property.propertyType == PropertyType.cssValue) {\n        const parsedValue = typeof value === 'string' ? splitCssImportant(value) : { value, important: false };\n        await d.updateStyleInSheetOrLocalAsync(property.propertyName ?? property.name, parsedValue.value, parsedValue.important);\n        //TODO: fix this hack somehow\r\n        //unkown css property names do not trigger the mutation observer of property grid, \r\n        //fixed by assinging style again to the attribute\r\n        (<HTMLElement>d.element).setAttribute('style', (<HTMLElement>d.element).getAttribute('style'));\r\n      } else {\r\n        let attributeName = property.attributeName\r\n        if (!attributeName)\r\n          attributeName = PropertiesHelper.camelToDashCase(property.name);\r\n\r\n\r\n        if (property.type === 'object') {\r\n          const json = JSON.stringify(value);\r\n          if (property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.setPropertyAndAttribute(property.name, json);\r\n          else if (property.propertyType == PropertyType.attribute)\r\n            d.setAttribute(attributeName, json);\r\n          else if (property.propertyType == PropertyType.property)\r\n            d.setProperty(property.name, value);\r\n        } else if (property.type == 'boolean' && (value === false || value == null)) {\r\n          if (property.propertyType == PropertyType.attribute || property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.removeAttribute(attributeName);\r\n          if (property.propertyType == PropertyType.property || property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.setProperty(property.name, false);\r\n        }\r\n        else if (property.type == 'boolean' && value === true) {\r\n          if (property.propertyType == PropertyType.attribute || property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.setAttribute(attributeName, \"\");\r\n          if (property.propertyType == PropertyType.property || property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.setProperty(property.name, true);\r\n        }\r\n        else {\r\n          if (property.propertyType == PropertyType.propertyAndAttribute)\r\n            d.setPropertyAndAttribute(property.name, value);\r\n          else if (property.propertyType == PropertyType.attribute)\r\n            d.setAttribute(attributeName, value.toString());\r\n          else if (property.propertyType == PropertyType.property)\r\n            d.setProperty(property.name, value);\r\n        }\r\n      }\r\n      this._notifyChangedProperty(d, property, value);\r\n    }\r\n    cg.commit();\r\n    if (this._recreateElementsOnPropertyChange)\r\n      AbstractPropertiesService.recreateElements(this, designItems);\r\n  }\r\n\r\n  getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    if (property.propertyType == PropertyType.attribute)\r\n      return BindingTarget.attribute;\r\n    if (property.propertyType == PropertyType.cssValue)\r\n      return BindingTarget.css;\r\n    return BindingTarget.property;\r\n  }\r\n\r\n  clearValue(designItems: IDesignItem[], property: IProperty, clearType: 'all' | 'binding' | 'value') {\r\n    const cg = designItems[0].openGroup(\"property cleared: \" + property.name);\r\n    for (let d of designItems) {\r\n      if (clearType != 'binding') {\r\n        if (property.propertyType == PropertyType.cssValue) {\r\n          d.removeStyle(property.propertyName ?? property.name);\r\n        } else {\r\n          if (property.propertyType == PropertyType.property || property.propertyType == PropertyType.propertyAndAttribute) {\r\n            d.element[property.propertyName ?? property.name] = null;\r\n          }\r\n          if (property.propertyType == PropertyType.attribute || property.propertyType == PropertyType.propertyAndAttribute) {\r\n            let attributeName = property.attributeName\r\n            if (!attributeName)\r\n              attributeName = PropertiesHelper.camelToDashCase(property.name);\r\n            d.removeAttribute(attributeName);\r\n          }\r\n        }\r\n      }\r\n      if (clearType != 'value') {\r\n        d.serviceContainer.forSomeServicesTillResult('bindingService', (s) => {\r\n          return s.clearBinding(d, property.name, this.getPropertyTarget(d, property));\r\n        });\r\n      }\r\n      this._notifyChangedProperty(d, property, undefined);\r\n    }\r\n    cg.commit();\r\n    if (this._recreateElementsOnPropertyChange)\r\n      AbstractPropertiesService.recreateElements(this, designItems);\r\n  }\r\n\r\n  isSet(designItems: IDesignItem[], property: IProperty): ValueType {\r\n    let all = true;\r\n    let some = false;\r\n    if (designItems != null && designItems.length !== 0) {\r\n      let attributeName = property.attributeName\r\n      if (!attributeName)\r\n        attributeName = PropertiesHelper.camelToDashCase(property.name);\r\n      for (let x of designItems) {\r\n        let has = false;\r\n        if (property.propertyType == PropertyType.cssValue)\r\n          has = x.hasStyle(property.name);\r\n        else\r\n          has = x.hasAttribute(attributeName);\r\n        all = all && has;\r\n        some = some || has;\r\n        if (!all && some)\r\n          break;\r\n      };\r\n\r\n      const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItems[0]);\r\n      if (property.propertyType == PropertyType.cssValue) {\r\n        if (bindings && bindings.find(x => (x.target == BindingTarget.css || x.target == BindingTarget.cssvar) && x.targetName == property.name))\r\n          return ValueType.bound;\r\n      } else {\r\n        if (property.propertyType == PropertyType.attribute) {\r\n          if (bindings && bindings.find(x => x.target == BindingTarget.attribute && x.targetName == property.name))\r\n            return ValueType.bound;\r\n        } else if (property.propertyType == PropertyType.property) {\r\n          if (bindings && bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty) && x.targetName == property.name))\r\n            return ValueType.bound;\r\n        } else {\r\n          if (bindings && bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty || x.target == BindingTarget.attribute) && x.targetName == property.name))\r\n            return ValueType.bound;\r\n        }\r\n      }\r\n\r\n      if (!all && property.propertyType == PropertyType.cssValue) {\r\n        let styles = AbstractPropertiesService._stylesCache.get(designItems[0]);\r\n        if (!styles) {\r\n          styles = new Set(designItems[0].getAllStyles().filter(x => x.selector != null).flatMap(x => x.declarations).map(x => x.name));\r\n          AbstractPropertiesService._stylesCache.set(designItems[0], styles);\r\n          clearTimeout(this._cssCacheClearTimer);\r\n          this._cssCacheClearTimer = setTimeout(() => AbstractPropertiesService._stylesCache.clear(), 30);\r\n        }\r\n\r\n        let cssValue = styles.has(property.propertyName ?? property.name);\r\n        if (cssValue)\r\n          return ValueType.fromStylesheet;\r\n      }\r\n    }\r\n    else\r\n      return ValueType.none\r\n\r\n    return all ? ValueType.all : some ? ValueType.some : ValueType.none;\r\n  }\r\n\r\n  static getOrBuildCachedBindings(designItem: IDesignItem) {\r\n    let bindings = AbstractPropertiesService._bindingsCache.get(designItem);\r\n    if (!bindings) {\r\n      const services = designItem.serviceContainer.getServices('bindingService');\r\n      bindings = [];\r\n      if (services) {\r\n        for (const s of services) {\r\n          const bs = s.getBindings(designItem);\r\n          if (bs && bs.length > 0) {\r\n            bindings.push(...bs);\r\n          }\r\n        }\r\n      }\r\n      AbstractPropertiesService._bindingsCache.set(designItem, bindings);\r\n      clearTimeout(this._bindingsCacheClearTimer);\r\n      this._bindingsCacheClearTimer = setTimeout(() => AbstractPropertiesService._bindingsCache.clear(), 30);\r\n    }\r\n    return bindings;\r\n  }\r\n\r\n  getValue(designItems: IDesignItem[], property: IProperty) {\r\n    if (designItems != null && designItems.length !== 0) {\r\n      if (property.propertyType == PropertyType.cssValue) {\r\n        let lastValue = designItems[0].getStyle(property.propertyName ?? property.name);\r\n        for (const d of designItems) {\r\n          let value = d.getStyle(property.name);\r\n          if (value != lastValue) {\r\n            lastValue = null;\r\n            break;\r\n          }\r\n        }\r\n        return typeof lastValue === 'string' ? appendCssImportant(lastValue, designItems[0].isStyleImportant(property.propertyName ?? property.name)) : lastValue;\n      } else if (property.propertyType == PropertyType.property) {\r\n        let propertyName = property.propertyName\r\n        if (!propertyName)\r\n          propertyName = PropertiesHelper.dashToCamelCase(property.name);\r\n        return designItems[0].element[propertyName];\r\n      } else {\r\n        let attributeName = property.attributeName\r\n        if (!attributeName)\r\n          attributeName = PropertiesHelper.camelToDashCase(property.name);\r\n\r\n        if (property.type == 'boolean') {\r\n          if (designItems[0].hasAttribute(attributeName)) {\r\n            const val = designItems[0].getAttribute(attributeName);\r\n            if (val == \"\")\r\n              return true;\r\n            return val;\r\n          }\r\n          return false;\r\n        }\r\n        let lastValue = designItems[0].getAttribute(attributeName);\r\n        /*\r\n        for (const x of designItems) {\r\n          let value = x.attributes.get(attributeName);\r\n          if (value != lastValue) {\r\n            lastValue = null;\r\n            break;\r\n          }\r\n        }\r\n        */\r\n        return lastValue;\r\n      }\r\n    }\r\n    return null;\r\n  }\r\n\r\n  getBinding(designItems: IDesignItem[], property: IProperty): IBinding {\r\n    const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItems[0]);\r\n    if (bindings != null) {\r\n      if (property.propertyType == PropertyType.cssValue) {\r\n        return bindings.find(x => (x.target == BindingTarget.css || x.target == BindingTarget.cssvar) && x.targetName == property.name);\r\n      } else {\r\n        if (property.propertyType == PropertyType.attribute) {\r\n          return bindings.find(x => x.target == BindingTarget.attribute && x.targetName == property.name);\r\n        } else if (property.propertyType == PropertyType.property) {\r\n          return bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty) && x.targetName == property.name);\r\n        } else {\r\n          return bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty || x.target == BindingTarget.attribute) && x.targetName == property.name);\r\n        }\r\n      }\r\n    }\r\n    return null;\r\n  }\r\n\r\n  getUnsetValue(designItems: IDesignItem[], property: IProperty) {\r\n    if (property.propertyType == PropertyType.cssValue) {\r\n      if (designItems != null && designItems.length !== 0) {\r\n        if (designItems[0].nodeType == NodeType.Element) {\r\n          let v = window.getComputedStyle(designItems[0].element)[property.propertyName ?? property.name];\r\n          return v;\r\n        }\r\n      }\r\n      return null;\r\n    }\r\n    else\r\n      return property.defaultValue;\r\n  }\r\n\r\n  protected static recreateElements(service: IPropertiesService, designItems: IDesignItem[]) {\r\n    for (let d of designItems) {\r\n      if (!service.isHandledElement(d))\r\n        continue;\r\n\r\n      let txt = '<' + d.name + ' ';\r\n      for (let a of d.element.attributes) {\r\n        txt += a.name + '=\"' + a.value.replaceAll('\"', '&quot;') + '\" ';\r\n      }\r\n      txt += '></' + d.name + '>';\r\n      let element = newElementFromString(txt, designItems[0].document); // some custom elements only parse attributes during constructor call \r\n      for (let c of [...d.element.childNodes])\r\n        element.appendChild(c);\r\n      (<HTMLElement>element).style.pointerEvents = 'auto';\r\n      (<HTMLElement>d.node).insertAdjacentElement('beforebegin', element);\r\n      if (d.node.parentNode)\r\n        (<HTMLElement>d.node.parentNode).removeChild(d.node);\r\n      d.replaceNode(element);\r\n    }\r\n  }\r\n\r\n  public getContextMenu(designItems: IDesignItem[], property: IProperty): IContextMenuItem[] {\r\n    const ctxMenuItems: IContextMenuItem[] = [\r\n      {\r\n        title: 'clear', action: (e) => {\r\n          property.service.clearValue(designItems, property, 'value');\r\n          designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshAllExtensions(designItems);\r\n        }\r\n      },\r\n      {\n        title: 'edit as text', action: (e, _1, _2, menu) => {\n          menu.close();\n          setTimeout(async () => {\n            const oldValue = property.service.getValue(designItems, property);\r\n            let value = prompt(`edit value of '${property.name}' as string:`, oldValue);\r\n            if (value && value != oldValue) {\r\n              await property.service.setValue(designItems, property, value);\r\n            }\r\n            designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshAllExtensions(designItems);\r\n          }, 10)\n        }\n      },\n    ];\n    if (designItems[0].serviceContainer.config.openBindingsEditor) {\n      ctxMenuItems.push(...[\r\n        { title: '-' },\r\n        {\r\n          title: 'edit binding', action: () => {\r\n            let target = property.service.getPropertyTarget(designItems[0], property);\r\n            let binding = property.service.getBinding(designItems, property);\r\n            designItems[0].serviceContainer.config.openBindingsEditor(property, designItems, binding, target);\r\n          }\r\n        }\r\n      ]);\r\n      if (property.service.isSet(designItems, property) == ValueType.bound) {\r\n        ctxMenuItems.push(...[\r\n          {\r\n            title: 'clear binding', action: () => {\r\n              property.service.clearValue(designItems, property, 'binding');\r\n              designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshAllExtensions(designItems);\r\n            }\r\n          }\r\n        ]);\r\n      }\r\n    };\r\n    return ctxMenuItems;\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/AttachedPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport class AttachedPropertiesService extends AbstractPropertiesService {\r\n\r\n  public name = \"attached\"\r\n\r\n  override getRefreshMode(designItem: IDesignItem): RefreshMode {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return designItem.serviceContainer.forSomeServicesTillResult('attachedPropertyService', x => x.isHandledElement(designItem));\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    let p: IProperty[] | IPropertyGroup[] = [];\r\n    if (designItem.serviceContainer.attachedPropertyServices) {\r\n      for (let s of designItem.serviceContainer.attachedPropertyServices) {\r\n        if (s.isHandledElement(designItem)) {\r\n          p.push(...<any>await s.getProperties(designItem));\r\n        }\r\n      }\r\n    }\r\n    return p;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/AttributesPropertiesService.ts",
    "content": "import { RefreshMode } from '../IPropertiesService.js';\r\nimport { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { IBinding } from \"../../../item/IBinding.js\";\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\n\r\nexport class AttributesPropertiesService extends AbstractPropertiesService {\r\n\r\n  public name = \"attributes\"\r\n\r\n  public getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.fullOnValueChange;\r\n  }\r\n\r\n  isHandledElement(designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return { name: name, type: 'string', service: this, propertyType: PropertyType.attribute };\r\n  }\r\n\r\n  async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (designItem) {\r\n      let p: IProperty[] = [];\r\n      for (let a of designItem.attributes()) {\r\n        p.push({ name: a[0], renamable: true, type: 'string', service: this, propertyType: PropertyType.attribute })\r\n      }\r\n      p.push({ name: '', type: 'addNew', service: this, propertyType: PropertyType.complex });\r\n      return p;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  override async setValue(designItems: IDesignItem[], property: IProperty, value: any) {\r\n    const cg = designItems[0].openGroup(\"properties changed\");\r\n    for (let d of designItems) {\r\n      d.setAttribute(<string>property.name, value);\r\n    }\r\n    cg.commit();\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    return BindingTarget.attribute;\r\n  }\r\n\r\n  override clearValue(designItems: IDesignItem[], property: IProperty) {\r\n    for (let d of designItems) {\r\n      d.removeAttribute(<string>property.name);\r\n      d.serviceContainer.forSomeServicesTillResult('bindingService', (s) => {\r\n        return s.clearBinding(d, property.name, this.getPropertyTarget(d, property));\r\n      });\r\n    }\r\n  }\r\n\r\n  override isSet(designItems: IDesignItem[], property: IProperty): ValueType {\r\n    let all = true;\r\n    let some = false;\r\n    if (designItems != null && designItems.length !== 0) {\r\n      if (designItems.length == 1 && typeof designItems[0].getAttribute(property.name) == 'object')\r\n        return ValueType.bound;\r\n      let propName = PropertiesHelper.dashToCamelCase(property.name);\r\n      let attributeName = property.name;\r\n      designItems.forEach((x) => {\r\n        let has = x.hasAttribute(attributeName);\r\n        all = all && has;\r\n        some = some || has;\r\n      });\r\n      const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItems[0]);\r\n      if (bindings && bindings.find(x => x.target == BindingTarget.attribute && x.targetName == propName))\r\n        return ValueType.bound;\r\n    }\r\n    else\r\n      return ValueType.none\r\n\r\n    return all ? ValueType.all : some ? ValueType.some : ValueType.none;\r\n  }\r\n\r\n  override getValue(designItems: IDesignItem[], property: IProperty) {\r\n    if (designItems != null && designItems.length !== 0) {\r\n      let attributeName = property.name;\r\n      let lastValue = designItems[0].getAttribute(attributeName);\r\n      if (typeof lastValue === 'object')\r\n        return (<IBinding>lastValue).rawValue;\r\n      /*\r\n      for (const x of designItems) {\r\n        let value = x.attributes.get(attributeName);\r\n        if (value != lastValue) {\r\n          lastValue = null;\r\n          break;\r\n        }\r\n      }\r\n      */\r\n      return lastValue;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  override getBinding(designItems: IDesignItem[], property: IProperty): IBinding {\r\n    const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItems[0]);\r\n    return bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty || x.target == BindingTarget.attribute) && x.targetName == property.name);\r\n  }\r\n\r\n  override getUnsetValue(designItems: IDesignItem[], property: IProperty) {\r\n    return property.defaultValue;\r\n  }\r\n\r\n  override getContextMenu(designItems: IDesignItem[], property: IProperty) {\r\n    const ctxMenuItems = super.getContextMenu(designItems, property);\r\n    const sourcePart = this._getAttributeSourcePart(designItems, property);\r\n\r\n    if (sourcePart) {\r\n      ctxMenuItems.splice(1, 0, {\r\n        title: 'jump to definition',\r\n        action: () => {\r\n          const selectionService = designItems[0].instanceServiceContainer.selectionService;\r\n          if (selectionService.primarySelection !== designItems[0])\r\n            selectionService.setSelectedElements([designItems[0]], null, sourcePart);\r\n          else\r\n            selectionService.setSelectedPart(sourcePart);\r\n        }\r\n      });\r\n    }\r\n\r\n    return ctxMenuItems;\r\n  }\r\n\r\n  private _getAttributeSourcePart(designItems: IDesignItem[], property: IProperty) {\r\n    if (designItems?.length !== 1 || !property?.name)\r\n      return null;\r\n\r\n    return designItems[0].instanceServiceContainer.designItemDocumentPositionService\r\n      ?.getSourceParts(designItems[0])\r\n      .find(x => x.kind === 'attribute' && x.key === `attribute:${property.name}`);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/BaseCustomWebComponentPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { BaseCustomWebComponentLazyAppend, BaseCustomWebComponentConstructorAppend, BaseCustomWebComponentNoAttachedTemplate, BaseCustomWebComponentConstructorAppendLazyReady } from '@node-projects/base-custom-webcomponent';\r\nimport { AbstractPolymerLikePropertiesService } from './AbstractPolymerLikePropertiesService.js';\r\n\r\nexport class BaseCustomWebComponentPropertiesService extends AbstractPolymerLikePropertiesService {\r\n  \r\n  public name = \"baseCustomWebComponent\";\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return designItem.element instanceof BaseCustomWebComponentLazyAppend ||\r\n      designItem.element instanceof BaseCustomWebComponentConstructorAppendLazyReady ||\r\n      designItem.element instanceof BaseCustomWebComponentConstructorAppend ||\r\n      designItem.element instanceof BaseCustomWebComponentNoAttachedTemplate;\r\n  }\r\n\r\n  protected override _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n    //@ts-ignore\r\n    (<BaseCustomWebComponentNoAttachedTemplate><any>designItem.element)._parseAttributesToProperties();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/BasicWebcomponentPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\n\r\nexport class BasicWebcomponentPropertiesService extends AbstractPropertiesService {\r\n\r\n  public name = \"webcomponent\"\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    //@ts-ignore\r\n    const attr = designItem.element.constructor?.observedAttributes;\r\n    if (attr && attr.length > 0) {\r\n      return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return (<IProperty[]>await this.getProperties(designItem)).find(x => x.name == name);\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    //@ts-ignore\r\n    const attr: string[] = designItem.element.constructor?.observedAttributes;\r\n\r\n    return attr.map(x => ({\r\n      name: PropertiesHelper.dashToCamelCase(x),\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }));\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/CommonPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport class CommonPropertiesService extends AbstractPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.none;\r\n  }\r\n\r\n  //@ts-ignore\r\n  private commonProperties: IProperty[] = [\r\n    {\r\n      name: \"class\",\r\n      type: \"string\",\r\n      service: this,\r\n      attributeName: \"class\",\r\n      propertyName: \"className\",\r\n      propertyType: PropertyType.attribute\r\n    }, {\r\n      name: \"title\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"part\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"tabindex\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  public name = \"common\"\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return this.commonProperties.find(x => x.name == name);\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    return this.commonProperties;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/ContentAndIdPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\r\n\r\nexport class ContentAndIdPropertiesService extends AbstractPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.none;\r\n  }\r\n\r\n  public contentProperty: IProperty =\r\n    {\r\n      name: \"textContent\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    };\r\n\r\n  public idProperty: IProperty =\r\n    {\r\n      name: \"id\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    };\r\n\r\n  public innerHtmlProperty: IProperty =\r\n    {\r\n      name: \"innerHTML\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    };\r\n\r\n  public name = \"content\"\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return name == 'id' ? this.idProperty : this.contentProperty;\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    return [this.idProperty, this.contentProperty, this.innerHtmlProperty];\r\n  }\r\n\r\n  override clearValue(designItems: IDesignItem[], property: IProperty, clearType: 'all' | 'binding' | 'value' = 'all'): void {\r\n    if (property.name == this.contentProperty.name || property.name == this.innerHtmlProperty.name) {\r\n      const cg = designItems[0].openGroup(\"property cleared: \" + property.name);\r\n      for (let d of designItems) {\r\n        if (clearType != 'binding') {\r\n          d.clearChildren();\r\n        }\r\n        if (clearType != 'value') {\r\n          d.serviceContainer.forSomeServicesTillResult('bindingService', (s) => {\r\n            return s.clearBinding(d, property.name, this.getPropertyTarget(d, property));\r\n          });\r\n        }\r\n        this._notifyChangedProperty(d, property, undefined);\r\n      }\r\n      cg.commit();\r\n    } else {\r\n      super.clearValue(designItems, property, clearType);\r\n    }\r\n  }\r\n\r\n  override isSet(designItems: IDesignItem[], property: IProperty): ValueType {\r\n    if (property.name == this.contentProperty.name || property.name == this.innerHtmlProperty.name) {\r\n      let all = true;\r\n      let some = false;\r\n      if (designItems != null && designItems.length !== 0) {\r\n        designItems.forEach((x) => {\r\n          let has = false;\r\n          has = x.element.childNodes.length > 0;\r\n          all = all && has;\r\n          some = some || has;\r\n        });\r\n        const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItems[0]);\r\n        if (bindings && bindings.find(x => (x.target == BindingTarget.property || x.target == BindingTarget.explicitProperty) && x.targetName == property.name))\r\n          return ValueType.bound;\r\n      }\r\n      return all ? ValueType.all : some ? ValueType.some : ValueType.none;\r\n    }\r\n    return super.isSet(designItems, property);\r\n  }\r\n\r\n  override getValue(designItems: IDesignItem[], property: IProperty): string | boolean {\r\n    if (property.name == this.contentProperty.name || property.name == this.innerHtmlProperty.name) {\r\n      return designItems[0].element.textContent;\r\n    }\r\n    return super.getValue(designItems, property);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/CssCurrentPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { IStyleDeclaration, IStyleRule } from '../../stylesheetService/IStylesheetService.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { NodeType } from '../../../item/NodeType.js';\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\nimport { CssPropertiesService } from './CssPropertiesService.js';\nimport { appendCssImportant, splitCssImportant } from '../../../helper/CssImportant.js';\n\r\nconst localName = '&lt;local&gt;';\r\n\r\nexport class CssCurrentPropertiesService extends CssPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.fullOnValueChange;\r\n  }\r\n\r\n  constructor() {\r\n    super(null);\r\n    this.name = 'styles';\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    const camelName = PropertiesHelper.dashToCamelCase(name);\r\n    return this._enrichCssProperty({ name: name, type: this._getPropertyType(camelName), values: this._getPropertyValues(camelName), service: this, propertyType: PropertyType.cssValue });\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (!designItem || designItem.nodeType != NodeType.Element)\r\n      return [];\r\n\r\n    let styles = designItem.getAllStyles().reverse().sort((a, b) => {\r\n      if (a.specificity == null)\r\n        return -1;\r\n      if (b.specificity == null)\r\n        return 1;\r\n      if (a.specificity.A > b.specificity.A)\r\n        return -1;\r\n      if (a.specificity.A === b.specificity.A && a.specificity.B > b.specificity.B)\r\n        return -1;\r\n      if (a.specificity.A === b.specificity.A && a.specificity.B === b.specificity.B && a.specificity.C > b.specificity.C)\r\n        return -1;\r\n      if (a.specificity.A === b.specificity.A && a.specificity.B === b.specificity.B && a.specificity.C === b.specificity.C)\r\n        return 0;\r\n      return 1;\r\n    });\r\n\r\n    const bindings = AbstractPropertiesService.getOrBuildCachedBindings(designItem);\r\n    let localStyles = styles.find(x => x.selector === null)\r\n    for (const b of bindings) {\r\n      if (b.target === BindingTarget.css) {\r\n        if (!localStyles.declarations.find(x => x.name == b.targetName)) {\r\n          localStyles.declarations.push({ name: b.targetName, value: null, important: false, parent: null });\r\n        }\r\n      }\r\n    }\r\n\r\n    let arr = styles.map(x => ({\r\n      name: (x.selector ?? localName) + (x.specificity ? ' (' + x.specificity.A + '-' + x.specificity.B + '-' + x.specificity.C + ')' : ''), description: x.stylesheetName ?? '', properties: [\r\n        ...x.declarations.map(y => {\r\n          const camelName = PropertiesHelper.dashToCamelCase(y.name);\r\n          return this._enrichCssProperty({\r\n            name: y.name,\r\n            renamable: true,\r\n            type: this._getPropertyType(camelName),\r\n            values: this._getPropertyValues(camelName),\r\n            service: this,\r\n            propertyType: PropertyType.cssValue,\r\n            //@ts-ignore\r\n            styleRule: x,\r\n            styleDeclaration: y\r\n          })\r\n        }),\r\n        { name: '', type: 'addNew', service: this, propertyType: PropertyType.complex, styleRule: x }\r\n      ]\r\n    }));\r\n    return arr;\r\n  }\r\n\r\n  override async setValue(designItems: IDesignItem[], property: (IProperty & { styleRule: IStyleRule, styleDeclaration: IStyleDeclaration }), value: any) {\n    const parsedValue = typeof value === 'string' ? splitCssImportant(value) : { value, important: false };\n    // No selector means local style, styleDeclaration is null means new property\n    if (property.styleRule?.selector !== null && property.styleDeclaration) {\n      designItems[0].instanceServiceContainer.stylesheetService.updateDeclarationValue(property.styleDeclaration, parsedValue.value, parsedValue.important);\n      this._notifyChangedProperty(designItems[0], property, value);\n      return;\n    }\n    if (property.styleRule?.selector !== null && !property.styleDeclaration) {\n      designItems[0].instanceServiceContainer.stylesheetService.insertDeclarationIntoRule(property.styleRule, property.name, parsedValue.value, parsedValue.important);\n      this._notifyChangedProperty(designItems[0], property, value);\n      return;\n    }\n\n    for (const d of designItems) {\n      // Local style\n      d.setStyle(property.name, parsedValue.value, parsedValue.important);\n      //unkown css property names do not trigger the mutation observer of property grid, \r\n      //fixed by assinging stle again to the attribute\r\n      (<HTMLElement>d.element).setAttribute('style', (<HTMLElement>d.element).getAttribute('style'));\r\n    }\r\n  }\r\n\r\n  override clearValue(designItems: IDesignItem[], property: IProperty & { styleRule: IStyleRule, styleDeclaration: IStyleDeclaration }, clearType: 'all' | 'binding' | 'value') {\r\n    if (property.styleRule?.selector !== null && property.styleDeclaration) {\r\n      designItems[0].instanceServiceContainer.stylesheetService.removeDeclarationFromRule(property.styleRule, property.styleDeclaration.name);\r\n      return;\r\n    }\r\n    super.clearValue(designItems, property, clearType);\r\n  }\r\n\r\n  override getValue(designItems: IDesignItem[], property: IProperty & { styleRule: IStyleRule, styleDeclaration: IStyleDeclaration }) {\r\n    if (property.styleRule?.selector && property.styleDeclaration)\n      return appendCssImportant(property.styleDeclaration.value, property.styleDeclaration.important);\n    return super.getValue(designItems, property);\n  }\n\n  override getUnsetValue(designItems: IDesignItem[], property: IProperty & { styleRule: IStyleRule, styleDeclaration: IStyleDeclaration }) {\n    if (property.styleRule?.selector && property.styleDeclaration)\n      return appendCssImportant(property.styleDeclaration.value, property.styleDeclaration.important);\n    return super.getUnsetValue(designItems, property);\n  }\n\r\n  override isSet(designItems: IDesignItem[], property: IProperty & { styleRule: IStyleRule, styleDeclaration: IStyleDeclaration }): ValueType {\r\n    if (property.styleRule?.selector && property.styleDeclaration) {\r\n      if (designItems[0].hasStyle(property.name))\r\n        return ValueType.none;\r\n      //TODO: we need to check if this is the dec. with the highest specifity\r\n      return ValueType.fromStylesheet;\r\n    }\r\n    return super.isSet(designItems, property);\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    if (property.name.startsWith('--'))\r\n      return BindingTarget.cssvar;\r\n    return BindingTarget.css;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/CssCustomPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { ValueType } from '../ValueType.js';\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\nimport { DesignerCanvas } from '../../../widgets/designerView/designerCanvas.js';\nimport { AbstractCssPropertiesService } from './AbstractCssPropertiesService.js';\nimport { appendCssImportant } from '../../../helper/CssImportant.js';\n\r\nexport class CssCustomPropertiesService extends AbstractCssPropertiesService {\r\n\r\n\r\n  removeInheritedCustomProperties: boolean\r\n\r\n  constructor(removeInheritedCustomProperties = true) {\r\n    super();\r\n    this.name = 'customProperties';\r\n    this.removeInheritedCustomProperties = removeInheritedCustomProperties;\r\n  }\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.fullOnValueChange;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return this._enrichCssProperty({ name: name, type: 'string', service: this, propertyType: PropertyType.cssValue });\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (designItem.nodeType != Node.ELEMENT_NODE)\r\n      return [];\r\n    if (designItem?.element?.computedStyleMap) {\r\n      let rootMap = Array.from((<DesignerCanvas>designItem.instanceServiceContainer.designerCanvas).computedStyleMap()).map(x => x[0]).filter(key => key.startsWith(\"--\"));\r\n      let props = Array.from(designItem.element.computedStyleMap()).map(x => x[0]).filter(key => key.startsWith(\"--\"))\r\n\r\n      if (this.removeInheritedCustomProperties)\r\n        props = props.filter(x => !rootMap.includes(x));\r\n\r\n      let arr: IProperty[] = props.map(x => ({\r\n        name: x,\r\n        service: this,\r\n        propertyType: PropertyType.cssValue\r\n      })).map(x => this._enrichCssProperty(x));\r\n      return arr;\r\n    }\r\n\r\n    let rootMap = Array.from(getComputedStyle(<DesignerCanvas>designItem.instanceServiceContainer.designerCanvas)).map(x => x[0]).filter(key => key.startsWith(\"--\"));\r\n    let props = Array.from(getComputedStyle(designItem.element)).map(x => x[0]).filter(key => key.startsWith(\"--\"))\r\n\r\n    if (this.removeInheritedCustomProperties)\r\n      props = props.filter(x => !rootMap.includes(x));\r\n\r\n    let arr: IProperty[] = props.map(x => ({\r\n      name: x,\r\n      service: this,\r\n      propertyType: PropertyType.cssValue\r\n    })).map(x => this._enrichCssProperty(x));\r\n    return arr;\r\n  }\r\n\r\n  override clearValue(designItems: IDesignItem[], property: IProperty, clearType: 'all' | 'binding' | 'value') {\r\n    super.clearValue(designItems, property, clearType);\r\n  }\r\n\r\n  override getValue(designItems: IDesignItem[], property: IProperty) {\r\n    let val = designItems[0].getStyle(property.name);\n    if (val)\n      return appendCssImportant(val, designItems[0].isStyleImportant(property.name));\n    return getComputedStyle(designItems[0].element).getPropertyValue(property.name);\r\n  }\r\n\r\n  override getUnsetValue(designItems: IDesignItem[], property: IProperty) {\r\n    if (designItems?.[0].element?.computedStyleMap) {\r\n      return designItems[0].element.computedStyleMap().get(property.name)?.[0];\r\n    } else {\r\n      return getComputedStyle(designItems[0].element).getPropertyValue(property.name);\r\n    }\r\n  }\r\n\r\n  override isSet(designItems: IDesignItem[], property: IProperty): ValueType {\r\n    if (super.isSet(designItems, property) == ValueType.bound)\r\n      return ValueType.bound;\r\n    return designItems[0].hasStyle(property.name) ? ValueType.all : ValueType.none;\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    return BindingTarget.cssvar;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/CssProperties.json",
    "content": "{\r\n    \"accentColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"additiveSymbols\": {},\r\n    \"alignContent\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"alignItems\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"self-start\",\r\n            \"self-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"alignSelf\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"self-start\",\r\n            \"self-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"alignmentBaseline\": {},\r\n    \"all\": {},\r\n    \"animation\": {},\r\n    \"animationDelay\": {\r\n        \"type\": \"time\"\r\n    },\r\n    \"animationDirection\": {},\r\n    \"animationDuration\": {\r\n        \"type\": \"time\"\r\n    },\r\n    \"animationFillMode\": {},\r\n    \"animationIterationCount\": {},\r\n    \"animationName\": {},\r\n    \"animationPlayState\": {},\r\n    \"animationTimingFunction\": {},\r\n    \"appRegion\": {},\r\n    \"appearance\": {},\r\n    \"ascentOverride\": {},\r\n    \"aspectRatio\": {},\r\n    \"backdropFilter\": {},\r\n    \"backfaceVisibility\": {},\r\n    \"background\": {},\r\n    \"backgroundAttachment\": {},\r\n    \"backgroundBlendMode\": {},\r\n    \"backgroundClip\": {},\r\n    \"backgroundColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"backgroundImage\": {},\r\n    \"backgroundOrigin\": {},\r\n    \"backgroundPosition\": {},\r\n    \"backgroundPositionX\": {},\r\n    \"backgroundPositionY\": {},\r\n    \"backgroundRepeat\": {},\r\n    \"backgroundRepeatX\": {},\r\n    \"backgroundRepeatY\": {},\r\n    \"backgroundSize\": {},\r\n    \"basePalette\": {},\r\n    \"baselineShift\": {},\r\n    \"blockSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"border\": {},\r\n    \"borderBlock\": {},\r\n    \"borderBlockColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderBlockEnd\": {},\r\n    \"borderBlockEndColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderBlockEndStyle\": {},\r\n    \"borderBlockEndWidth\": {},\r\n    \"borderBlockStart\": {},\r\n    \"borderBlockStartColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderBlockStartStyle\": {},\r\n    \"borderBlockStartWidth\": {},\r\n    \"borderBlockStyle\": {},\r\n    \"borderBlockWidth\": {},\r\n    \"borderBottom\": {},\r\n    \"borderBottomColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderBottomLeftRadius\": {},\r\n    \"borderBottomRightRadius\": {},\r\n    \"borderBottomStyle\": {},\r\n    \"borderBottomWidth\": {},\r\n    \"borderCollapse\": {},\r\n    \"borderColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderEndEndRadius\": {},\r\n    \"borderEndStartRadius\": {},\r\n    \"borderImage\": {},\r\n    \"borderImageOutset\": {},\r\n    \"borderImageRepeat\": {},\r\n    \"borderImageSlice\": {},\r\n    \"borderImageSource\": {},\r\n    \"borderImageWidth\": {},\r\n    \"borderInline\": {},\r\n    \"borderInlineColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderInlineEnd\": {},\r\n    \"borderInlineEndColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderInlineEndStyle\": {},\r\n    \"borderInlineEndWidth\": {},\r\n    \"borderInlineStart\": {},\r\n    \"borderInlineStartColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderInlineStartStyle\": {},\r\n    \"borderInlineStartWidth\": {},\r\n    \"borderInlineStyle\": {},\r\n    \"borderInlineWidth\": {},\r\n    \"borderLeft\": {},\r\n    \"borderLeftColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderLeftStyle\": {},\r\n    \"borderLeftWidth\": {},\r\n    \"borderRadius\": {},\r\n    \"borderRight\": {},\r\n    \"borderRightColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderRightStyle\": {},\r\n    \"borderRightWidth\": {},\r\n    \"borderSpacing\": {},\r\n    \"borderStartEndRadius\": {},\r\n    \"borderStartStartRadius\": {},\r\n    \"borderStyle\": {},\r\n    \"borderTop\": {},\r\n    \"borderTopColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"borderTopLeftRadius\": {},\r\n    \"borderTopRightRadius\": {},\r\n    \"borderTopStyle\": {},\r\n    \"borderTopWidth\": {},\r\n    \"borderWidth\": {\r\n        \"type\": \"length\",\r\n        \"values\": [\r\n            \"medium\",\r\n            \"thin\",\r\n            \"thick\"\r\n        ]\r\n    },\r\n    \"bottom\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"boxShadow\": {},\r\n    \"boxSizing\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"border-box\",\r\n            \"content-box\"\r\n        ]\r\n    },\r\n    \"breakAfter\": {},\r\n    \"breakBefore\": {},\r\n    \"breakInside\": {},\r\n    \"bufferedRendering\": {},\r\n    \"captionSide\": {},\r\n    \"caretColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"clear\": {},\r\n    \"clip\": {},\r\n    \"clipPath\": {},\r\n    \"clipRule\": {},\r\n    \"color\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"colorInterpolation\": {},\r\n    \"colorInterpolationFilters\": {},\r\n    \"colorRendering\": {},\r\n    \"colorScheme\": {},\r\n    \"columnCount\": {},\r\n    \"columnFill\": {},\r\n    \"columnGap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"columnRule\": {},\r\n    \"columnRuleColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"columnRuleStyle\": {},\r\n    \"columnRuleWidth\": {},\r\n    \"columnSpan\": {},\r\n    \"columnWidth\": {},\r\n    \"columns\": {},\r\n    \"contain\": {},\r\n    \"containIntrinsicBlockSize\": {},\r\n    \"containIntrinsicHeight\": {},\r\n    \"containIntrinsicInlineSize\": {},\r\n    \"containIntrinsicSize\": {},\r\n    \"containIntrinsicWidth\": {},\r\n    \"container\": {},\r\n    \"containerName\": {},\r\n    \"containerType\": {},\r\n    \"content\": {},\r\n    \"contentVisibility\": {},\r\n    \"counterIncrement\": {},\r\n    \"counterReset\": {},\r\n    \"counterSet\": {},\r\n    \"cursor\": {},\r\n    \"cx\": {},\r\n    \"cy\": {},\r\n    \"d\": {},\r\n    \"descentOverride\": {},\r\n    \"direction\": {},\r\n    \"display\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"block\",\r\n            \"inline\",\r\n            \"inline-block\",\r\n            \"flex\",\r\n            \"inline-flex\",\r\n            \"contents\",\r\n            \"grid\",\r\n            \"inline-grid\",\r\n            \"inline-table\",\r\n            \"list-item\",\r\n            \"run-in\",\r\n            \"table\",\r\n            \"table-caption\",\r\n            \"table-column-group\",\r\n            \"table-header-group\",\r\n            \"table-footer-group\",\r\n            \"tabl-row-group\",\r\n            \"table-cell\",\r\n            \"table-column\",\r\n            \"table-row\",\r\n            \"inherit\",\r\n            \"initial\",\r\n            \"none\"\r\n        ]\r\n    },\r\n    \"dominantBaseline\": {},\r\n    \"emptyCells\": {},\r\n    \"fallback\": {},\r\n    \"fill\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"fillOpacity\": {},\r\n    \"fillRule\": {},\r\n    \"filter\": {},\r\n    \"flex\": {},\r\n    \"flexBasis\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"flexDirection\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"row\",\r\n            \"column\",\r\n            \"row-reverse\",\r\n            \"column-reverse\"\r\n        ]\r\n    },\r\n    \"flexFlow\": {},\r\n    \"flexGrow\": {\r\n        \"type\": \"number\"\r\n    },\r\n    \"flexShrink\": {\r\n        \"type\": \"number\"\r\n    },\r\n    \"flexWrap\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"nowrap\",\r\n            \"wrap\",\r\n            \"wrap-reverse\"\r\n        ]\r\n    },\r\n    \"fieldSizing\": {},\r\n    \"float\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"none\",\r\n            \"left\",\r\n            \"right\",\r\n            \"inline-start\",\r\n            \"inline-end\"\r\n        ]\r\n    },\r\n    \"floodColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"floodOpacity\": {},\r\n    \"font\": {},\r\n    \"fontDisplay\": {},\r\n    \"fontFamily\": {\r\n        \"type\": \"font\"\r\n    },\r\n    \"fontFeatureSettings\": {},\r\n    \"fontKerning\": {},\r\n    \"fontOpticalSizing\": {},\r\n    \"fontPalette\": {},\r\n    \"fontSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"fontSizeAdjust\": {},\r\n    \"fontStretch\": {},\r\n    \"fontStyle\": {},\r\n    \"fontSynthesis\": {},\r\n    \"fontSynthesisSmallCaps\": {},\r\n    \"fontSynthesisStyle\": {},\r\n    \"fontSynthesisWeight\": {},\r\n    \"fontVariant\": {},\r\n    \"fontVariantCaps\": {},\r\n    \"fontVariantEastAsian\": {},\r\n    \"fontVariantLigatures\": {},\r\n    \"fontVariantNumeric\": {},\r\n    \"fontVariationSettings\": {},\r\n    \"fontWeight\": {},\r\n    \"forcedColorAdjust\": {},\r\n    \"gap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"grid\": {},\r\n    \"gridArea\": {},\r\n    \"gridAutoColumns\": {},\r\n    \"gridAutoFlow\": {},\r\n    \"gridAutoRows\": {},\r\n    \"gridColumn\": {},\r\n    \"gridColumnEnd\": {},\r\n    \"gridColumnGap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"gridColumnStart\": {},\r\n    \"gridGap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"gridRow\": {},\r\n    \"gridRowEnd\": {},\r\n    \"gridRowGap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"gridRowStart\": {},\r\n    \"gridTemplate\": {},\r\n    \"gridTemplateAreas\": {},\r\n    \"gridTemplateColumns\": {},\r\n    \"gridTemplateRows\": {},\r\n    \"height\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"hyphenateCharacter\": {},\r\n    \"hyphens\": {},\r\n    \"imageOrientation\": {},\r\n    \"imageRendering\": {},\r\n    \"imageResolution\": {\r\n        \"type\": \"resolution\"\r\n    },\r\n    \"inherits\": {},\r\n    \"initialValue\": {},\r\n    \"inlineSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"inset\": {},\r\n    \"insetBlock\": {},\r\n    \"insetBlockEnd\": {},\r\n    \"insetBlockStart\": {},\r\n    \"insetInline\": {},\r\n    \"insetInlineEnd\": {},\r\n    \"insetInlineStart\": {},\r\n    \"isolation\": {},\r\n    \"justifyContent\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"self-start\",\r\n            \"self-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"justifyItems\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"justifySelf\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"flex-start\",\r\n            \"flex-end\",\r\n            \"self-start\",\r\n            \"self-end\",\r\n            \"center\",\r\n            \"normal\",\r\n            \"baseline\",\r\n            \"first baseline\",\r\n            \"last baseline\",\r\n            \"space-between\",\r\n            \"space-around\",\r\n            \"space-evenly\",\r\n            \"stretch\",\r\n            \"safe\",\r\n            \"unsafe\"\r\n        ]\r\n    },\r\n    \"left\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"letterSpacing\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"lightingColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"lineBreak\": {},\r\n    \"lineGapOverride\": {},\r\n    \"lineHeight\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"listStyle\": {},\r\n    \"listStyleImage\": {},\r\n    \"listStylePosition\": {},\r\n    \"listStyleType\": {},\r\n    \"margin\": {},\r\n    \"marginBlock\": {},\r\n    \"marginBlockEnd\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginBlockStart\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginBottom\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginInline\": {},\r\n    \"marginInlineEnd\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginInlineStart\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginLeft\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginRight\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marginTop\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"marker\": {},\r\n    \"markerEnd\": {},\r\n    \"markerMid\": {},\r\n    \"markerStart\": {},\r\n    \"mask\": {},\r\n    \"maskType\": {},\r\n    \"maxBlockSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"maxHeight\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"maxInlineSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"maxWidth\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"minBlockSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"minHeight\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"minInlineSize\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"minWidth\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"mixBlendMode\": {},\r\n    \"negative\": {},\r\n    \"objectFit\": {},\r\n    \"objectPosition\": {\r\n        \"type\": \"position\"\r\n    },\r\n    \"objectViewBox\": {},\r\n    \"offset\": {},\r\n    \"offsetDistance\": {},\r\n    \"offsetPath\": {},\r\n    \"offsetRotate\": {\r\n        \"type\": \"angle\"\r\n    },\r\n    \"opacity\": {\r\n        \"type\": \"number\"\r\n    },\r\n    \"order\": {},\r\n    \"orphans\": {},\r\n    \"outline\": {},\r\n    \"outlineColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"outlineOffset\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"outlineStyle\": {},\r\n    \"outlineWidth\": {\r\n        \"type\": \"length\",\r\n        \"values\": [\r\n            \"medium\",\r\n            \"thin\",\r\n            \"thick\"\r\n        ]\r\n    },\r\n    \"overflow\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"visible\",\r\n            \"hidden\",\r\n            \"clip\",\r\n            \"scroll\",\r\n            \"auto\"\r\n        ]\r\n    },\r\n    \"overflowAnchor\": {},\r\n    \"overflowClipMargin\": {},\r\n    \"overflowWrap\": {},\r\n    \"overflowX\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"visible\",\r\n            \"hidden\",\r\n            \"clip\",\r\n            \"scroll\",\r\n            \"auto\"\r\n        ]\r\n    },\r\n    \"overflowY\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"visible\",\r\n            \"hidden\",\r\n            \"clip\",\r\n            \"scroll\",\r\n            \"auto\"\r\n        ]\r\n    },\r\n    \"overrideColors\": {},\r\n    \"overscrollBehavior\": {},\r\n    \"overscrollBehaviorBlock\": {},\r\n    \"overscrollBehaviorInline\": {},\r\n    \"overscrollBehaviorX\": {},\r\n    \"overscrollBehaviorY\": {},\r\n    \"pad\": {},\r\n    \"padding\": {},\r\n    \"paddingBlock\": {},\r\n    \"paddingBlockEnd\": {},\r\n    \"paddingBlockStart\": {},\r\n    \"paddingBottom\": {},\r\n    \"paddingInline\": {},\r\n    \"paddingInlineEnd\": {},\r\n    \"paddingInlineStart\": {},\r\n    \"paddingLeft\": {},\r\n    \"paddingRight\": {},\r\n    \"paddingTop\": {},\r\n    \"page\": {},\r\n    \"pageBreakAfter\": {},\r\n    \"pageBreakBefore\": {},\r\n    \"pageBreakInside\": {},\r\n    \"pageOrientation\": {},\r\n    \"paintOrder\": {},\r\n    \"perspective\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"perspectiveOrigin\": {},\r\n    \"placeContent\": {},\r\n    \"placeItems\": {},\r\n    \"placeSelf\": {},\r\n    \"pointerEvents\": {},\r\n    \"position\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"static\",\r\n            \"relative\",\r\n            \"absolute\",\r\n            \"fixed\",\r\n            \"sticky\"\r\n        ]\r\n    },\r\n    \"prefix\": {},\r\n    \"quotes\": {},\r\n    \"r\": {},\r\n    \"range\": {},\r\n    \"resize\": {},\r\n    \"right\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"rotate\": {\r\n        \"type\": \"angle\"\r\n    },\r\n    \"rowGap\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"rubyAlign\": {},\r\n    \"rubyPosition\": {},\r\n    \"rx\": {},\r\n    \"ry\": {},\r\n    \"scale\": {\r\n        \"type\": \"scale\"\r\n    },\r\n    \"scrollbarColor\": {},\r\n    \"scrollbarGutter\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"auto\",\r\n            \"stable\",\r\n            \"stable both-edges\"\r\n        ]\r\n    },\r\n    \"scrollbarWidth\": {},\r\n    \"scrollBehavior\": {},\r\n    \"scrollMargin\": {},\r\n    \"scrollMarginBlock\": {},\r\n    \"scrollMarginBlockEnd\": {},\r\n    \"scrollMarginBlockStart\": {},\r\n    \"scrollMarginBottom\": {},\r\n    \"scrollMarginInline\": {},\r\n    \"scrollMarginInlineEnd\": {},\r\n    \"scrollMarginInlineStart\": {},\r\n    \"scrollMarginLeft\": {},\r\n    \"scrollMarginRight\": {},\r\n    \"scrollMarginTop\": {},\r\n    \"scrollPadding\": {},\r\n    \"scrollPaddingBlock\": {},\r\n    \"scrollPaddingBlockEnd\": {},\r\n    \"scrollPaddingBlockStart\": {},\r\n    \"scrollPaddingBottom\": {},\r\n    \"scrollPaddingInline\": {},\r\n    \"scrollPaddingInlineEnd\": {},\r\n    \"scrollPaddingInlineStart\": {},\r\n    \"scrollPaddingLeft\": {},\r\n    \"scrollPaddingRight\": {},\r\n    \"scrollPaddingTop\": {},\r\n    \"scrollSnapAlign\": {},\r\n    \"scrollSnapStop\": {},\r\n    \"scrollSnapType\": {},\r\n    \"shapeImageThreshold\": {},\r\n    \"shapeMargin\": {},\r\n    \"shapeOutside\": {},\r\n    \"shapeRendering\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"auto\",\r\n            \"optimizeSpeed\",\r\n            \"crispEdges\",\r\n            \"geometricPrecision\"\r\n        ]\r\n    },\r\n    \"size\": {},\r\n    \"sizeAdjust\": {},\r\n    \"speak\": {},\r\n    \"speakAs\": {},\r\n    \"src\": {},\r\n    \"stopColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"stopOpacity\": {},\r\n    \"stroke\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"strokeDasharray\": {},\r\n    \"strokeDashoffset\": {},\r\n    \"strokeLinecap\": {},\r\n    \"strokeLinejoin\": {},\r\n    \"strokeMiterlimit\": {},\r\n    \"strokeOpacity\": {},\r\n    \"strokeWidth\": {\r\n        \"type\": \"number\"\r\n    },\r\n    \"suffix\": {},\r\n    \"symbols\": {},\r\n    \"syntax\": {},\r\n    \"system\": {},\r\n    \"tabSize\": {},\r\n    \"tableLayout\": {},\r\n    \"textAlign\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"start\",\r\n            \"end\",\r\n            \"left\",\r\n            \"right\",\r\n            \"center\",\r\n            \"justify\",\r\n            \"justify-all\",\r\n            \"match-parent\"\r\n        ]\r\n    },\r\n    \"textAlignLast\": {},\r\n    \"textAnchor\": {},\r\n    \"textCombineUpright\": {},\r\n    \"textDecoration\": {},\r\n    \"textDecorationColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"textDecorationLine\": {},\r\n    \"textDecorationSkipInk\": {},\r\n    \"textDecorationStyle\": {},\r\n    \"textDecorationThickness\": {},\r\n    \"textEmphasis\": {},\r\n    \"textEmphasisColor\": {\r\n        \"type\": \"color\"\r\n    },\r\n    \"textEmphasisPosition\": {},\r\n    \"textEmphasisStyle\": {},\r\n    \"textIndent\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"textOrientation\": {},\r\n    \"textOverflow\": {},\r\n    \"textRendering\": {},\r\n    \"textShadow\": {},\r\n    \"textSizeAdjust\": {},\r\n    \"textTransform\": {},\r\n    \"textUnderlineOffset\": {},\r\n    \"textUnderlinePosition\": {},\r\n    \"textWrap\": {},\r\n    \"textWrapMode\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"wrap\",\r\n            \"nowrap\"\r\n        ]\r\n    },\r\n    \"textWrapStyle\": {\r\n        \"type\": \"list\",\r\n        \"values\": [\r\n            \"auto\",\r\n            \"balance\",\r\n            \"pretty\",\r\n            \"stable\"\r\n        ]\r\n    },\r\n    \"top\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"touchAction\": {},\r\n    \"transform\": {},\r\n    \"transformBox\": {},\r\n    \"transformOrigin\": {},\r\n    \"transformStyle\": {},\r\n    \"transition\": {},\r\n    \"transitionDelay\": {\r\n        \"type\": \"time\"\r\n    },\r\n    \"transitionBehavior\": {},\r\n    \"transitionDuration\": {\r\n        \"type\": \"time\"\r\n    },\r\n    \"transitionProperty\": {},\r\n    \"transitionTimingFunction\": {},\r\n    \"translate\": {},\r\n    \"unicodeBidi\": {},\r\n    \"unicodeRange\": {},\r\n    \"userSelect\": {},\r\n    \"vectorEffect\": {},\r\n    \"verticalAlign\": {},\r\n    \"visibility\": {},\r\n    \"whiteSpace\": {},\r\n    \"widows\": {},\r\n    \"width\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"willChange\": {},\r\n    \"wordBreak\": {},\r\n    \"wordSpacing\": {\r\n        \"type\": \"length\"\r\n    },\r\n    \"wordWrap\": {},\r\n    \"writingMode\": {},\r\n    \"x\": {},\r\n    \"y\": {},\r\n    \"zIndex\": {},\r\n    \"zoom\": {\r\n        \"type\": \"scale\"\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/CssPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { BindingTarget } from '../../../item/BindingTarget.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { GridAssignedRowColumnPropertyEditor } from '../propertyEditors/special/GridAssignedRowColumnPropertyEditor.js';\r\nimport { MetricsPropertyEditor } from '../propertyEditors/special/MetricsPropertyEditor.js';\r\nimport cssProperties from \"./CssProperties.json\" with { type: 'json' };\r\nimport { AbstractCssPropertiesService } from './AbstractCssPropertiesService.js';\r\n\r\nexport class CssPropertiesService extends AbstractCssPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.none;\r\n  }\r\n\r\n  //metrics\r\n\r\n  public layout = {\r\n    \"common\": [\r\n      \"display\",\r\n      \"color\",\r\n      \"background-color\",\r\n      \"box-sizing\",\r\n      \"border\",\r\n      \"box-shadow\",\r\n      \"opacity\",\r\n      \"position\",\r\n      \"align-content\",\r\n    ],\r\n    \"font\": [\r\n      \"font-family\",\r\n      \"font-size\",\r\n      \"font-weight\",\r\n    ],\r\n    \"layout\": [\r\n      \"inset\",\r\n      \"margin\",\r\n      \"border\",\r\n      \"padding\",\r\n      \"overflow\",\r\n      \"metrics\"\r\n    ]\r\n  }\r\n\r\n  public grid = [\r\n    \"display\",\r\n    \"position\",\r\n    \"grid-template-columns\",\r\n    \"grid-template-rows\",\r\n    \"gap\",\r\n    \"column-gap\",\r\n    \"row-gap\",\r\n    \"align-content\",\r\n    \"justify-content\",\r\n    \"align-items\",\r\n    \"justify-items\"\r\n  ];\r\n\r\n  public gridChild = [\r\n    \"grid-row\",\r\n    \"grid-column\",\r\n    \"assigned-row-column\",\r\n    \"align-self\",\r\n    \"justify-self\"\r\n  ];\r\n\r\n  public flex = [\r\n    \"display\",\r\n    \"position\",\r\n    \"flex-direction\",\r\n    \"flex-wrap\",\r\n    \"align-content\",\r\n    \"justify-content\",\r\n    \"align-items\",\r\n    \"gap\"\r\n  ];\r\n\r\n  public flexChild = [\r\n    \"align-self\",\r\n    \"justify-self\"\r\n  ];\r\n\r\n  public svg = [\r\n    \"fill\",\r\n    \"fill-rule\",\r\n    \"fill-opacity\",\r\n    \"stroke\",\r\n    \"stroke-width\",\r\n    \"stroke-dasharray\",\r\n    \"stroke-dashoffset\",\r\n    \"stroke-opacity\"\r\n  ];\r\n\r\n  public layoutSvgChild = {\r\n    \"common\": [\r\n      \"display\",\r\n      \"fill\",\r\n      \"fill-rule\",\r\n      \"fill-opacity\",\r\n      \"stroke\",\r\n      \"stroke-width\",\r\n      \"stroke-dasharray\",\r\n      \"stroke-dashoffset\",\r\n      \"stroke-opacity\"\r\n    ],\r\n  }\r\n\r\n  constructor(name: 'layout' | 'layoutSvgChild' | 'grid' | 'gridChild' | 'flex' | 'flexChild' | 'svg') {\r\n    super();\r\n    this.name = name;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return this._getPropertyDef(name);\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    const propNames: string[] | Record<string, string[]> = this[this.name];\r\n    if (Array.isArray(propNames)) {\r\n      const propertiesList = propNames.map(x => this._getPropertyDef(x));\r\n      return propertiesList;\r\n    } else {\r\n      let grps: IPropertyGroup[] = [];\r\n      for (let g in propNames) {\r\n        let grp: IPropertyGroup = { name: g, properties: propNames[g].map(x => this._getPropertyDef(x)) };\r\n        grps.push(grp);\r\n      }\r\n      return grps;\r\n    }\r\n  }\r\n\r\n  getPropertyNameSuggestions(designItems: IDesignItem[]): string[] {\r\n    return Object.keys(cssProperties).map(x => PropertiesHelper.camelToDashCase(x));\r\n  }\r\n\r\n  protected _getPropertyType(name: string): string {\r\n    const typeFromJson = cssProperties[name]?.type;\r\n    return typeFromJson ? 'css-' + typeFromJson : 'string';\r\n  }\r\n\r\n  protected _getPropertyValues(name: string): string[] {\r\n    return cssProperties[name]?.values;\r\n  }\r\n\r\n  _getPropertyDef(name: string): IProperty {\r\n    const camelName = PropertiesHelper.dashToCamelCase(name);\r\n    switch (camelName) {\r\n      case 'assignedRowColumn':\n        return { name, service: this, propertyType: PropertyType.complex, createEditor: (p) => new GridAssignedRowColumnPropertyEditor(p) };\n      case 'metrics':\n        return { name, service: this, propertyType: PropertyType.complex, hideLabel: true, createEditor: (p) => new MetricsPropertyEditor(p) };\n      default:\r\n        return this._enrichCssProperty({\r\n          name,\r\n          //attributeName: PropertiesHelper.camelToDashCase(name),\r\n          type: this._getPropertyType(camelName),\r\n          values: this._getPropertyValues(camelName),\r\n          service: this,\r\n          propertyType: PropertyType.cssValue\r\n        })\r\n    }\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    return BindingTarget.css;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/IJsonPropertyDefinition.ts",
    "content": "import { PropertyType } from '../PropertyType.js';\r\n\r\nexport interface IJsonPropertyDefinition {\r\n  name: string;\r\n  propertyName?: string,\r\n  attributeName?: string\r\n  description?: string;\r\n  type?: string; // -> string, number, list, color, thickness, css numeric types\r\n  default?: any;\r\n  min?: number;\r\n  max?: number;\r\n  step?: number;\r\n  values?: string[]; // list selectable values\r\n  units?: string[]; // selectable units for editors that support unit changes\r\n  numericValueDecimalPlaces?: number; // rounding used by numeric unit conversions\r\n  enumValues?: [name: string, value: string | number][]; // list selectable enum values\r\n  value?: any;\r\n  defaultValue?: any;\r\n  propertyType?: PropertyType.cssValue\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/IJsonPropertyDefinitions.ts",
    "content": "import { IJsonPropertyDefinition } from './IJsonPropertyDefinition.js';\r\n\r\nexport interface IJsonPropertyDefinitions {\r\n  [key: string]: IJsonPropertyDefinition[];\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/ListPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IJsonPropertyDefinitions } from './IJsonPropertyDefinitions.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport class ListPropertiesService extends AbstractPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  public name = \"list\"\r\n\r\n  private _propertys: Map<string, IProperty[]> = new Map();\r\n\r\n  constructor(propertyDefinitions: IJsonPropertyDefinitions, recreateElementsOnPropertyChange?: boolean) {\r\n    super(recreateElementsOnPropertyChange);\r\n\r\n    for (let e in propertyDefinitions) {\r\n      let parr: IProperty[] = []\r\n      this._propertys.set(e, parr);\r\n      for (let p in propertyDefinitions[e]) {\r\n        let pdef = propertyDefinitions[e][p];\r\n        parr.push(\r\n          {\r\n            name: pdef.name,\r\n            propertyName: pdef.propertyName,\r\n            attributeName: pdef.attributeName,\r\n            description: pdef.description,\r\n            type: pdef.type,\r\n            default: pdef.default,\r\n            min: pdef.min,\r\n            max: pdef.max,\r\n            step: pdef.step,\r\n            values: pdef.values,\r\n            enumValues: pdef.enumValues,\r\n            value: pdef.value,\r\n            defaultValue: pdef.defaultValue,\r\n            service: this,\r\n            propertyType: pdef.propertyType ?? PropertyType.propertyAndAttribute\r\n          });\r\n      }\r\n    }\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return this._propertys.has(designItem.element.localName);\r\n  }\r\n\r\n  protected override _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    return this._propertys.get(designItem.element.localName);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/Lit2PropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { AbstractPolymerLikePropertiesService } from './AbstractPolymerLikePropertiesService.js';\r\nimport { PropertiesHelper } from './PropertiesHelper.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport class Lit2PropertiesService extends AbstractPolymerLikePropertiesService {\r\n\r\n  public name = \"lit2\"\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    let prop = (<any>designItem.element.constructor).elementProperties;\r\n    if (prop)\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  public override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (!this.isHandledElement(designItem))\r\n      return null;\r\n    let properties: IProperty[] = [];\r\n    for (const p of (<any>designItem.element.constructor).elementProperties.entries()) {\r\n      let name = p[0];\r\n      const litProperty = p[1];\r\n      let type = litProperty;\r\n      if (litProperty.type)\r\n        type = litProperty.type;\r\n\r\n      if (type === String) {\r\n        let property: IProperty = { name: name, type: \"string\", service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      } else if (type === Object) {\r\n        let property: IProperty = { name: name, type: \"string\", service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      } else if (type === Number) {\r\n        let property: IProperty = { name: name, type: \"number\", service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      } else if (type === Date) {\r\n        let property: IProperty = { name: name, type: \"date\", service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      } else if (type === Boolean) {\r\n        let property: IProperty = { name: name, type: \"boolean\", service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      } else if (PropertiesHelper.isTypescriptEnum(type)) {\r\n        let property: IProperty = { name: name, type: \"enum\", enumValues: PropertiesHelper.getTypescriptEnumEntries(type), service: this, propertyType: PropertyType.propertyAndAttribute };\r\n        properties.push(property);\r\n      }\r\n    }\r\n    return properties;\r\n  }\r\n\r\n  protected override _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/LitElementPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { AbstractPolymerLikePropertiesService } from './AbstractPolymerLikePropertiesService.js';\r\n\r\nexport class LitElementPropertiesService extends AbstractPolymerLikePropertiesService {\r\n\r\n  public name = \"lit\"\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    let proto = (<any>designItem.element.constructor).__proto__;\r\n    while (proto != null) {\r\n      if (proto.name == 'LitElement')\r\n        return true;\r\n      if (proto.name == undefined || proto.name == 'HTMLElement' || proto.name == 'Element' || proto.name == 'Node' || proto.name == 'HTMLElement')\r\n        return false;\r\n      proto = proto.__proto__;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  protected override _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/MathMLElementsPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { CommonPropertiesService } from './CommonPropertiesService.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\n\r\nexport class MathMLElementsPropertiesService extends CommonPropertiesService {\r\n\r\n  private commonMathProperties: IProperty[] = [\r\n    {\r\n      name: \"displaystyle\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      defaultValue: true,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private mathProperties: IProperty[] = [\r\n    {\r\n      name: \"display\",\r\n      type: \"list\",\r\n      values: [\"block\", \"inline\"],\r\n      service: this,\r\n      defaultValue: \"text\",\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private mfracProperties: IProperty[] = [\r\n    {\r\n      name: \"denomalign\",\r\n      type: \"list\",\r\n      values: [\"left\", \"center\", \"right\"],\r\n      service: this,\r\n      defaultValue: \"center\",\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"linethickness\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"numalign\",\r\n      type: \"list\",\r\n      values: [\"left\", \"center\", \"right\"],\r\n      service: this,\r\n      defaultValue: \"center\",\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n  ];\r\n\r\n  public override name = \"mathml\"\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return designItem.element instanceof designItem.window.MathMLElement;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return (<IProperty[]>await this.getProperties(designItem)).find(x => x.name == name);\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (!this.isHandledElement(designItem))\r\n      return null;\r\n\r\n    switch (designItem.element.localName) {\r\n      case 'math':\r\n        return [...this.commonMathProperties, ...this.mathProperties];\r\n      case 'merror':\r\n        return [...this.commonMathProperties];\r\n      case 'mfrac':\r\n        return [...this.commonMathProperties, ...this.mfracProperties];\r\n      default:\r\n        return [...this.commonMathProperties];\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/NativeElementsPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\n\r\nexport class NativeElementsPropertiesService extends AbstractPropertiesService {\r\n\r\n  private inputProperties: IProperty[] = [\r\n    {\r\n      name: \"type\",\r\n      type: \"list\",\r\n      values: [\"text\", \"number\", \"button\", \"checkbox\", \"color\", \"date\", \"datetime-local\", \"email\", \"file\", \"hidden\", \"image\", \"month\", \"password\", \"radio\", \"range\", \"reset\", \"search\", \"submit\", \"tel\", \"time\", \"url\", \"week\"],\r\n      service: this,\r\n      defaultValue: \"text\",\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"value\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"placeholder\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"checked\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"min\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"max\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"readonly\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"valueAsDate\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    },\r\n    {\r\n      name: \"valueAsNumber\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    }\r\n  ];\r\n\r\n  private textareaProperties: IProperty[] = [\r\n    {\r\n      name: \"value\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    },\r\n    {\r\n      name: \"placeholder\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"maxlength\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"cols\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"rows\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"readonly\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"resize\",\r\n      type: \"list\",\r\n      values: [\"both\", \"none\", \"horizontal\", \"vertical\"],\r\n      service: this,\r\n      propertyType: PropertyType.cssValue\r\n    }\r\n  ];\r\n\r\n  private selectProperties: IProperty[] = [\r\n    {\r\n      name: \"value\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.property\r\n    },\r\n    {\r\n      name: \"size\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"multiple\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private buttonProperties: IProperty[] = [\r\n    {\r\n      name: \"type\",\r\n      type: \"list\",\r\n      values: [\"button\", \"submit\", \"reset\"],\r\n      service: this,\r\n      defaultValue: \"button\",\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"value\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private anchorProperties: IProperty[] = [\r\n    {\r\n      name: \"href\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private divProperties: IProperty[] = [\r\n    {\r\n      name: \"title\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private imgProperties: IProperty[] = [\r\n    {\r\n      name: \"src\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"alt\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private iframeProperties: IProperty[] = [\r\n    {\r\n      name: \"src\",\r\n      type: \"string\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private formElementProperties: IProperty[] = [\r\n    {\r\n      name: \"autofocus\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"disabled\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"required\",\r\n      type: \"boolean\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  private meterProperties: IProperty[] = [\r\n    {\r\n      name: \"value\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    },\r\n    {\r\n      name: \"min\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"max\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"low\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"high\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }, {\r\n      name: \"optimum\",\r\n      type: \"number\",\r\n      service: this,\r\n      propertyType: PropertyType.propertyAndAttribute\r\n    }\r\n  ];\r\n\r\n  public name = \"native\"\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    switch (designItem.element.localName) {\r\n      case 'input':\r\n      case 'textarea':\r\n      case 'select':\r\n      case 'button':\r\n      case 'a':\r\n      case 'div':\r\n      case 'span':\r\n      case 'br':\r\n      case 'img':\r\n      case 'iframe':\r\n      case 'meter':\r\n      case 'h1':\r\n      case 'h2':\r\n      case 'h3':\r\n      case 'h4':\r\n      case 'h5':\r\n      case 'h6':\r\n      case 'p':\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return (<IProperty[]>await this.getProperties(designItem)).find(x => x.name == name);\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    if (!this.isHandledElement(designItem))\r\n      return null;\r\n    switch (designItem.element.localName) {\r\n      case 'input':\r\n        return [...this.inputProperties, ...this.formElementProperties];\r\n      case 'textarea':\r\n        return [...this.textareaProperties, ...this.formElementProperties];\r\n      case 'select':\r\n        return [...this.selectProperties, ...this.formElementProperties];\r\n      case 'button':\r\n        return [...this.buttonProperties, ...this.formElementProperties];\r\n      case 'a':\r\n        return this.anchorProperties;\r\n      case 'div':\r\n        return this.divProperties;\r\n      case 'img':\r\n        return this.imgProperties;\r\n      case 'iframe':\r\n        return this.iframeProperties;\r\n      case 'meter':\r\n        return this.meterProperties;\r\n      case 'h1':\r\n      case 'h2':\r\n      case 'h3':\r\n      case 'h4':\r\n      case 'h5':\r\n      case 'h6':\r\n      case 'p':\r\n        return [];\r\n    }\r\n\r\n    return null;\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/PolymerPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { AbstractPolymerLikePropertiesService } from './AbstractPolymerLikePropertiesService.js';\r\n\r\nexport class PolymerPropertiesService extends AbstractPolymerLikePropertiesService {\r\n\r\n  public name = \"polymer\"\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return (<any>designItem.element.constructor).polymerElementVersion != null;\r\n  }\r\n\r\n  protected override _notifyChangedProperty(designItem: IDesignItem, property: IProperty, value: any) {\r\n    (<{ set: (name: string, value: any) => void }><any>designItem.element).set(property.name, value);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/PropertiesHelper.ts",
    "content": "export class PropertiesHelper {\r\n  public static isTypescriptEnum(value: any) {\r\n    if (value && typeof value === 'object' && value.constructor == Object) {\r\n      for (let k in value) {\r\n        const tp = typeof value[k];\r\n        if (tp !== 'string' && tp !== 'number')\r\n          return false;\r\n      }\r\n      return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  public static getTypescriptEnumEntries(value: any): [name: string, value: string | number][] {\r\n    let ret: [name: string, value: string | number][] = [];\r\n    for (let k in value) {\r\n      if (isNaN(<any>k))\r\n        ret.push([k, value[k]]);\r\n    }\r\n    return ret;\r\n  }\r\n\r\n  public static camelToDashCase(text: string) {\r\n    return text.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);\r\n  }\r\n\r\n  public static dashToCamelCase(text: string) {\r\n    return text.replace(/-([a-z])/g, (i) => i[1].toUpperCase());\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/SVGElementsPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\nimport { IDesignItem } from '../../../item/IDesignItem.js';\nimport { CommonPropertiesService } from './CommonPropertiesService.js';\nimport { PropertyType } from '../PropertyType.js';\nimport { IPropertyGroup } from '../IPropertyGroup.js';\nimport { RefreshMode } from '../IPropertiesService.js';\n\nexport class SVGElementsPropertiesService extends CommonPropertiesService {\n\n  private rectProperties: IProperty[] = [\n    {\n      name: \"x\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"y\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"width\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"height\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"rx\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"ry\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"pathLength\",\n      type: \"svg-length\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"fill\",\n      type: \"color\",\n      defaultValue: \"transparent\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"fill-opacity\",\n      type: \"number\",\n      defaultValue: \"1\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n  ];\n\n  private lineProperties: IProperty[] = [\n    {\n      name: \"x1\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"y1\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"x2\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"y2\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"pathLength\",\n      type: \"svg-length\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n  ];\n\n  private ellipseProperties: IProperty[] = [\n    {\n      name: \"cx\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"cy\",\n      type: \"svg-length\",\n      defaultValue: \"0\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"rx\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"ry\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"pathLength\",\n      type: \"svg-length\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"fill\",\n      type: \"color\",\n      defaultValue: \"transparent\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"fill-opacity\",\n      type: \"number\",\n      defaultValue: \"1\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n  ];\n\n  private pathProperties: IProperty[] = [\n    {\n      name: \"d\",\n      type: \"string\",\n      defaultValue: '',\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"pathLength\",\n      type: \"svg-length\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n\n  ];\n\n  private svgProperties: IProperty[] = [\n    {\n      name: \"width\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"height\",\n      type: \"svg-length\",\n      defaultValue: \"auto\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"viewBox\",\n      type: \"string\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n  ];\n\n  private defaultProperties: IProperty[] = [\n    {\n      name: \"stroke\",\n      type: \"color\",\n      defaultValue: \"currentcolor\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"stroke-width\",\n      type: \"svg-length\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"stroke-opacity\",\n      type: \"number\",\n      defaultValue: \"1\",\n      service: this,\n      propertyType: PropertyType.attribute\n    },\n    {\n      name: \"visibility\",\n      type: \"list\",\n      values: [\"visible\", \"hidden\"],\n      defaultValue: \"visible\",\n      service: this,\n      propertyType: PropertyType.attribute\n    }\n  ];\n\n  public override name = \"svg\"\n\n  public override getRefreshMode(designItem: IDesignItem) {\n    return RefreshMode.fullOnClassChange;\n  }\n\n  override isHandledElement(designItem: IDesignItem): boolean {\n    switch (designItem.element.localName) {\n      case 'rect':\n      case 'line':\n      case 'ellipse':\n      case 'path':\n      case 'svg':\n        return true;\n    }\n    return false;\n  }\n\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\n    return (<IProperty[]>await this.getProperties(designItem)).find(x => x.name == name);\n  }\n\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\n    let props: IProperty[] = null;\n    if (!this.isHandledElement(designItem))\n      return null;\n    switch (designItem.element.localName) {\n      case 'rect':\n        props = [...this.rectProperties, ...this.defaultProperties];\n        break;\n      case 'line':\n        props = [...this.lineProperties, ...this.defaultProperties];\n        break;\n      case 'ellipse':\n        props = [...this.ellipseProperties, ...this.defaultProperties];\n        break;\n      case 'path':\n        props = [...this.pathProperties, ...this.defaultProperties];\n        break;\n      case 'svg':\n        props = this.svgProperties;\n        break;\n    }\n\n    if (designItem.element.localName == 'line' || designItem.element.localName == 'path') {\n      const markers = this.getAllMarkerIds(designItem);\n      if (markers.length > 0 || designItem.hasAttribute('marker-start')) {\n        props.push({ name: \"marker-start\", type: \"string\", values: markers, service: this, propertyType: PropertyType.attribute });\n      }\n      if (markers.length > 0 || designItem.hasAttribute('marker-mid')) {\n        props.push({ name: \"marker-mid\", type: \"string\", values: markers, service: this, propertyType: PropertyType.attribute });\n      }\n      if (markers.length > 0 || designItem.hasAttribute('marker-end')) {\n        props.push({ name: \"marker-end\", type: \"string\", values: markers, service: this, propertyType: PropertyType.attribute });\n      }\n    }\n\n    return props;\n  }\n\n  private getAllMarkerIds(designItem: IDesignItem): string[] {\n    const svgElement = designItem.element as SVGElement;\n    const svg = svgElement.ownerSVGElement;\n    const markerElements = svg.querySelectorAll<SVGMarkerElement>('defs > marker');\n    return [...markerElements].map(m => `url(#${m.id})`).filter(id => !!id);\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/UnkownElementsPropertiesService.ts",
    "content": "import { IProperty } from '../IProperty.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IPropertyGroup } from '../IPropertyGroup.js';\r\nimport { AbstractPropertiesService } from './AbstractPropertiesService.js';\r\nimport { RefreshMode } from '../IPropertiesService.js';\r\nimport { PropertyType } from '../PropertyType.js';\r\n\r\nexport class UnkownElementsPropertiesService extends AbstractPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  isHandledElement(designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    let list = Object.getOwnPropertyNames(Object.getPrototypeOf(designItem.element));\r\n    let props: IProperty[] = [];\r\n    for (let p of list) {\r\n      if (p.startsWith('on'))\r\n        continue;\r\n      let desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(designItem.element), p);\r\n      if (desc.get || desc.set) {\r\n        let v = designItem.element[p];\r\n        if (typeof v == 'boolean')\r\n          props.push({ name: p, type: 'boolean', service: this, propertyType: PropertyType.propertyAndAttribute });\r\n        else if (typeof v == 'number')\r\n          props.push({ name: p, type: 'bonumberolean', service: this, propertyType: PropertyType.propertyAndAttribute });\r\n        else\r\n          props.push({ name: p, type: 'string', service: this, propertyType: PropertyType.propertyAndAttribute });\r\n      }\r\n    }\r\n    return props;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/propertiesService/services/WebcomponentManifestPropertiesService.ts",
    "content": "import { BindingTarget } from \"../../../item/BindingTarget.js\";\r\nimport { IDesignItem } from \"../../../item/IDesignItem.js\";\r\nimport { IPropertiesService, RefreshMode } from \"../IPropertiesService.js\";\r\nimport { IProperty } from \"../IProperty.js\";\r\nimport { IPropertyGroup } from \"../IPropertyGroup.js\";\r\nimport { PropertyType } from \"../PropertyType.js\";\r\nimport { AbstractPropertiesService } from \"./AbstractPropertiesService.js\";\r\n\r\nexport class WebcomponentManifestPropertiesService extends AbstractPropertiesService implements IPropertiesService {\r\n\r\n  public override getRefreshMode(designItem: IDesignItem) {\r\n    return RefreshMode.full;\r\n  }\r\n\r\n  private _name: string;\r\n  get name() { return this._name; }\r\n\r\n  private _propertiesList: Record<string, IProperty[]>;\r\n\r\n  constructor(name: string, manifest: any, recreateElementsOnPropertyChange?: boolean) {\r\n    super(recreateElementsOnPropertyChange);\r\n    this._name = name;\r\n    this._parseManifest(manifest);\r\n  }\r\n\r\n  private _parseManifest(manifest) {\r\n    this._propertiesList = {};\r\n    let declarations = [];\r\n    for (let m of manifest.modules) {\r\n      if (m.declarations)\r\n        declarations.push(...m.declarations);\r\n    }\r\n    for (let m of manifest.modules) {\r\n      for (let e of m.exports) {\r\n        if (e.kind == 'custom-element-definition') {\r\n          let properties: IProperty[] = [];\r\n          let declaration = declarations.find(x => x.name == e.declaration.name);\r\n          if (declaration) {\r\n            if (declaration.members) {\r\n              for (let d of declaration.members) {\r\n                if (d.kind == 'field' && d.privacy !== 'private' && d.privacy !== 'protected') {\r\n                  let pType = PropertyType.property;\r\n                  if (declaration.attributes)\r\n                    pType = declaration.attributes.find(x => x.fieldName == d.name) != null ? PropertyType.propertyAndAttribute : PropertyType.property;\r\n                    const p = WebcomponentManifestPropertiesService.manifestClassPropertyTypeToEditorPropertyType(d.type?.text, d.type?.editor);\r\n                  if (d.name)\r\n                    properties.push({ name: d.name, service: this, propertyType: pType, type: p[0], values: p[1], description: d.description });\r\n                }\r\n              }\r\n              this._propertiesList[e.name] = properties;\r\n            }\r\n          } else {\r\n            console.warn('declaration for ' + e.declaration.name + ' not found', manifest);\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  public static manifestClassPropertyTypeToEditorPropertyType(type: string, editor: string): [type: string, values?: string[]] {\r\n    if (editor) {\r\n      if (editor.toLowerCase() === 'color')\r\n        return ['color'];\r\n    }\r\n    if (type) {\r\n      if (type.toLowerCase() === 'boolean')\r\n        return ['boolean'];\r\n      if (type.toLowerCase() === 'number')\r\n        return ['number'];\r\n      if (type.toLowerCase() === 'string')\r\n        return ['string'];\r\n      if (type.startsWith(\"'\") && type.includes(\"|\")) {\r\n        const values = type.split(\"|\").map(x => x.trim()).map(x => x.substring(1, x.length - 1));\r\n        return ['list', values]\r\n      }\r\n    }\r\n    return [type];\r\n  }\r\n\r\n  override isHandledElement(designItem: IDesignItem): boolean {\r\n    return this._propertiesList[designItem.name] != null;\r\n  }\r\n\r\n  override async getProperties(designItem: IDesignItem): Promise<IProperty[] | IPropertyGroup[]> {\r\n    return this._propertiesList[designItem.name];\r\n  }\r\n\r\n  override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\r\n    return this._propertiesList[designItem.name].find(x => x.name == name);\r\n  }\r\n\r\n  override getPropertyTarget(designItem: IDesignItem, property: IProperty): BindingTarget {\r\n    return this._propertiesList[designItem.name].find(x => x.name == property.name).propertyType == PropertyType.attribute ? BindingTarget.attribute : BindingTarget.property\r\n  }\r\n\r\n  override getUnsetValue(designItems: IDesignItem[], property: IProperty) {\r\n    return designItems[0].element[property.propertyName ?? property.name];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/refactorService/BindingsRefactorService.ts",
    "content": "import { IBinding } from \"../../item/IBinding.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IRefactorService } from \"./IRefactorService.js\";\r\nimport { IRefactoring } from \"./IRefactoring.js\";\r\n\r\nexport class BindingsRefactorService implements IRefactorService {\r\n    getRefactorings(designItems: IDesignItem[]): IRefactoring[] {\r\n        let refactorings: (IRefactoring & { shortName?: string })[] = [];\r\n        for (let d of designItems) {\r\n            let bindings = d.serviceContainer.bindingService.getBindings(d);\r\n            if (bindings) {\r\n                for (let b of bindings) {\r\n                    for (let s of b.bindableObjectNames) {\r\n                        if (s.includes(':')) {\r\n                            let nm = s.split(':')[0];\r\n                            let sng = s.substring(nm.length + 1);\r\n                            refactorings.push({ service: this, name: sng, itemType: 'signal', designItem: d, type: 'binding', sourceObject: b, display: b.target + '/' + b.targetName + ' - ' + nm + ':', shortName: nm });\r\n                        } else {\r\n                            refactorings.push({ service: this, name: s, itemType: 'signal', designItem: d, type: 'binding', sourceObject: b, display: b.target + '/' + b.targetName });\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return refactorings;\r\n    }\r\n\r\n    refactor(refactoring: (IRefactoring & { shortName?: string }), oldValue: string, newValue: string) {\r\n        let binding = refactoring.sourceObject as IBinding;\r\n        if (refactoring.shortName)\r\n            binding.bindableObjectNames = binding.bindableObjectNames.map(x => x == refactoring.shortName + ':' + oldValue ? refactoring.shortName + ':' + newValue : x);\r\n        else\r\n            binding.bindableObjectNames = binding.bindableObjectNames.map(x => x == oldValue ? newValue : x);\r\n        refactoring.designItem.serviceContainer.bindingService.setBinding(refactoring.designItem, binding);\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/refactorService/IRefactorService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IRefactoring } from \"./IRefactoring.js\";\r\n\r\nexport interface IRefactorService {\r\n    getRefactorings(designItems: IDesignItem[]): IRefactoring[];\r\n    refactor(refactoring: IRefactoring, oldValue: string, newValue: string);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/refactorService/IRefactoring.ts",
    "content": "import { BindingTarget } from \"../../item/BindingTarget.js\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IRefactorService } from \"./IRefactorService.js\";\r\n\r\nexport interface IRefactoring {\r\n    service: IRefactorService;\r\n    name: string; //wert der \r\n    itemType: string; //for example: text, signal, property, screen, ....\r\n    designItem: IDesignItem;\r\n    type: 'binding' | 'script' | 'content' | 'attribute';\r\n    display?: string;\r\n    sourceObject: any;\r\n    target?: BindingTarget;\r\n    targetName?: string;\r\n\r\n    //usage?: string; //spezieller typ? bspw. css, attribute, script, binding\r\n    //dynamicType?: string; //bein wincc, direkte variable, script, ...\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/refactorService/TextRefactorService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IRefactorService } from \"./IRefactorService.js\";\r\nimport { IRefactoring } from \"./IRefactoring.js\";\r\n\r\nexport class TextRefactorService implements IRefactorService {\r\n    getRefactorings(designItems: IDesignItem[]): IRefactoring[] {\r\n        let refactorings: IRefactoring[] = [];\r\n        for (let d of designItems) {\r\n            if (d.element instanceof d.window.HTMLInputElement || d.element instanceof d.window.HTMLTextAreaElement) {\r\n                if (d.element.value)\r\n                    refactorings.push({ service: this, name: d.element.value, itemType: 'text', designItem: d, type: 'attribute', sourceObject: d, display: 'attribute' + '/' + 'value' });\r\n            }\r\n            if (d.childCount > 0 && d.element.textContent) {\r\n                let onlyTextNodes = true;\r\n                for (const n of d.element.childNodes)\r\n                    if (n.nodeType != 3) {\r\n                        onlyTextNodes = false;\r\n                    }\r\n                if (onlyTextNodes)\r\n                    refactorings.push({ service: this, name: d.element.textContent, itemType: 'text', designItem: d, type: 'content', sourceObject: d, display: 'textContent' });\r\n            }\r\n\r\n        }\r\n        return refactorings;\r\n    }\r\n\r\n    refactor(refactoring: IRefactoring, oldValue: string, newValue: string) {\r\n        if (refactoring.type == 'attribute') {\r\n            refactoring.designItem.setAttribute('value', newValue);\r\n        } else if (refactoring.type == 'content') {\r\n            refactoring.designItem.content = newValue;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/referencesChangedService/IReferencesChangedService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\n\ntype idChange = { designItem: IDesignItem, oldValue: string, type: 'idChanged' };\ntype deleted = { designItem: IDesignItem, type: 'deleted' };\n\nexport interface IReferencesChangedService {\n    notifyReferencesChanged(changes: (idChange | deleted)[]): void;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/renderedDesignItemService/IRenderedDesignItemService.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\n\nexport interface IRenderedDesignItemService {\n  updateRenderedDesignItem(designItem: IDesignItem): void;\n  updateRenderedNode(node: Node): Node;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/renderedDesignItemService/StyleElementRenderedDesignItemService.ts",
    "content": "import { forceActiveAttributeName, forceFocusAttributeName, forceFocusVisibleAttributeName, forceFocusWithinAttributeName, forceHoverAttributeName, forceVisitedAttributeName } from \"../../item/DesignItem.js\";\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\nimport { patchStylesheetSelectorForDesigner } from \"../../helper/DesignerStylesheetPatcher.js\";\nimport { IRenderedDesignItemService } from \"./IRenderedDesignItemService.js\";\n\nexport class StyleElementRenderedDesignItemService implements IRenderedDesignItemService {\n  updateRenderedDesignItem(designItem: IDesignItem): void {\n    const styleDesignItem = this.getStyleDesignItem(designItem);\n    if (!styleDesignItem)\n      return;\n\n    const patchedContent = this.patchStyleText([...styleDesignItem.children()].map(x => x.content).join(''));\n    this.updateStyleElementText(styleDesignItem.element, patchedContent);\n    this.updateDeclarativeShadowStyle(styleDesignItem, patchedContent);\n    styleDesignItem.instanceServiceContainer.designerCanvas.lazyTriggerReparseDocumentStylesheets();\n  }\n\n  updateRenderedNode(node: Node): Node {\n    const win = node.ownerDocument?.defaultView ?? window;\n    if (node instanceof win.HTMLStyleElement)\n      this.updateStyleElementText(node, this.patchStyleText(node.textContent));\n\n    if (node instanceof win.HTMLTemplateElement)\n      this.updateRenderedNode(node.content);\n\n    if ('querySelectorAll' in node) {\n      for (const styleElement of (<ParentNode>node).querySelectorAll('style')) {\n        this.updateStyleElementText(styleElement, this.patchStyleText(styleElement.textContent));\n      }\n    }\n    return node;\n  }\n\n  private getStyleDesignItem(designItem: IDesignItem): IDesignItem {\n    if (designItem?.name == 'style')\n      return designItem;\n    if (designItem?.parent?.name == 'style')\n      return designItem.parent;\n    return null;\n  }\n\n  private updateDeclarativeShadowStyle(styleDesignItem: IDesignItem, patchedContent: string) {\n    const templateDesignItem = this.getDeclarativeShadowTemplateDesignItem(styleDesignItem);\n    if (!templateDesignItem)\n      return;\n\n    const host = templateDesignItem.parent?.element as HTMLElement;\n    if (!host?.shadowRoot)\n      return;\n\n    const sourceStyleElements = [...(<HTMLTemplateElement>templateDesignItem.node).content.querySelectorAll('style')];\n    const sourceIndex = sourceStyleElements.indexOf(<HTMLStyleElement>styleDesignItem.node);\n    const renderedStyleElements = [...host.shadowRoot.querySelectorAll('style')];\n    if (sourceIndex >= 0 && sourceIndex < renderedStyleElements.length)\n      this.updateStyleElementText(renderedStyleElements[sourceIndex], patchedContent);\n    else\n      this.updateRenderedNode(host.shadowRoot);\n  }\n\n  private getDeclarativeShadowTemplateDesignItem(designItem: IDesignItem): IDesignItem {\n    let current = designItem.parent;\n    while (current) {\n      const win = current.node.ownerDocument.defaultView ?? window;\n      if (current.node instanceof win.HTMLTemplateElement && current.getAttribute('shadowrootmode') == 'open')\n        return current;\n      current = current.parent;\n    }\n    return null;\n  }\n\n  private updateStyleElementText(styleElement: Element, text: string) {\n    if (styleElement.textContent !== text)\n      styleElement.textContent = text;\n  }\n\n  private patchStyleText(text: string) {\n    return patchStylesheetSelectorForDesigner(text, {\n      forceHoverAttributeName,\n      forceActiveAttributeName,\n      forceVisitedAttributeName,\n      forceFocusAttributeName,\n      forceFocusWithinAttributeName,\n      forceFocusVisibleAttributeName\n    });\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/searchService/ISearchResult.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\n\n// This class could later wrap, how it was found by the search, for example which property matched, etc. For now it only contains the design item itself.\n\nexport interface ISearchResult {\n    designItem: IDesignItem;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/searchService/ISearchService.ts",
    "content": "import { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\nimport { ISearchResult } from \"./ISearchResult.js\";\n\nexport interface ISearchService {\n  search(designerCanvas: IDesignerCanvas, searchTerm: string, options?: { caseSensitive?: boolean, wholeWord?: boolean, useRegExp?: boolean }): Promise<ISearchResult[]>;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/searchService/SearchService.ts",
    "content": "import { DesignItem } from \"../../item/DesignItem.js\";\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\nimport { ISearchResult } from \"./ISearchResult.js\";\nimport { ISearchService } from \"./ISearchService.js\";\n\nexport class SearchService implements ISearchService {\n    async search(designerCanvas: IDesignerCanvas, searchTerm: string, options?: { caseSensitive?: boolean, wholeWord?: boolean, useRegExp?: boolean }): Promise<ISearchResult[]> {\n        if (searchTerm != \"\") {\n            let selectedElements = designerCanvas.rootDesignItem.querySelectorAll(searchTerm);\n            let searchResults: ISearchResult[] = [];\n            for (let i = 0; i < selectedElements.length; i++) {\n                const designItem = DesignItem.GetDesignItem(selectedElements[i]);\n                if (designItem)\n                    searchResults.push({ designItem });\n            }\n            return searchResults;\n        }\n        return null;\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/selectionService/ISelectionChangedEvent.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\nimport { ISourcePart } from '../sourceMapService/ISourcePart.js';\n\nexport interface ISelectionChangedEvent {\n    oldSelectedElements: IDesignItem[]\n    selectedElements: IDesignItem[],\n    selectedPart?: ISourcePart,\n    oldSelectedPart?: ISourcePart,\n    event?: Event\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/selectionService/ISelectionRefreshEvent.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\nimport { ISourcePart } from '../sourceMapService/ISourcePart.js';\n\nexport interface ISelectionRefreshEvent {\n    selectedElements: IDesignItem[],\n    selectedPart?: ISourcePart,\n    event?: Event\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/selectionService/ISelectionService.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ISelectionChangedEvent } from './ISelectionChangedEvent.js';\r\nimport { ISelectionRefreshEvent } from './ISelectionRefreshEvent.js';\r\nimport { ISourcePart } from '../sourceMapService/ISourcePart.js';\r\n\r\nexport interface ISelectionService {\r\n  primarySelection: IDesignItem;\r\n  selectedElements: IDesignItem[];\r\n  selectedPart: ISourcePart;\r\n\r\n  setSelectedElements(designItems: IDesignItem[], even?: Event, selectedPart?: ISourcePart): void;\r\n  setSelectedPart(sourcePart: ISourcePart, event?: Event): void;\r\n  setSelectionByTextRange(positionStart: number, positionEnd: number);\r\n\r\n  clearSelectedElements(): void;\r\n\r\n  isSelected(designItem: IDesignItem): boolean;\r\n\r\n  readonly onSelectionChanged: TypedEvent<ISelectionChangedEvent>;\r\n  readonly onSelectionRefresh: TypedEvent<ISelectionRefreshEvent>;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/selectionService/SelectionService.ts",
    "content": "import { ISelectionService } from './ISelectionService.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ISelectionChangedEvent } from './ISelectionChangedEvent.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { SelectionChangedAction } from '../undoService/transactionItems/SelectionChangedAction.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { ISelectionRefreshEvent } from './ISelectionRefreshEvent.js';\r\nimport { ISourcePart } from '../sourceMapService/ISourcePart.js';\r\n\r\nfunction findDesignItem(designItem: IDesignItem, position: number): IDesignItem {\r\n  let usedItem = null;\r\n  if (designItem.hasChildren) {\r\n    for (let d of designItem.children()) {\r\n      const nodePosition = designItem.instanceServiceContainer.designItemDocumentPositionService.getPosition(d);\r\n      if (nodePosition) {\r\n        if (nodePosition.start <= position && nodePosition.start + nodePosition.length >= position)\r\n          usedItem = d;\r\n      }\r\n    }\r\n  }\r\n  if (usedItem) {\r\n    return findDesignItem(usedItem, position)\r\n  }\r\n  return designItem;\r\n}\r\n\r\nexport class SelectionService implements ISelectionService {\r\n  primarySelection: IDesignItem;\r\n  selectedElements: IDesignItem[] = [];\r\n  selectedPart: ISourcePart;\r\n  _designerCanvas: IDesignerCanvas;\r\n  _undoSelectionChanges: boolean;\r\n\r\n  constructor(designerCanvas: IDesignerCanvas, undoSelectionChanges: boolean) {\r\n    this._designerCanvas = designerCanvas;\r\n    this._undoSelectionChanges = undoSelectionChanges;\r\n  }\r\n\r\n  setSelectedElements(designItems: IDesignItem[], event?: Event, selectedPart?: ISourcePart) {\r\n    this._setSelectedElements(designItems, event, selectedPart);\r\n  }\r\n\r\n  private _setSelectedElements(designItems: IDesignItem[], event?: Event, selectedPart?: ISourcePart) {\r\n    if (designItems === null || designItems.length === 0)\r\n      designItems = [this._designerCanvas.rootDesignItem];\r\n\r\n    if (this.selectedElements != designItems && !(this.selectedElements.length === 0 && (designItems == null || designItems.length === 0))) {\r\n      if (this.selectedElements?.length === 1 && designItems?.length === 1 && designItems[0] === this.selectedElements[0]) {\r\n        this._setSelectedPart(selectedPart);\r\n        this.onSelectionRefresh.emit({ selectedElements: this.selectedElements, selectedPart: this.selectedPart, event });\r\n        return;\r\n      }\r\n      if (this._undoSelectionChanges) {\r\n        const action = new SelectionChangedAction(this.selectedElements, designItems, this);\r\n        this._designerCanvas.instanceServiceContainer.undoService.execute(action);\r\n      } else {\r\n        this._withoutUndoSetSelectedElements(designItems, event, selectedPart);\r\n      }\r\n    }\r\n  }\r\n\r\n  setSelectedPart(sourcePart: ISourcePart, event?: Event): void {\r\n    if (this._setSelectedPart(sourcePart))\r\n      this.onSelectionRefresh.emit({ selectedElements: this.selectedElements, selectedPart: this.selectedPart, event });\r\n  }\r\n\r\n  private _setSelectedPart(sourcePart: ISourcePart): boolean {\r\n    const oldSelectedPart = this.selectedPart;\r\n    if (oldSelectedPart === sourcePart || (oldSelectedPart?.designItem === sourcePart?.designItem && oldSelectedPart?.key === sourcePart?.key))\r\n      return false;\r\n\r\n    this.selectedPart = sourcePart;\r\n    return true;\r\n  }\r\n\r\n  setSelectionByTextRange(positionStart: number, positionEnd: number) {\r\n    const sourcePart = this._designerCanvas.instanceServiceContainer.designItemDocumentPositionService?.getSourcePartAt(positionStart);\r\n    const item = findDesignItem(this._designerCanvas.rootDesignItem, positionStart);\r\n    if (item) {\r\n      if (this.selectedElements.length != 1 || this.primarySelection != item)\r\n        this._setSelectedElements([item], undefined, sourcePart);\r\n      else\r\n        this.setSelectedPart(sourcePart);\r\n    }\r\n  }\r\n\r\n  _withoutUndoSetSelectedElements(designItems: IDesignItem[], event?: Event, selectedPart?: ISourcePart) {\r\n    let oldSelectedElements = this.selectedElements;\r\n    let oldSelectedPart = this.selectedPart;\r\n    if (!designItems) {\r\n      this.selectedElements = [];\r\n      this.primarySelection = null\r\n      this.selectedPart = null;\r\n    } else {\r\n      let newSelection: IDesignItem[] = []\r\n      for (let d of designItems) {\r\n        if (d && (designItems.length == 1 || d !== d.instanceServiceContainer.designerCanvas.rootDesignItem))\r\n          newSelection.push(d)\r\n      }\r\n      this.selectedElements = newSelection;\r\n      if (newSelection && newSelection.length > 0)\r\n        this.primarySelection = newSelection[0];\r\n      else\r\n        this.primarySelection = null;\r\n      this.selectedPart = selectedPart?.designItem === this.primarySelection ? selectedPart : null;\r\n    }\r\n    this.onSelectionChanged.emit({ selectedElements: this.selectedElements, oldSelectedElements: oldSelectedElements, selectedPart: this.selectedPart, oldSelectedPart, event });\r\n  }\r\n\r\n  clearSelectedElements() {\r\n    this.setSelectedElements([]);\r\n  }\r\n\r\n  isSelected(designItem: IDesignItem) {\r\n    return this.selectedElements.indexOf(designItem) >= 0;\r\n  }\r\n\r\n  readonly onSelectionChanged = new TypedEvent<ISelectionChangedEvent>();\r\n  readonly onSelectionRefresh = new TypedEvent<ISelectionRefreshEvent>();\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/sourceMapService/ISourceMapProvider.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\nimport { IStringPosition } from '../htmlWriterService/IStringPosition.js';\nimport { ISourcePart } from './ISourcePart.js';\n\nexport type SourceMapContextKind = 'attribute' | 'style-declaration' | 'text-node';\n\nexport interface ISourceMapContext {\n  designItem: IDesignItem;\n  sourceKind: SourceMapContextKind;\n  name?: string;\n  value: string;\n  valueTextRange: IStringPosition;\n}\n\nexport interface ISourceMapProvider {\n  canMap(context: ISourceMapContext): boolean;\n  map(context: ISourceMapContext): ISourcePart[];\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/sourceMapService/ISourcePart.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\nimport { IStringPosition } from '../htmlWriterService/IStringPosition.js';\n\nexport type SourcePartKind =\n  | 'attribute'\n  | 'attribute-value'\n  | 'style-declaration'\n  | 'style-value'\n  | 'svg-path-handle'\n  | string;\n\nexport interface ISourcePart<TData = unknown> {\n  designItem: IDesignItem;\n  kind: SourcePartKind;\n  key: string;\n  name?: string;\n  textRange: IStringPosition;\n  data?: TData;\n}\n\nexport interface ISvgPathHandleSourcePartData {\n  attribute: 'd';\n  segmentIndex: number;\n  handleType: 'anchor' | 'cp1' | 'cp2';\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/sourceMapService/SvgPathDataSourceMap.ts",
    "content": "export type SvgPathHandleType = 'anchor' | 'cp1' | 'cp2';\n\nexport interface ISvgPathHandleRange {\n  segmentIndex: number;\n  handleType: SvgPathHandleType;\n  start: number;\n  length: number;\n}\n\ninterface NumberToken {\n  value: number;\n  start: number;\n  end: number;\n}\n\ntype PathToken = NumberToken | { command: string; start: number; end: number };\n\nconst parameterCount: Record<string, number> = {\n  M: 2,\n  L: 2,\n  H: 1,\n  V: 1,\n  C: 6,\n  S: 4,\n  Q: 4,\n  T: 2,\n  A: 7,\n  Z: 0,\n};\n\nfunction isCommandToken(token: PathToken): token is { command: string; start: number; end: number } {\n  return 'command' in token;\n}\n\nfunction isNumberToken(token: PathToken): token is NumberToken {\n  return 'value' in token;\n}\n\nfunction tokenizePathData(value: string): PathToken[] {\n  const tokens: PathToken[] = [];\n  const tokenRegex = /[AaCcHhLlMmQqSsTtVvZz]|[-+]?(?:(?:\\d*\\.\\d+)|(?:\\d+\\.?))(?:[eE][-+]?\\d+)?/g;\n  let match: RegExpExecArray;\n\n  while ((match = tokenRegex.exec(value))) {\n    const token = match[0];\n    if (/^[AaCcHhLlMmQqSsTtVvZz]$/.test(token))\n      tokens.push({ command: token, start: match.index, end: match.index + token.length });\n    else\n      tokens.push({ value: Number(token), start: match.index, end: match.index + token.length });\n  }\n\n  return tokens;\n}\n\nfunction getRange(numbers: NumberToken[], indexes: number[]): { start: number; length: number } {\n  const selected = indexes.map(index => numbers[index]).filter(x => x);\n  const start = Math.min(...selected.map(x => x.start));\n  const end = Math.max(...selected.map(x => x.end));\n  return { start, length: end - start };\n}\n\nfunction addHandleRange(ranges: ISvgPathHandleRange[], segmentIndex: number, handleType: SvgPathHandleType, numbers: NumberToken[], indexes: number[]) {\n  const range = getRange(numbers, indexes);\n  ranges.push({ segmentIndex, handleType, start: range.start, length: range.length });\n}\n\nfunction addSegmentRanges(ranges: ISvgPathHandleRange[], command: string, segmentIndex: number, numbers: NumberToken[]) {\n  switch (command.toUpperCase()) {\n    case 'M':\n    case 'L':\n    case 'T':\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [0, 1]);\n      break;\n    case 'H':\n    case 'V':\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [0]);\n      break;\n    case 'C':\n      addHandleRange(ranges, segmentIndex, 'cp1', numbers, [0, 1]);\n      addHandleRange(ranges, segmentIndex, 'cp2', numbers, [2, 3]);\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [4, 5]);\n      break;\n    case 'S':\n      addHandleRange(ranges, segmentIndex, 'cp2', numbers, [0, 1]);\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [2, 3]);\n      break;\n    case 'Q':\n      addHandleRange(ranges, segmentIndex, 'cp1', numbers, [0, 1]);\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [2, 3]);\n      break;\n    case 'A':\n      addHandleRange(ranges, segmentIndex, 'anchor', numbers, [5, 6]);\n      break;\n  }\n}\n\nexport function parseSvgPathDataSourceMap(value: string): ISvgPathHandleRange[] {\n  const tokens = tokenizePathData(value);\n  const ranges: ISvgPathHandleRange[] = [];\n  let index = 0;\n  let command: string = null;\n  let segmentIndex = 0;\n\n  while (index < tokens.length) {\n    const token = tokens[index];\n    if (isCommandToken(token)) {\n      command = token.command;\n      index++;\n\n      if (command.toUpperCase() === 'Z') {\n        segmentIndex++;\n        continue;\n      }\n    } else if (!command) {\n      index++;\n      continue;\n    }\n\n    let effectiveCommand = command;\n    let parameters = parameterCount[effectiveCommand.toUpperCase()];\n    if (parameters == null || parameters === 0)\n      continue;\n\n    let firstMoveSegment = effectiveCommand.toUpperCase() === 'M';\n    while (index < tokens.length && !isCommandToken(tokens[index])) {\n      if (firstMoveSegment) {\n        effectiveCommand = command;\n        firstMoveSegment = false;\n      } else if (command.toUpperCase() === 'M') {\n        effectiveCommand = command === command.toLowerCase() ? 'l' : 'L';\n      }\n\n      parameters = parameterCount[effectiveCommand.toUpperCase()];\n      const numbers: NumberToken[] = [];\n      for (let parameterIndex = 0; parameterIndex < parameters && index < tokens.length; parameterIndex++, index++) {\n        const numberToken = tokens[index];\n        if (!isNumberToken(numberToken))\n          break;\n        numbers.push(numberToken);\n      }\n\n      if (numbers.length !== parameters)\n        break;\n\n      addSegmentRanges(ranges, effectiveCommand, segmentIndex, numbers);\n      segmentIndex++;\n    }\n  }\n\n  return ranges;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/sourceMapService/SvgPathSourceMapProvider.ts",
    "content": "import { ISourceMapContext, ISourceMapProvider } from './ISourceMapProvider.js';\nimport { ISourcePart, ISvgPathHandleSourcePartData } from './ISourcePart.js';\nimport { parseSvgPathDataSourceMap } from './SvgPathDataSourceMap.js';\n\nexport function createSvgPathHandleSourcePartKey(segmentIndex: number, handleType: ISvgPathHandleSourcePartData['handleType']) {\n  return `attribute:d/svg-path/segment:${segmentIndex}/handle:${handleType}`;\n}\n\nexport class SvgPathSourceMapProvider implements ISourceMapProvider {\n  canMap(context: ISourceMapContext): boolean {\n    return context.sourceKind === 'attribute'\n      && context.name === 'd'\n      && context.designItem.element instanceof context.designItem.window.SVGPathElement;\n  }\n\n  map(context: ISourceMapContext): ISourcePart<ISvgPathHandleSourcePartData>[] {\n    return parseSvgPathDataSourceMap(context.value).map(handleRange => ({\n      designItem: context.designItem,\n      kind: 'svg-path-handle',\n      key: createSvgPathHandleSourcePartKey(handleRange.segmentIndex, handleRange.handleType),\n      name: 'd',\n      textRange: {\n        start: context.valueTextRange.start + handleRange.start,\n        length: handleRange.length\n      },\n      data: {\n        attribute: 'd',\n        segmentIndex: handleRange.segmentIndex,\n        handleType: handleRange.handleType\n      }\n    }));\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/stylesheetService/AbstractStylesheetService.ts",
    "content": "import { TypedEvent } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IDocumentStylesheet, IStyleDeclaration, IStyleRule, IStylesheet, IStylesheetService } from './IStylesheetService.js';\r\nimport { InstanceServiceContainer } from \"../InstanceServiceContainer.js\";\r\nimport { IDesignerCanvas } from \"../../widgets/designerView/IDesignerCanvas.js\";\r\nimport { forceActiveAttributeName, forceFocusAttributeName, forceFocusVisibleAttributeName, forceFocusWithinAttributeName, forceHoverAttributeName, forceVisitedAttributeName } from \"../../item/DesignItem.js\";\r\nimport { Specificity, calculateSpecificity } from \"./SpecificityCalculator.js\";\nimport { patchStylesheetSelectorForDesigner } from \"../../helper/DesignerStylesheetPatcher.js\";\n\r\nexport abstract class AbstractStylesheetService implements IStylesheetService {\r\n    protected _stylesheets = new Map<string, { stylesheet: IStylesheet, ast: any }>();\r\n    protected _documentStylesheets = new Map<string, { stylesheet: IDocumentStylesheet, ast: any }>();\r\n    protected _allStylesheets = new Map<string, { stylesheet: IStylesheet | IDocumentStylesheet, ast: any }>();\r\n\r\n    protected _instanceServiceContainer: InstanceServiceContainer;\r\n\r\n    constructor(designerCanvas: IDesignerCanvas) {\r\n        this._instanceServiceContainer = designerCanvas.instanceServiceContainer;\r\n    }\r\n\r\n    abstract getRules(rule: string): IStyleRule[];\r\n    abstract addRule(stylesheet: IStylesheet, rule: string): Promise<IStyleRule>;\r\n\r\n    async setStylesheets(stylesheets: IStylesheet[]): Promise<void> {\r\n        await this.internalSetStylesheets(stylesheets, this._stylesheets);\r\n    }\r\n\r\n    async setDocumentStylesheets(stylesheets: IDocumentStylesheet[]): Promise<void> {\r\n        await this.internalSetStylesheets(stylesheets, this._documentStylesheets);\r\n    }\r\n\r\n    async internalSetStylesheets(stylesheets: IStylesheet[], targetMap: Map<string, { stylesheet: IStylesheet, ast: any }>): Promise<void> {\r\n        if (targetMap != null && stylesheets != null && targetMap.size == stylesheets.length && stylesheets.every(x => targetMap.has(x.name))) {\r\n            for (let stylesheet of stylesheets) {\r\n                const old = targetMap.get(stylesheet.name);\r\n                if (old.stylesheet.content != stylesheet.content) {\r\n                    try {\r\n                        targetMap.set(stylesheet.name, {\r\n                            stylesheet: stylesheet,\r\n                            ast: await this.internalParse(stylesheet.content)\r\n                        });\r\n                    }\r\n                    catch (err) {\r\n                        console.warn(\"error parsing stylesheet\", stylesheet, err)\r\n                    }\r\n                    if (targetMap == this._stylesheets)\r\n                        this.stylesheetChanged.emit({ name: stylesheet.name, newStyle: stylesheet.content, oldStyle: old.stylesheet.content, changeSource: 'extern' });\r\n                }\r\n            }\r\n        } else if (stylesheets != null) {\r\n            targetMap.clear();\r\n            for (let stylesheet of stylesheets) {\r\n                let ast = null;\r\n                try {\r\n                    ast = await this.internalParse(stylesheet.content)\r\n                }\r\n                catch (err) {\r\n                    console.warn(\"error parsing stylesheet\", stylesheet, err)\r\n                }\r\n                targetMap.set(stylesheet.name, {\r\n                    stylesheet: stylesheet,\r\n                    ast: ast\r\n                });\r\n            }\r\n            if (targetMap == this._stylesheets)\r\n                this.stylesheetsChanged.emit();\r\n        } else {\r\n            targetMap.clear();\r\n        }\r\n\r\n        this._allStylesheets.clear();\r\n        for (let s of this._documentStylesheets) {\r\n            this._allStylesheets.set(s[0], s[1]);\r\n        }\r\n        for (let s of this._stylesheets) {\r\n            this._allStylesheets.set(s[0], s[1]);\r\n        }\r\n    }\r\n\r\n    protected async internalReparseStylesheet(name: string) {\r\n        let lst = this._allStylesheets;\r\n        if (this._documentStylesheets.has(name))\r\n            lst = this._documentStylesheets;\r\n        if (this._stylesheets.has(name))\r\n            lst = this._stylesheets;\r\n\r\n        const ss = lst.get(name);\r\n        let ast = null;\r\n        try {\r\n            ast = await this.internalParse(ss.stylesheet.content)\r\n        }\r\n        catch (err) {\r\n            console.warn(\"error parsing stylesheet\", name, err)\r\n        }\r\n        const v = {\r\n            stylesheet: ss.stylesheet,\r\n            ast: ast\r\n        }\r\n        this._stylesheets.set(name, v);\r\n        this._allStylesheets.set(name, v);\r\n    }\r\n\r\n    protected abstract internalParse(style: string): Promise<any>;\r\n\r\n    //TODO: rename to externalStylesheets\r\n    getStylesheets(): IStylesheet[] {\r\n        let stylesheets: IStylesheet[] = [];\r\n        for (let item of this._stylesheets) {\r\n            stylesheets.push(item[1].stylesheet);\r\n        };\r\n        /*for (let item of this._documentStylesheets) {\r\n            stylesheets.push(item[1].stylesheet);\r\n        };*/\r\n        return stylesheets;\r\n    }\r\n\r\n    abstract getAppliedRules(designItem: IDesignItem): IStyleRule[]\r\n    abstract getDeclarations(designItem: IDesignItem, styleName: string): IStyleDeclaration[]\r\n\r\n\r\n    getDeclarationsSortedBySpecificity(designItem: IDesignItem, styleName: string): IStyleDeclaration[] {\r\n        const decls = this.getDeclarations(designItem, styleName);\r\n        decls.reverse().sort((a, b) => {\r\n            if (a.parent.specificity == null)\r\n                return -1;\r\n            if (b.parent.specificity == null)\r\n                return 1;\r\n            if (a.parent.specificity.A > b.parent.specificity.A)\r\n                return -1;\r\n            if (a.parent.specificity.A === b.parent.specificity.A && a.parent.specificity.B > b.parent.specificity.B)\r\n                return -1;\r\n            if (a.parent.specificity.A === b.parent.specificity.A && a.parent.specificity.B === b.parent.specificity.B && a.parent.specificity.C > b.parent.specificity.C)\r\n                return -1;\r\n            if (a.parent.specificity.A === b.parent.specificity.A && a.parent.specificity.B === b.parent.specificity.B && a.parent.specificity.C === b.parent.specificity.C)\r\n                return 0;\r\n            return 1;\r\n        });\r\n        return decls;\r\n    }\r\n\r\n\r\n    public updateDeclarationValue(declaration: IStyleDeclaration, value: string, important: boolean) {\r\n        this.updateDeclarationValueWithoutUndo(declaration, value, important);\r\n    }\r\n\r\n    abstract insertDeclarationIntoRule(rule: IStyleRule, property: string, value: string, important: boolean): boolean\r\n    abstract removeDeclarationFromRule(rule: IStyleRule, property: string): boolean;\r\n    abstract updateCompleteStylesheet(name: string, newStyle: string);\r\n    abstract updateCompleteStylesheetWithoutUndo(name: string, newStyle: string);\r\n\r\n    public abstract updateDeclarationValueWithoutUndo(declaration: IStyleDeclaration, value: string, important: boolean)\r\n\r\n    public stylesheetChanged = new TypedEvent<{ name: string, newStyle: string, oldStyle: string, changeSource: 'extern' | 'styleupdate' | 'undo' }>();\r\n    public stylesheetsChanged: TypedEvent<void> = new TypedEvent<void>();\r\n\r\n    public static patchStylesheetSelectorForDesigner(text: string) {\n        return patchStylesheetSelectorForDesigner(text, {\n            forceHoverAttributeName,\n            forceActiveAttributeName,\n            forceVisitedAttributeName,\n            forceFocusAttributeName,\n            forceFocusWithinAttributeName,\n            forceFocusVisibleAttributeName\n        });\n    }\n\r\n    protected elementMatchesASelector(designItem: IDesignItem, selectors: string[]): false | Specificity {\r\n        let s: Specificity = null;\n        for (let selector of selectors) {\n            try {\n                const patchedSelector = AbstractStylesheetService.patchStylesheetSelectorForDesigner(selector);\n                if (designItem && patchedSelector === ':host' && designItem.isRootItem) {\n                    let spec = calculateSpecificity(selector);\r\n                    if (s === null || spec.A > s.A || (spec.A === s.A && (spec.B > s.B || (spec.B === s.B && spec.C > s.C))))\r\n                        s = spec;\r\n                } else if (!designItem || designItem.element.matches(patchedSelector)) {\n                    let spec = calculateSpecificity(selector);\r\n                    if (s === null || spec.A > s.A || (spec.A === s.A && (spec.B > s.B || (spec.B === s.B && spec.C > s.C))))\r\n                        s = spec;\r\n                }\r\n            }\r\n            catch (err) {\r\n                console.warn(\"invalid selector: \", selector, \"patched: \" + AbstractStylesheetService.patchStylesheetSelectorForDesigner(selector));\r\n            }\r\n        }\r\n        return s === null ? false : s;\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/stylesheetService/IStylesheetService.ts",
    "content": "import { TypedEvent } from \"@node-projects/base-custom-webcomponent\";\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\nimport { Specificity } from \"./SpecificityCalculator.js\";\n\nexport interface IStyleRule {\n    selector: string;\n    declarations: IStyleDeclaration[];\n    specificity: Specificity;\n    stylesheetName?: string;\n    stylesheet: IStylesheet;\n}\n\nexport interface IStyleDeclaration {\n    name: string;\n    value: string;\n    important: boolean;\n    parent: IStyleRule;\n    stylesheet?: IStylesheet;\n}\n\nexport interface IStylesheet {\n    content: string;\n    name: string;\n    readOnly?: boolean;\n}\n\nexport interface IDocumentStylesheet extends IStylesheet {\n    designItem: IDesignItem;\n}\n\nexport interface IStylesheetService {\n    setStylesheets(stylesheets: IStylesheet[]): Promise<void>;\n    getStylesheets(): IStylesheet[];\n\n    getRules(selector: string): IStyleRule[];\n\n    setDocumentStylesheets(stylesheets: IDocumentStylesheet[]): Promise<void>;\n\n    getAppliedRules(designItem: IDesignItem): IStyleRule[];\n    getDeclarations(designItem: IDesignItem, styleName: string): IStyleDeclaration[];\n    getDeclarationsSortedBySpecificity(designItem: IDesignItem, styleName: string): IStyleDeclaration[];\n\n    updateDeclarationValue(declaration: IStyleDeclaration, value: string, important: boolean);\n    updateDeclarationValueWithoutUndo(declaration: IStyleDeclaration, value: string, important: boolean)\n    insertDeclarationIntoRule(rule: IStyleRule, property: string, value: string, important: boolean): boolean;\n    removeDeclarationFromRule(rule: IStyleRule, property: string): boolean;\n    updateCompleteStylesheet(name: string, newStyle: string): Promise<void>;\n    updateCompleteStylesheetWithoutUndo(name: string, newStyle: string): Promise<void>;\n    addRule(stylesheet: IStylesheet, selector: string): Promise<IStyleRule>;\n\n    stylesheetChanged: TypedEvent<{ name: string, newStyle: string, oldStyle: string, changeSource: 'extern' | 'styleupdate' | 'undo' }>;\n    stylesheetsChanged: TypedEvent<void>;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/stylesheetService/SpecificityCalculator.ts",
    "content": "export type Level = \"A\" | \"B\" | \"C\";\nexport type Specificity = { A: number; B: number; C: number };\n\n// Char codes used throughout\nconst CH_HASH = 35;        // #\nconst CH_DOT = 46;         // .\nconst CH_COLON = 58;       // :\nconst CH_LBRACKET = 91;    // [\nconst CH_RBRACKET = 93;    // ]\nconst CH_LPAREN = 40;      // (\nconst CH_RPAREN = 41;      // )\nconst CH_COMMA = 44;       // ,\nconst CH_BACKSLASH = 92;   // \\\nconst CH_STAR = 42;        // *\nconst CH_PIPE = 124;       // |\nconst CH_SPACE = 32;       // ' '\nconst CH_GT = 62;          // >\nconst CH_PLUS = 43;        // +\nconst CH_TILDE = 126;      // ~\nconst CH_SQUOTE = 39;      // '\nconst CH_DQUOTE = 34;      // \"\nconst CH_UNDERSCORE = 95;  // _\nconst CH_DASH = 45;        // -\n\nexport function calculateSpecificity(selector: string): Specificity {\n    const spec: Specificity = { A: 0, B: 0, C: 0 };\n\n    // Fast path: simple selectors without pseudo-classes, attributes, functions, commas, or escapes\n    if (!needsFullParse(selector)) {\n        calcSimple(selector, spec);\n        return spec;\n    }\n\n    parseSelectorList(selector, 0, spec);\n    return spec;\n}\n\nfunction needsFullParse(selector: string): boolean {\n    for (let i = 0; i < selector.length; i++) {\n        const c = selector.charCodeAt(i);\n        if (c === CH_COLON || c === CH_LBRACKET || c === CH_LPAREN || c === CH_COMMA || c === CH_BACKSLASH)\n            return true;\n    }\n    return false;\n}\n\nfunction calcSimple(selector: string, spec: Specificity): void {\n    const len = selector.length;\n    let i = 0;\n    while (i < len) {\n        const c = selector.charCodeAt(i);\n        // Column combinator ||\n        if (c === CH_PIPE && i + 1 < len && selector.charCodeAt(i + 1) === CH_PIPE) { i += 2; continue; }\n        if (c === CH_SPACE || c === CH_GT || c === CH_PLUS || c === CH_TILDE) { i++; continue; }\n        if (c === CH_HASH) {\n            spec.A++;\n            i++;\n            while (i < len && isIdentCC(selector.charCodeAt(i))) i++;\n            continue;\n        }\n        if (c === CH_DOT) {\n            spec.B++;\n            i++;\n            while (i < len && isIdentCC(selector.charCodeAt(i))) i++;\n            continue;\n        }\n        if (c === CH_STAR) { i++; continue; }\n        if (isIdentStartCC(c) || c === CH_PIPE) {\n            while (i < len) {\n                const cc = selector.charCodeAt(i);\n                if (isIdentCC(cc) || cc === CH_PIPE) i++;\n                else break;\n            }\n            spec.C++;\n            continue;\n        }\n        i++;\n    }\n}\n\n/* ---- Full parser (used when selector contains :, [, (, \\, or ,) ---- */\n\nfunction parseSelectorList(input: string, start: number, spec: Specificity): number {\n    let i = start;\n    let tA = 0, tB = 0, tC = 0;\n\n    while (i < input.length) {\n        tA = tB = tC = 0;\n        i = parseSelector(input, i, spec, true);\n        // parseSelector wrote into spec; grab those values as temp then reset\n        tA = spec.A; tB = spec.B; tC = spec.C;\n\n        // On the very first iteration we just keep the values.\n        // On subsequent iterations we pick the most specific (lexicographic).\n        // To avoid extra bookkeeping we always overwrite spec and compare below.\n        if (input.charCodeAt(i) === CH_COMMA) {\n            i++;\n            // Need to parse more selectors — save best so far\n            let bestA = tA, bestB = tB, bestC = tC;\n            while (i < input.length) {\n                spec.A = spec.B = spec.C = 0;\n                i = parseSelector(input, i, spec, true);\n                if (spec.A > bestA || (spec.A === bestA && (spec.B > bestB || (spec.B === bestB && spec.C > bestC)))) {\n                    bestA = spec.A; bestB = spec.B; bestC = spec.C;\n                }\n                if (input.charCodeAt(i) === CH_COMMA) { i++; continue; }\n                break;\n            }\n            spec.A = bestA; spec.B = bestB; spec.C = bestC;\n        }\n        break;\n    }\n\n    return i;\n}\n\n// When called from parseSelectorList with direct=true, writes directly into spec (avoids temp object).\n// When called recursively (direct=false), the caller manages its own temp.\nfunction parseSelector(input: string, start: number, spec: Specificity, direct: boolean): number {\n    let i = start;\n    if (direct) { spec.A = spec.B = spec.C = 0; }\n\n    while (i < input.length) {\n        const c = input.charCodeAt(i);\n        if (c === CH_COMMA || c === CH_RPAREN) break;\n\n        // Column combinator ||\n        if (c === CH_PIPE && input.charCodeAt(i + 1) === CH_PIPE) { i += 2; continue; }\n\n        if (c === CH_SPACE || c === CH_GT || c === CH_PLUS || c === CH_TILDE) { i++; continue; }\n        if (c === CH_HASH) { i = readIdent(input, i + 1); spec.A++; continue; }\n        if (c === CH_DOT) { i = readIdent(input, i + 1); spec.B++; continue; }\n        if (c === CH_LBRACKET) { i = readBalanced(input, i, CH_LBRACKET, CH_RBRACKET); spec.B++; continue; }\n\n        if (c === CH_COLON) {\n            if (input.charCodeAt(i + 1) === CH_COLON) {\n                i += 2;\n                i = readIdent(input, i);\n                if (input.charCodeAt(i) === CH_LPAREN) i = readBalanced(input, i, CH_LPAREN, CH_RPAREN);\n                spec.C++;\n                continue;\n            }\n\n            const nameStart = i + 1;\n            const nameEnd = readIdent(input, nameStart);\n            i = nameEnd;\n\n            // Legacy single-colon pseudo-elements — count as C (pseudo-element), not B\n            if (isLegacyPseudoElement(input, nameStart, nameEnd - nameStart)) {\n                spec.C++;\n                continue;\n            }\n\n            if (input.charCodeAt(i) === CH_LPAREN) {\n                const innerStart = i + 1;\n                const innerEnd = readBalanced(input, i, CH_LPAREN, CH_RPAREN);\n\n                // Identify the pseudo-class by comparing chars directly (avoids substring allocation)\n                const nameLen = nameEnd - nameStart;\n                const pcKind = classifyPseudo(input, nameStart, nameLen);\n\n                switch (pcKind) {\n                    case PC_WHERE:\n                        i = innerEnd; continue;\n\n                    case PC_IS:\n                    case PC_NOT:\n                    case PC_HAS:\n                    case PC_MATCHES:\n                    case PC_WEBKIT_ANY:\n                    case PC_MOZ_ANY: {\n                        let bestA = 0, bestB = 0, bestC = 0;\n                        let j = innerStart;\n                        const limit = innerEnd - 1;\n                        while (j < limit) {\n                            const saved = { A: 0, B: 0, C: 0 };\n                            j = parseSelectorInner(input, j, saved);\n                            if (saved.A > bestA || (saved.A === bestA && (saved.B > bestB || (saved.B === bestB && saved.C > bestC)))) {\n                                bestA = saved.A; bestB = saved.B; bestC = saved.C;\n                            }\n                            if (input.charCodeAt(j) === CH_COMMA) j++;\n                            else break;\n                        }\n                        spec.A += bestA;\n                        spec.B += bestB;\n                        spec.C += bestC;\n                        i = innerEnd;\n                        continue;\n                    }\n\n                    case PC_SLOTTED: {\n                        const inner: Specificity = { A: 0, B: 0, C: 0 };\n                        parseSelector(input, innerStart, inner, false);\n                        spec.A += inner.A;\n                        spec.B += inner.B;\n                        spec.C += inner.C;\n                        i = innerEnd;\n                        continue;\n                    }\n\n                    case PC_HOST:\n                    case PC_HOST_CTX: {\n                        spec.B++;\n                        const inner: Specificity = { A: 0, B: 0, C: 0 };\n                        parseSelector(input, innerStart, inner, false);\n                        spec.A += inner.A;\n                        spec.B += inner.B;\n                        spec.C += inner.C;\n                        i = innerEnd;\n                        continue;\n                    }\n\n                    case PC_NTH_CHILD:\n                    case PC_NTH_LAST: {\n                        spec.B++;\n                        const limit = innerEnd - 1;\n                        const ofIndex = findOfKeyword(input, innerStart, limit);\n                        if (ofIndex !== -1) {\n                            let afterOf = ofIndex + 2;\n                            while (afterOf < limit && isWhitespaceCC(input.charCodeAt(afterOf))) afterOf++;\n\n                            let bestA = 0, bestB = 0, bestC = 0;\n                            let j = afterOf;\n                            while (j < limit) {\n                                const ts: Specificity = { A: 0, B: 0, C: 0 };\n                                j = parseSelector(input, j, ts, false);\n                                if (ts.A > bestA || (ts.A === bestA && (ts.B > bestB || (ts.B === bestB && ts.C > bestC)))) {\n                                    bestA = ts.A; bestB = ts.B; bestC = ts.C;\n                                }\n                                if (input.charCodeAt(j) === CH_COMMA) j++;\n                                else break;\n                            }\n                            spec.A += bestA;\n                            spec.B += bestB;\n                            spec.C += bestC;\n                        }\n                        i = innerEnd;\n                        continue;\n                    }\n\n                    default:\n                        spec.B++;\n                        i = innerEnd;\n                        continue;\n                }\n            } else {\n                spec.B++;\n                continue;\n            }\n        }\n\n        if (c === CH_STAR) { i++; continue; }\n        if (isIdentStartCC(c) || c === CH_PIPE) {\n            while (i < input.length && (isIdentCC(input.charCodeAt(i)) || input.charCodeAt(i) === CH_PIPE)) i++;\n            spec.C++;\n            continue;\n        }\n\n        i++;\n    }\n\n    return i;\n}\n\n// Parse a single selector within a functional pseudo-class argument (handles commas at the caller level)\nfunction parseSelectorInner(input: string, start: number, spec: Specificity): number {\n    return parseSelector(input, start, spec, false);\n}\n\n/* ---- Pseudo-class classification (avoids substring + switch on string) ---- */\n\nconst PC_OTHER = 0;\nconst PC_WHERE = 1;\nconst PC_IS = 2;\nconst PC_NOT = 3;\nconst PC_HAS = 4;\nconst PC_SLOTTED = 5;\nconst PC_HOST = 6;\nconst PC_HOST_CTX = 7;\nconst PC_NTH_CHILD = 8;\nconst PC_NTH_LAST = 9;\nconst PC_MATCHES = 10;\nconst PC_WEBKIT_ANY = 11;\nconst PC_MOZ_ANY = 12;\n\nfunction classifyPseudo(input: string, start: number, len: number): number {\n    // Most common first\n    if (len === 5 && input.charCodeAt(start) === 119) { // 'w'here\n        if (input.charCodeAt(start + 1) === 104 && input.charCodeAt(start + 2) === 101 &&\n            input.charCodeAt(start + 3) === 114 && input.charCodeAt(start + 4) === 101) return PC_WHERE;\n    }\n    if (len === 3 && input.charCodeAt(start) === 110) { // 'n'ot\n        if (input.charCodeAt(start + 1) === 111 && input.charCodeAt(start + 2) === 116) return PC_NOT;\n    }\n    if (len === 2 && input.charCodeAt(start) === 105) { // 'i's\n        if (input.charCodeAt(start + 1) === 115) return PC_IS;\n    }\n    if (len === 3 && input.charCodeAt(start) === 104) { // 'h'as / 'h'ost\n        if (input.charCodeAt(start + 1) === 97 && input.charCodeAt(start + 2) === 115) return PC_HAS;\n    }\n    if (len === 4 && input.charCodeAt(start) === 104) { // 'h'ost\n        if (input.charCodeAt(start + 1) === 111 && input.charCodeAt(start + 2) === 115 &&\n            input.charCodeAt(start + 3) === 116) return PC_HOST;\n    }\n    if (len === 12 && input.charCodeAt(start) === 104) { // host-context\n        if (input.charCodeAt(start + 4) === CH_DASH && input.charCodeAt(start + 5) === 99) {\n            // Quick check first+last chars, then full\n            if (input.substring(start, start + len) === 'host-context') return PC_HOST_CTX;\n        }\n    }\n    if (len === 7 && input.charCodeAt(start) === 115) { // slotted\n        if (input.substring(start, start + 7) === 'slotted') return PC_SLOTTED;\n    }\n    if (len === 9 && input.charCodeAt(start) === 110) { // nth-child\n        if (input.substring(start, start + 9) === 'nth-child') return PC_NTH_CHILD;\n    }\n    if (len === 14 && input.charCodeAt(start) === 110) { // nth-last-child\n        if (input.substring(start, start + 14) === 'nth-last-child') return PC_NTH_LAST;\n    }\n    if (len === 7 && input.charCodeAt(start) === 109) { // matches\n        if (input.substring(start, start + 7) === 'matches') return PC_MATCHES;\n    }\n    if (len === 11 && input.charCodeAt(start) === CH_DASH) { // -webkit-any\n        if (input.substring(start, start + 11) === '-webkit-any') return PC_WEBKIT_ANY;\n    }\n    if (len === 8 && input.charCodeAt(start) === CH_DASH) { // -moz-any\n        if (input.substring(start, start + 8) === '-moz-any') return PC_MOZ_ANY;\n    }\n    return PC_OTHER;\n}\n\n// Legacy single-colon pseudo-elements that should count as C, not B\nfunction isLegacyPseudoElement(input: string, start: number, len: number): boolean {\n    if (len === 6) { // before / after\n        const c0 = input.charCodeAt(start);\n        if (c0 === 98) return input.substring(start, start + 6) === 'before'; // 'b'efore\n        if (c0 === 97) return input.substring(start, start + 6) === 'after';  // 'a'fter (5 chars, won't match — handled below)\n    }\n    if (len === 5 && input.charCodeAt(start) === 97) { // after\n        return input.substring(start, start + 5) === 'after';\n    }\n    if (len === 10 && input.charCodeAt(start) === 102) { // first-line\n        return input.substring(start, start + 10) === 'first-line';\n    }\n    if (len === 12 && input.charCodeAt(start) === 102) { // first-letter\n        return input.substring(start, start + 12) === 'first-letter';\n    }\n    return false;\n}\n\n/* ---- Character classification (charCode-based, no string allocation) ---- */\n\nfunction isIdentStartCC(code: number): boolean {\n    return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || code === CH_UNDERSCORE || code === CH_DASH || code === CH_BACKSLASH;\n}\n\nfunction isIdentCC(code: number): boolean {\n    return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) || (code >= 48 && code <= 57) || code === CH_UNDERSCORE || code === CH_DASH || code === CH_BACKSLASH;\n}\n\nfunction isWhitespaceCC(code: number): boolean {\n    return code === 32 || code === 9 || code === 10 || code === 13 || code === 12;\n}\n\nfunction readIdent(input: string, start: number): number {\n    let i = start;\n    while (i < input.length) {\n        const cc = input.charCodeAt(i);\n        if (cc === CH_BACKSLASH) { i += 2; continue; }\n        if (!isIdentCC(cc)) break;\n        i++;\n    }\n    return i;\n}\n\nfunction readBalanced(input: string, start: number, open: number, close: number): number {\n    let depth = 0, inQuote = 0, i = start;\n    while (i < input.length) {\n        const c = input.charCodeAt(i);\n        if (inQuote) {\n            if (c === CH_BACKSLASH) { i += 2; continue; }\n            if (c === inQuote) inQuote = 0;\n            i++;\n            continue;\n        }\n        if (c === CH_DQUOTE || c === CH_SQUOTE) { inQuote = c; i++; continue; }\n        if (c === open) depth++;\n        else if (c === close) { depth--; if (depth === 0) return i + 1; }\n        i++;\n    }\n    return i;\n}\n\nfunction findOfKeyword(input: string, start: number, end: number): number {\n    let i = start;\n    while (i <= end - 1) {\n        while (i <= end - 1 && isWhitespaceCC(input.charCodeAt(i))) i++;\n        if (input.charCodeAt(i) === 111 && input.charCodeAt(i + 1) === 102 && // 'o','f'\n            (i + 2 > end - 1 || isWhitespaceCC(input.charCodeAt(i + 2)) || input.charCodeAt(i + 2) === CH_LPAREN))\n            return i;\n        i++;\n    }\n    return -1;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/treeStructureService/ITreeStructureChangedEvent.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\nexport interface ITreeStructureChangedEvent {\r\n  action: 'add' | 'move' | 'remove' | 'replace' | 'reset'\r\n  newItems?: IDesignItem[]\r\n  newStartingIndex?: number\r\n  oldItems?: IDesignItem[]\r\n  oldStartingIndex?: number\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/treeStructureService/ITreeStructureService.ts",
    "content": "export interface ITreeStructureService {\r\n\r\n}\r\n\r\n//maybe this service caches a dictionary designitem -> treeelement?, filled by treeView control?"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/ChangeGroup.ts",
    "content": "import { ITransactionItem } from './ITransactionItem.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { UndoChangeSource } from './IUndoChangeEvent.js';\r\nimport { IContentChanged } from '../InstanceServiceContainer.js';\r\n\r\nexport class ChangeGroup implements ITransactionItem {\r\n\r\n  redoBranches?: ITransactionItem[][];\r\n  source: UndoChangeSource;\r\n  private _contentChanges: IContentChanged[] = [];\r\n\r\n  title: string;\r\n  get affectedItems(): IDesignItem[] {\r\n    let s = new Set<IDesignItem>();\r\n    for (let u of this.undoStack)\r\n      for (let i of u.affectedItems)\r\n        s.add(i);\r\n    for (let u of this.redoStack)\r\n      for (let i of u.affectedItems)\r\n        s.add(i);\r\n    return [...s.values()]\r\n  }\r\n  private commitHandler: (transactionItem: ChangeGroup) => void;\r\n  private abortHandler: (transactionItem: ChangeGroup) => void;\r\n\r\n  constructor(title: string, commitHandler: (transactionItem: ChangeGroup) => void, abortHandler: (transactionItem: ChangeGroup) => void, source: UndoChangeSource = 'local') {\r\n    this.title = title;\r\n    this.commitHandler = commitHandler;\r\n    this.abortHandler = abortHandler;\r\n    this.source = source;\r\n  }\r\n\r\n  get contentChanges(): IContentChanged[] | null {\r\n    return this._contentChanges.length > 0 ? this._contentChanges : null;\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    let item: ITransactionItem = null;\r\n    let changes: IContentChanged[] = [];\r\n    while (item = this.redoStack.pop()) {\r\n      try {\r\n        let result = item.do();\r\n        if (result) {\r\n          changes.push(...result);\r\n        }\r\n        this.undoStack.push(item);\r\n      } catch (err) {\r\n        throw err;\r\n      }\r\n    }\r\n    return changes.length > 0 ? changes : null;\r\n\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    let item: ITransactionItem = null;\r\n    let changes: IContentChanged[] = [];\r\n    while (item = this.undoStack.pop()) {\r\n      try {\r\n        let result = item.undo();\r\n        if (result) {\r\n          changes.push(...result);\r\n        }\r\n        this.redoStack.push(item);\r\n      } catch (err) {\r\n        throw err;\r\n      }\r\n    }\r\n    return changes.length > 0 ? changes : null;\r\n  };\r\n\r\n  commit() {\r\n    this.commitHandler(this);\r\n  }\r\n\r\n  abort() {\r\n    this.abortHandler(this);\r\n  }\r\n\r\n  mergeWith(other: ITransactionItem): boolean {\r\n    return false;\r\n  }\r\n\r\n  addCommitedSubchangeGroup(changeGroup: ChangeGroup) {\r\n    this.undoStack.push(changeGroup);\r\n    this._recordContentChanges(changeGroup.contentChanges);\r\n  }\r\n\r\n  public undoStack: ITransactionItem[] = []\r\n  public redoStack: ITransactionItem[] = []\r\n\r\n  public execute(item: ITransactionItem): IContentChanged[] | null {\r\n    let changes: IContentChanged[] | null = item.do();\r\n    this._recordContentChanges(changes);\r\n\r\n    for (let existingItem of this.undoStack) {\r\n      if (existingItem.mergeWith(item))\r\n        return changes;\r\n    }\r\n\r\n    this.undoStack.push(item);\r\n    return changes;\r\n  }\r\n\r\n  private _recordContentChanges(changes: IContentChanged[] | null) {\r\n    if (changes?.length)\r\n      this._contentChanges.push(...changes);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/ITransactionItem.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IContentChanged } from '../InstanceServiceContainer.js';\r\n\r\nexport interface ITransactionItem {\r\n  title?: string\r\n  readonly affectedItems?: IDesignItem[]\r\n  do: () => IContentChanged[] | null\r\n  undo: () => IContentChanged[] | null\r\n  mergeWith(other: ITransactionItem): boolean;\r\n  redoBranches?: ITransactionItem[][];\r\n};"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/IUndoChangeEvent.ts",
    "content": "import { ITransactionItem } from './ITransactionItem.js';\n\nexport type UndoChangeKind = 'execute' | 'undo' | 'redo';\nexport type UndoChangeSource = 'local' | 'remote';\n\nexport interface IUndoChangeEvent {\n  item: ITransactionItem;\n  kind: UndoChangeKind;\n  source: UndoChangeSource;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/IUndoService.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IService } from '../IService.js';\r\nimport { ChangeGroup } from './ChangeGroup.js';\r\nimport { IUndoChangeEvent, UndoChangeSource } from './IUndoChangeEvent.js';\r\nimport { ITransactionItem } from './ITransactionItem.js';\r\n\r\nexport interface IUndoService extends IService {\r\n  openGroup(title: string, source?: UndoChangeSource): ChangeGroup;\r\n  execute(item: ITransactionItem, source?: UndoChangeSource): void;\r\n  canUndo(): boolean;\r\n  canRedo(): boolean;\r\n  clear();\r\n  clearTransactionstackIfNotEmpty();\r\n  undo(source?: UndoChangeSource);\r\n  redo(source?: UndoChangeSource);\r\n  redoTo(transactionItems: ITransactionItem[]);\r\n  getUndoEntryNames(count?: number): Generator<string, void, unknown>;\r\n  getUndoEntries(count?: number): Generator<ITransactionItem, void, unknown>;\r\n  getRedoEntryNames(count?: number): Generator<string, void, unknown>;\r\n  getRedoEntries(count?: number): Generator<ITransactionItem, void, unknown>;\r\n  readonly undoCount: number;\r\n  readonly redoCount: number;\r\n  readonly onTransaction: TypedEvent<IUndoChangeEvent>;\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/UndoService.ts",
    "content": "import { ITransactionItem } from './ITransactionItem.js';\r\nimport { ChangeGroup } from \"./ChangeGroup.js\";\r\nimport { IUndoService } from './IUndoService.js';\r\nimport { IDesignerCanvas } from '../../widgets/designerView/IDesignerCanvas.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IUndoChangeEvent, UndoChangeKind, UndoChangeSource } from './IUndoChangeEvent.js';\r\nimport { IContentChanged } from '../InstanceServiceContainer.js';\r\n\r\n/*\r\n * Manages a stack of available undo/redo actions\r\n */\r\nexport class UndoService implements IUndoService {\r\n  private _undoStack: ITransactionItem[] = [];\r\n  private _redoStack: ITransactionItem[] = [];\r\n  private _transactionStack: ChangeGroup[] = [];\r\n  private _designerCanvas: IDesignerCanvas;\r\n  private _storeRedoBranches: boolean;\r\n\r\n  constructor(designerCanvas: IDesignerCanvas, storeRedoBranches: boolean = false) {\r\n    this._designerCanvas = designerCanvas;\r\n    this._storeRedoBranches = storeRedoBranches;\r\n  }\r\n\r\n  openGroup(title: string, source: UndoChangeSource = 'local'): ChangeGroup {\r\n    let t = new ChangeGroup(title, (t) => this.commitTransactionItem(t), (t) => this.abortTransactionItem(t), source);\r\n    this._transactionStack.push(t);\r\n    return t;\r\n  }\r\n\r\n  private commitTransactionItem(transactionItem: ChangeGroup) {\r\n    let itm = this._transactionStack.pop();\r\n    if (itm !== transactionItem) {\r\n      this.clear();\r\n      throw \"UndoService - Commited Transaction was not the last\";\r\n    }\r\n    if (itm.undoStack.length) {\r\n      if (this._transactionStack.length > 0) {\r\n        this._transactionStack[this._transactionStack.length - 1].addCommitedSubchangeGroup(itm);\r\n      } else {\r\n        if (this._storeRedoBranches && this._redoStack.length) {\r\n          if (itm.redoBranches == null)\r\n            itm.redoBranches = [];\r\n          itm.redoBranches.push(this._redoStack);\r\n        }\r\n        this._redoStack = [];\r\n        this._undoStack.push(itm);\r\n      }\r\n    }\r\n    if (this._transactionStack.length == 0) {\r\n      this._designerCanvas.extensionManager.refreshAllExtensions(transactionItem.affectedItems);\r\n      this.emitTransaction(transactionItem, 'execute', (<ChangeGroup>transactionItem).source ?? 'local');\r\n      if (transactionItem.contentChanges)\r\n        this._designerCanvas.instanceServiceContainer.onContentChanged.emit(transactionItem.contentChanges);\r\n    }\r\n  }\r\n\r\n  private abortTransactionItem(transactionItem: ChangeGroup) {\r\n    if (this._transactionStack.length > 0) {\r\n      let itm = this._transactionStack.pop();\r\n      if (itm !== transactionItem) {\r\n        this.clear();\r\n        throw \"UndoService - Aborted Transaction was not the last\";\r\n      }\r\n      itm.undo();\r\n    }\r\n  }\r\n\r\n  execute(item: ITransactionItem, source: UndoChangeSource = 'local') {\r\n    let changeItems: IContentChanged[] | null = null;\r\n    if (this._transactionStack.length == 0) {\r\n      changeItems = item.do();\r\n      if (this._storeRedoBranches && this._redoStack.length) {\r\n        if (item.redoBranches == null)\r\n          item.redoBranches = [];\r\n        item.redoBranches.push(this._redoStack);\r\n      }\r\n      this._redoStack = [];\r\n      this._undoStack.push(item);\r\n    } else {\r\n      changeItems = this._transactionStack[this._transactionStack.length - 1].execute(item);\r\n    }\r\n    if (this._transactionStack.length == 0) {\r\n      this._designerCanvas.extensionManager.refreshAllExtensions(item.affectedItems);\r\n      this.emitTransaction(item, 'execute', source);\r\n      if (changeItems)\r\n        this._designerCanvas.instanceServiceContainer.onContentChanged.emit(changeItems);\r\n    }\r\n  }\r\n\r\n  clear() {\r\n    this._undoStack = [];\r\n    this._redoStack = [];\r\n    this._transactionStack = [];\r\n  }\r\n\r\n  clearTransactionstackIfNotEmpty() {\r\n    if (this._transactionStack.length) {\r\n      console.error(\"transactionStack was not empty, but should be\", this._transactionStack);\r\n      this._transactionStack = [];\r\n    }\r\n  }\r\n\r\n  undo(source: UndoChangeSource = 'local') {\r\n    if (!this.canUndo())\r\n      return;\r\n    if (this._transactionStack.length != 0)\r\n      throw \"Cannot Undo while transaction is running\";\r\n\r\n    let item = this._undoStack.pop();\r\n    let changeItems: IContentChanged[] | null = null;\r\n    try {\r\n      changeItems = item.undo();\r\n      this._redoStack.push(item);\r\n    } catch (err) {\r\n      this.clear();\r\n      throw err;\r\n    }\r\n    this._designerCanvas.extensionManager.refreshAllExtensions(item.affectedItems);\r\n    this.emitTransaction(item, 'undo', source);\r\n    if (changeItems)\r\n      this._designerCanvas.instanceServiceContainer.onContentChanged.emit(changeItems);\r\n  }\r\n\r\n  redo(source: UndoChangeSource = 'local') {\r\n    if (!this.canRedo())\r\n      return;\r\n    if (this._transactionStack.length != 0)\r\n      throw \"Cannot Redo while transaction is running\";\r\n\r\n    let item = this._redoStack.pop();\r\n    let changeItems: IContentChanged[] | null = null;\r\n    try {\r\n      changeItems = item.do();\r\n      this._undoStack.push(item);\r\n    } catch (err) {\r\n      this.clear();\r\n      throw err;\r\n    }\r\n    this._designerCanvas.extensionManager.refreshAllExtensions(item.affectedItems);\r\n    this.emitTransaction(item, 'redo', source);\r\n    if (changeItems)\r\n      this._designerCanvas.instanceServiceContainer.onContentChanged.emit(changeItems);\r\n  }\r\n\r\n  redoTo(transactionItems: ITransactionItem[]) {\r\n    this._redoStack = transactionItems.toReversed();\r\n    for (let n = 0; n < transactionItems.length; n++)\r\n      this.redo();\r\n  }\r\n\r\n  canUndo(): boolean {\r\n    return this._undoStack.length > 0;\r\n  }\r\n\r\n  canRedo(): boolean {\r\n    return this._redoStack.length > 0;\r\n  }\r\n\r\n  get undoCount(): number {\r\n    return this._undoStack.length;\r\n  }\r\n\r\n  get redoCount(): number {\r\n    return this._redoStack.length;\r\n  }\r\n\r\n  *getUndoEntryNames(count: number = 999): Generator<string, void, unknown> {\r\n    for (let i = Math.min(this._undoStack.length, count) - 1; i >= 0; i--)\r\n      yield this._undoStack[i].title;\r\n  }\r\n\r\n  *getUndoEntries(count: number = 999): Generator<ITransactionItem, void, unknown> {\r\n    for (let i = Math.min(this._undoStack.length, count) - 1; i >= 0; i--)\r\n      yield this._undoStack[i];\r\n  }\r\n\r\n  *getRedoEntryNames(count: number = 999): Generator<string, void, unknown> {\r\n    for (let i = Math.min(this._redoStack.length, count) - 1; i >= 0; i--)\r\n      yield this._redoStack[i].title;\r\n  }\r\n\r\n  *getRedoEntries(count: number = 999): Generator<ITransactionItem, void, unknown> {\r\n    for (let i = Math.min(this._redoStack.length, count) - 1; i >= 0; i--)\r\n      yield this._redoStack[i];\r\n  }\r\n\r\n  /** Event emitted when a transaction occurs, needed for remote collaboration */\r\n  readonly onTransaction = new TypedEvent<IUndoChangeEvent>();\r\n\r\n  private emitTransaction(item: ITransactionItem, kind: UndoChangeKind, source: UndoChangeSource) {\r\n    this.onTransaction.emit({ item, kind, source });\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/AttributeAndPropertyChangeAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IBinding } from '../../../item/IBinding.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class AttributeAndPropertyChangeAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, attributeName: string, propertyName: string, newValue: any | IBinding | null, oldValue: any | IBinding | null) {\r\n    this.title = \"Change Attribute & Property\" + attributeName + \" of &lt;\" + designItem.name + \"&gt;\";\r\n\r\n    this.designItem = designItem;\r\n    this.attributeName = attributeName;\r\n    this.propertyName = propertyName;\r\n    this.newValue = newValue;\r\n    this.oldValue = oldValue;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.designItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    this.designItem.element[this.propertyName] = this.oldValue;\r\n    if (this.oldValue == null) {\r\n      this.designItem._withoutUndoRemoveAttribute(this.attributeName);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.attributeName, oldValue: this.newValue, newValue: null }];\r\n    } else {\r\n      let val = this.oldValue;\r\n      if (typeof this.oldValue !== 'string')\r\n        val = this.oldValue.toString();\r\n      this.designItem._withoutUndoSetAttribute(<string>this.attributeName, val);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.attributeName, oldValue: this.newValue, newValue: val }];\r\n    }\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this.designItem.element[this.propertyName] = this.newValue;\r\n    if (this.newValue == null) {\r\n      this.designItem._withoutUndoRemoveAttribute(<string>this.attributeName);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.attributeName, oldValue: this.newValue, newValue: null }];\r\n    } else {\r\n      let val = this.newValue;\r\n      if (typeof this.newValue !== 'string')\r\n        val = this.newValue.toString();\r\n      this.designItem._withoutUndoSetAttribute(<string>this.attributeName, val);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.attributeName, oldValue: this.oldValue, newValue: val }];\r\n    }\r\n  }\r\n\r\n  public designItem: IDesignItem;\r\n  public attributeName: any;\r\n  public propertyName: any;\r\n  public newValue: any;\r\n  public oldValue: any;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    if (other instanceof AttributeAndPropertyChangeAction && this.designItem === other.designItem && this.attributeName === other.attributeName && this.propertyName === other.propertyName) {\r\n      this.newValue = other.newValue;\r\n      return true;\r\n    }\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/AttributeChangeAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IBinding } from '../../../item/IBinding.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class AttributeChangeAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, name: string, newValue: string | IBinding | null, oldValue: string | IBinding | null) {\r\n    this.title = \"Change Attribute \" + name + \" of &lt;\" + designItem.name + \"&gt;\";\r\n\r\n    this.designItem = designItem;\r\n    this.name = name;\r\n    this.newValue = newValue;\r\n    this.oldValue = oldValue;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.designItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    if (this.oldValue == null) {\r\n      this.designItem._withoutUndoRemoveAttribute(this.name);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.name, oldValue: this.newValue, newValue: null }];\r\n    } else {\r\n      let val = this.oldValue;\r\n      if (typeof this.oldValue !== 'string')\r\n        val = this.oldValue.toString();\r\n      this.designItem._withoutUndoSetAttribute(<string>this.name, val);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.name, oldValue: this.newValue, newValue: val }];\r\n    }\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    if (this.newValue == null) {\r\n      this.designItem._withoutUndoRemoveAttribute(<string>this.name);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.name, oldValue: this.newValue, newValue: null }];\r\n    } else {\r\n      let val = this.newValue;\r\n      if (typeof this.newValue !== 'string')\r\n        val = this.newValue.toString();\r\n      this.designItem._withoutUndoSetAttribute(<string>this.name, val);\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'attribute', name: this.name, oldValue: this.oldValue, newValue: val }];\r\n    }\r\n  }\r\n\r\n  public designItem: IDesignItem;\r\n  public name: string;\r\n  public newValue: any;\r\n  public oldValue: any;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    if (other instanceof AttributeChangeAction && this.designItem === other.designItem && this.name === other.name) {\r\n      this.newValue = other.newValue;\r\n      return true;\r\n    }\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/CssStyleChangeAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class CssStyleChangeAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, name: string, newValue: any, oldValue: any, newImportant: boolean = false, oldImportant: boolean = false) {\n    this.title = \"Change Css Style \" + name + \" of &lt;\" + designItem.name + \"&gt;\";\r\n\r\n    this.designItem = designItem;\r\n    this.name = name;\n    this.newValue = newValue;\n    this.oldValue = oldValue;\n    this.newImportant = newImportant;\n    this.oldImportant = oldImportant;\n  }\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.designItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    if (this.oldValue === '' || this.oldValue == null) {\r\n      this.designItem._withoutUndoRemoveStyle(<string>this.name);\r\n      if ((<string>this.name).startsWith('--')) {\r\n        (<ElementCSSInlineStyle><unknown>this.designItem.element).style.removeProperty(<string>this.name);\r\n      } else {\r\n        (<ElementCSSInlineStyle><unknown>this.designItem.element).style[<string>this.name] = '';\r\n      }\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'css', name: this.name, oldValue: this.newValue, newValue: null }];\r\n    } else {\r\n      this.designItem._withoutUndoSetStyle(<string>this.name, this.oldValue, this.oldImportant);\n      (<ElementCSSInlineStyle><unknown>this.designItem.element).style.setProperty(<string>this.name, this.oldValue, this.oldImportant ? 'important' : '');\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'css', name: this.name, oldValue: this.newValue, newValue: this.oldValue }];\n    }\r\n  }\r\n  do(): IContentChanged[] | null {\r\n    if (this.newValue === '' || this.newValue == null) {\r\n      this.designItem._withoutUndoRemoveStyle(<string>this.name);\r\n      if ((<string>this.name).startsWith('--')) {\r\n        (<ElementCSSInlineStyle><unknown>this.designItem.element).style.removeProperty(<string>this.name);\r\n      } else {\r\n        (<ElementCSSInlineStyle><unknown>this.designItem.element).style[<string>this.name] = '';\r\n      }\r\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'css', name: this.name, oldValue: this.oldValue, newValue: null }];\r\n    } else {\r\n      this.designItem._withoutUndoSetStyle(<string>this.name, this.newValue, this.newImportant);\n      (<ElementCSSInlineStyle><unknown>this.designItem.element).style.setProperty(<string>this.name, this.newValue, this.newImportant ? 'important' : '');\n      return [{ changeType: 'changed', designItems: this.affectedItems, type: 'css', name: this.name, oldValue: this.oldValue, newValue: this.newValue }];\n    }\r\n  }\r\n\r\n  public designItem: IDesignItem;\r\n  public name: string;\n  public newValue: any;\n  public oldValue: any;\n  public newImportant: boolean;\n  public oldImportant: boolean;\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/DeleteAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { DomHelper } from '@node-projects/base-custom-webcomponent';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class DeleteAction implements ITransactionItem {\r\n\r\n  constructor(deletedItems: IDesignItem[]) {\r\n    this.title = \"Delete Items\";\r\n    this.deletedItems = deletedItems;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return this.deletedItems;\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    for (let n = 0; n < this.deletedItems.length; n++) {\r\n      this._parentItems[n]._insertChildInternal(this.deletedItems[n], this._parentIndexes[n]);\r\n    }\r\n    return [{ changeType: 'added', designItems: this.deletedItems }];\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this._parentItems = [];\r\n    this._parentIndexes = [];\r\n    for (let n = 0; n < this.deletedItems.length; n++) {\r\n      this._parentItems.push(this.deletedItems[n].parent);\r\n      this._parentIndexes.push(DomHelper.nodeIndex(this.deletedItems[n].element));\r\n    }\r\n    for (let n = 0; n < this.deletedItems.length; n++) {\r\n      this.deletedItems[n].parent._removeChildInternal(this.deletedItems[n]);\r\n    }\r\n    return [{ changeType: 'removed', designItems: this.deletedItems }];\r\n  }\r\n\r\n  public deletedItems: IDesignItem[];\r\n  private _parentItems: IDesignItem[];\r\n  private _parentIndexes: number[];\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/InsertAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class InsertAction implements ITransactionItem {\r\n\r\n  constructor(containerItem: IDesignItem, index: number, newItem: IDesignItem) {\r\n    this.title = \"Insert Item\";\r\n\r\n    this.containerItem = containerItem;\r\n    this.index = index;\r\n    this.newItem = newItem;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.containerItem, this.newItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    this.newItem.parent._removeChildInternal(this.newItem);\r\n    return [{ changeType: 'removed', designItems: [this.newItem] }];\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this.containerItem._insertChildInternal(this.newItem, this.index);\r\n    return [{ changeType: 'added', designItems: [this.newItem] }];\r\n  }\r\n\r\n  public containerItem: IDesignItem;\r\n  public index: number;\r\n  public newItem: IDesignItem;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/InsertChildAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class InsertChildAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, newParent: IDesignItem, newIndex: number) {\r\n    this.title = \"Move or Insert Item\";\r\n\r\n    this.designItem = designItem;\r\n    this.newParent = newParent;\r\n    this.newIndex = newIndex;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    if (this.oldParent)\r\n      return [this.designItem, this.newParent, this.oldParent];\r\n    return [this.designItem, this.newParent];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    if (this.oldParent) {\r\n      this.oldParent._insertChildInternal(this.designItem, this.oldIndex);\r\n      return [{ changeType: 'moved', designItems: [this.designItem] }];\r\n    } else {\r\n      this.designItem.parent._removeChildInternal(this.designItem);\r\n      return [{ changeType: 'removed', designItems: [this.designItem] }];\r\n    }\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this.oldParent = this.designItem.parent;\r\n    if (this.oldParent)\r\n      this.oldIndex = this.designItem.parent.indexOf(this.designItem);\r\n    this.newParent._insertChildInternal(this.designItem, this.newIndex);\r\n    return [{ changeType: this.oldParent ? 'moved' : 'added', designItems: [this.designItem] }];\r\n  }\r\n\r\n  public designItem: IDesignItem;\r\n  public newParent: IDesignItem;\r\n  public newIndex: number;\r\n  public oldParent: IDesignItem;\r\n  public oldIndex: number;\r\n  public newItem: IDesignItem;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/PropertyChangeAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { setDeepValue } from '../../../helper/Helper.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class PropertyChangeAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, name: string, newValue: any, oldValue: any) {\r\n    this.title = \"Change Property \" + name + \" of &lt;\" + designItem.name + \"&gt;\";\r\n\r\n    this.designItem = designItem;\r\n    this.name = name;\r\n    this.newValue = newValue;\r\n    this.oldValue = oldValue;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.designItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    setDeepValue(this.designItem.node, this.name, this.oldValue);\r\n    return [{ changeType: 'changed', type: 'property', name: this.name, designItems: [this.designItem] }];\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    setDeepValue(this.designItem.node, this.name, this.newValue);\r\n    return [{ changeType: 'changed', type: 'property', name: this.name, designItems: [this.designItem] }];\r\n  }\r\n\r\n  public designItem: IDesignItem;\r\n  public name: string;\r\n  public newValue: any;\r\n  public oldValue: any;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    if (other instanceof PropertyChangeAction && this.designItem === other.designItem && this.name === other.name) {\r\n      this.newValue = other.newValue;\r\n      return true;\r\n    }\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/SelectionChangedAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ISelectionService } from '../../selectionService/ISelectionService.js';\r\nimport { SelectionService } from '../../selectionService/SelectionService.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class SelectionChangedAction implements ITransactionItem {\r\n\r\n    constructor(oldSelection: IDesignItem[], newSelection: IDesignItem[], selectionService: ISelectionService) {\r\n        this.title = \"Change Selection\";\r\n\r\n        this.oldSelection = oldSelection;\r\n        this.newSelection = newSelection;\r\n        this.selectionService = selectionService;\r\n    }\r\n\r\n    title?: string;\r\n\r\n    get affectedItems() {\r\n        if (this.oldSelection && this.newSelection)\r\n            return [...this.oldSelection, ...this.newSelection];\r\n        if (this.oldSelection)\r\n            return [...this.oldSelection];\r\n        return [...this.newSelection];\r\n    }\r\n\r\n    undo(): IContentChanged[] | null {\r\n        (<SelectionService>this.selectionService)._withoutUndoSetSelectedElements(this.oldSelection);\r\n        return null;\r\n    }\r\n\r\n    do(): IContentChanged[] | null {\r\n        (<SelectionService>this.selectionService)._withoutUndoSetSelectedElements(this.newSelection);\r\n        return null;\r\n    }\r\n\r\n    public oldSelection: IDesignItem[];\r\n    public newSelection: IDesignItem[];\r\n    public selectionService: ISelectionService\r\n\r\n    mergeWith(other: ITransactionItem) {\r\n        return false\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/SetDesignItemsAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class SetDesignItemsAction implements ITransactionItem {\r\n\r\n  constructor(newDesignItems: IDesignItem[], oldDesignItems: IDesignItem[]) {\r\n    this.title = \"Set all DesignItems\";\r\n\r\n    this.newDesignItems = newDesignItems;\r\n    this.oldDesignItems = oldDesignItems;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return this.newDesignItems;\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    this.newDesignItems[0].instanceServiceContainer.designerCanvas._internalSetDesignItems(this.oldDesignItems);\r\n    return null;\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this.newDesignItems[0].instanceServiceContainer.designerCanvas._internalSetDesignItems(this.newDesignItems);\r\n    return null;\r\n  }\r\n\r\n  public newDesignItems: IDesignItem[];\r\n  public oldDesignItems: IDesignItem[];\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/StylesheetChangedAction.ts",
    "content": "import { IContentChanged } from '../../InstanceServiceContainer.js';\r\nimport { IStylesheetService } from '../../stylesheetService/IStylesheetService.js';\r\nimport { ITransactionItem } from '../ITransactionItem.js';\r\n\r\nexport class StylesheetChangedAction implements ITransactionItem {\r\n\r\n  private stylesheetService: IStylesheetService;\r\n\r\n  constructor(stylesheetService: IStylesheetService, name: string, newValue: string, oldValue: string) {\r\n    this.title = \"Changed Css Stylesheet: \" + name;\r\n\r\n    this.stylesheetService = stylesheetService;\r\n    this.name = name;\r\n    this.newValue = newValue;\r\n    this.oldValue = oldValue;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\r\n    this.stylesheetService.updateCompleteStylesheetWithoutUndo(this.name, this.oldValue);\r\n    return null;\r\n  }\r\n\r\n  do(): IContentChanged[] | null {\r\n    this.stylesheetService.updateCompleteStylesheetWithoutUndo(this.name, this.newValue);\r\n    return null;\r\n  }\r\n\r\n  public name: string;\r\n  public newValue: string;\r\n  public oldValue: string;\r\n\r\n  mergeWith(other: ITransactionItem) { return false; }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/services/undoService/transactionItems/TextContentChangeAction.ts",
    "content": "import { ITransactionItem } from '../ITransactionItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IBinding } from '../../../item/IBinding.js';\r\nimport { IContentChanged } from '../../InstanceServiceContainer.js';\r\n\r\nexport class TextContentChangeAction implements ITransactionItem {\r\n\r\n  constructor(designItem: IDesignItem, newValue: string | IBinding | null, oldValue: string | IBinding | null) {\r\n    this.title = \"Change TextContent from '\" + oldValue + \"' to '\" + newValue + \"'\";\r\n\r\n    this.designItem = designItem;\r\n    this.newValue = newValue;\r\n    this.oldValue = oldValue;\r\n  }\r\n\r\n  title?: string;\r\n\r\n  get affectedItems() {\r\n    return [this.designItem];\r\n  }\r\n\r\n  undo(): IContentChanged[] | null {\n    this.designItem.element.textContent = this.oldValue;\n    this.designItem.refreshRenderedDesignItem();\n    return [{ changeType: 'changed', designItems: this.affectedItems, type: 'property', name: 'textContent', oldValue: this.newValue, newValue: this.oldValue }];\n  }\n\n  do(): IContentChanged[] | null {\n    this.designItem.element.textContent = this.newValue;\n    this.designItem.refreshRenderedDesignItem();\n    return [{ changeType: 'changed', designItems: this.affectedItems, type: 'property', name: 'textContent', oldValue: this.oldValue, newValue: this.newValue }];\n  }\n\r\n  public designItem: IDesignItem;\r\n  public newValue: any;\r\n  public oldValue: any;\r\n\r\n  mergeWith(other: ITransactionItem) {\r\n    return false\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/bindableObjectsBrowser/IBindableObjectsBrowser.ts",
    "content": "import { TypedEvent } from \"@node-projects/base-custom-webcomponent\";\nimport { BindableObjectsTarget } from \"../../services/bindableObjectsService/BindableObjectsTarget.js\";\nimport { InstanceServiceContainer } from \"../../services/InstanceServiceContainer.js\";\nimport { ServiceContainer } from \"../../services/ServiceContainer.js\";\nimport { IBindableObject } from \"../../services/bindableObjectsService/IBindableObject.js\";\n\nexport interface IBindableObjectsBrowser extends HTMLElement {\n    initialize(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, bindableObjectsTarget: BindableObjectsTarget): Promise<void>;\n    selectedObject: IBindableObject<any>;\n    objectDoubleclicked: TypedEvent<void>;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/codeView/ICodeView.ts",
    "content": "import { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IDisposable } from '../../../interfaces/IDisposable.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { IStringPosition } from '../../services/htmlWriterService/IStringPosition.js';\r\nimport { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport interface ICodeView extends IUiCommandHandler, IDisposable, HTMLElement {\r\n  update(code: string, instanceServiceContainer?: InstanceServiceContainer) : void;\r\n  getText(): string;\r\n  setSelection(position: IStringPosition): void;\r\n  focusEditor(): void;\r\n  onTextChanged: TypedEvent<string>;\r\n  readOnly?: boolean;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/codeView/code-view-simple.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { ICodeView } from './ICodeView.js';\r\nimport { IActivateable } from '../../../interfaces/IActivateable.js';\r\nimport { IStringPosition } from '../../services/htmlWriterService/IStringPosition.js';\r\nimport { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IUiCommand } from '../../../commandHandling/IUiCommand.js';\r\n\r\nexport class CodeViewSimple extends BaseCustomWebComponentConstructorAppend implements ICodeView, IActivateable, IUiCommandHandler {\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  canvasElement: HTMLElement;\r\n  elementsToPackages: Map<string, string>;\r\n\r\n  public onTextChanged = new TypedEvent<string>();\r\n\r\n  private _text: HTMLTextAreaElement;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      width: 100%;\r\n    }\r\n\r\n    textarea {\r\n      height: 100%;\r\n      width: 100%;\r\n      resize: none;\r\n      white-space: nowrap;\r\n      box-sizing: border-box;\r\n    }\r\n    `;\r\n\r\n  static override readonly template = html`\r\n      <div id=\"container\" style=\"width: 100%; height: 100%; position: absolute;\">\r\n        <textarea id=\"text\"></textarea>\r\n      </div>\r\n  `;\r\n\r\n  executeCommand(command: IUiCommand) {\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    return false;\r\n  }\r\n\r\n  async ready() {\r\n    this._text = this._getDomElement<HTMLTextAreaElement>('text');\r\n  }\r\n\r\n  focusEditor() {\r\n    requestAnimationFrame(() => {\r\n      this.focus();\r\n      this._text.focus();\r\n    });\r\n  }\r\n\r\n  activated() {\r\n  }\r\n\r\n  update(code) {\r\n    this._text.value = code;\r\n  }\r\n\r\n  getText() {\r\n    return this._text.value;\r\n  }\r\n\r\n  setSelection(position: IStringPosition) {\r\n    this._text.setSelectionRange(position.start, position.start + position.length);\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-code-view-simple', CodeViewSimple);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/debugView/debug-view.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { DesignItem } from \"../../item/DesignItem.js\";\r\n\r\n\r\nfunction generateSelector(element: Element) {\r\n    if (!element)\r\n        return '';\r\n    let selector, tag = element.nodeName.toLowerCase();\r\n    if (element.id) {\r\n        selector = '#' + element.getAttribute('id');\r\n    } else if (element.getAttribute('class')) {\r\n        selector = '.' + element.getAttribute('class').split(' ').join('.');\r\n    }\r\n    return selector ? tag + selector : tag;\r\n}\r\n\r\nconst getClosestStackingContext = function (node: Node) {\r\n    // the root element (HTML).\r\n    if (!node || node.nodeName === 'HTML' || node.nodeName === '#document') {\r\n        return { node: document.documentElement, reason: 'root' };\r\n    }\r\n\r\n    // handle shadow root elements.\r\n    if (node.nodeName === '#document-fragment') {\r\n        return getClosestStackingContext((<ShadowRoot>node).host);\r\n    }\r\n\r\n    const computedStyle = node.ownerDocument.defaultView.getComputedStyle(<Element>node);\r\n\r\n    // position: fixed or sticky.\r\n    if (computedStyle.position === 'fixed' || computedStyle.position === 'sticky') {\r\n        return { node: node, reason: `position: ${computedStyle.position}` };\r\n    }\r\n\r\n    // positioned (absolutely or relatively) with a z-index value other than \"auto\".\r\n    if (computedStyle.zIndex !== 'auto' && computedStyle.position !== 'static') {\r\n        return { node: node, reason: `position: ${computedStyle.position}; z-index: ${computedStyle.zIndex}` };\r\n    }\r\n\r\n    // elements with an opacity value less than 1.\r\n    if (computedStyle.opacity !== '1') {\r\n        return { node: node, reason: `opacity: ${computedStyle.opacity}` };\r\n    }\r\n\r\n    // elements with a transform value other than \"none\".\r\n    if (computedStyle.transform !== 'none') {\r\n        return { node: node, reason: `transform: ${computedStyle.transform}` };\r\n    }\r\n\r\n    // elements with a scale value other than \"none\".\r\n    if (computedStyle.scale !== 'none') {\r\n        return { node: node, reason: `scale: ${computedStyle.scale}` };\r\n    }\r\n\r\n    // elements with a transform value other than \"none\".\r\n    if (computedStyle.translate !== 'none') {\r\n        return { node: node, reason: `translate: ${computedStyle.translate}` };\r\n    }\r\n\r\n    // elements with a transform value other than \"none\".\r\n    if (computedStyle.rotate !== 'none') {\r\n        return { node: node, reason: `rotate: ${computedStyle.rotate}` };\r\n    }\r\n\r\n    // elements with a mix-blend-mode value other than \"normal\".\r\n    if (computedStyle.mixBlendMode !== 'normal') {\r\n        return { node: node, reason: `mixBlendMode: ${computedStyle.mixBlendMode}` };\r\n    }\r\n\r\n    // elements with a filter value other than \"none\".\r\n    if (computedStyle.filter !== 'none') {\r\n        return { node: node, reason: `filter: ${computedStyle.filter}` };\r\n    }\r\n\r\n    // elements with a filter value other than \"none\".\r\n    if (computedStyle.backdropFilter !== 'none') {\r\n        return { node: node, reason: `backdropFilter: ${computedStyle.backdropFilter}` };\r\n    }\r\n\r\n    // elements with a perspective value other than \"none\".\r\n    if (computedStyle.perspective !== 'none') {\r\n        return { node: node, reason: `perspective: ${computedStyle.perspective}` };\r\n    }\r\n\r\n    // elements with a clip-path value other than \"none\".\r\n    if (computedStyle.clipPath !== 'none') {\r\n        return { node: node, reason: `clip-path: ${computedStyle.clipPath} ` };\r\n    }\r\n\r\n    // elements with a mask value other than \"none\".\r\n    const mask = computedStyle.mask || computedStyle.webkitMask;\r\n    if (mask !== 'none' && mask !== undefined) {\r\n        return { node: node, reason: `mask:  ${mask}` };\r\n    }\r\n\r\n    // elements with a mask-image value other than \"none\".\r\n    const maskImage = computedStyle.maskImage || computedStyle.webkitMaskImage;\r\n    if (maskImage !== 'none' && maskImage !== undefined) {\r\n        return { node: node, reason: `mask-image: ${maskImage}` };\r\n    }\r\n\r\n    // elements with a mask-border value other than \"none\".\r\n    //@ts-ignore\r\n    const maskBorder = computedStyle.maskBorder || computedStyle.webkitMaskBorder;\r\n    if (maskBorder !== 'none' && maskBorder !== undefined) {\r\n        return { node: node, reason: `mask-border: ${maskBorder}` };\r\n    }\r\n\r\n    // elements with isolation set to \"isolate\".\r\n    if (computedStyle.isolation === 'isolate') {\r\n        return { node: node, reason: `isolation: ${computedStyle.isolation}` };\r\n    }\r\n\r\n    // transform or opacity in will-change even if you don't specify values for these attributes directly.\r\n    if (computedStyle.willChange === 'transform' || computedStyle.willChange === 'opacity' || computedStyle.willChange === 'scale' || computedStyle.willChange === 'translate' || computedStyle.willChange === 'rotate') {\r\n        return { node: node, reason: `willChange: ${computedStyle.willChange}` };\r\n    }\r\n\r\n    // an item with a z-index value other than \"auto\".\r\n    if (computedStyle.zIndex !== 'auto') {\r\n        const parentStyle = node.ownerDocument.defaultView.getComputedStyle(<Element>node.parentNode);\r\n        // with a flex|inline-flex parent.\r\n        if (parentStyle.display === 'flex' || parentStyle.display === 'inline-flex') {\r\n            return {\r\n                node: node,\r\n                reason: `flex-item; z-index: ${computedStyle.zIndex}`,\r\n            };\r\n            // with a grid parent.\r\n        } else if (parentStyle.grid !== 'none / none / none / row / auto / auto') {\r\n            return {\r\n                node: node,\r\n                reason: `child of grid container; z-index: ${computedStyle.zIndex}`,\r\n            };\r\n        }\r\n    }\r\n\r\n    // contain with a value of layout, or paint, or a composite value that includes either of them\r\n    const contain = computedStyle.contain;\r\n    if (['layout', 'paint', 'strict', 'content'].indexOf(contain) > -1 ||\r\n        contain.indexOf('paint') > -1 ||\r\n        contain.indexOf('layout') > -1\r\n    ) {\r\n        return {\r\n            node: node,\r\n            reason: `contain: ${contain}`,\r\n        };\r\n    }\r\n\r\n    return getClosestStackingContext(node.parentNode);\r\n};\r\n\r\nexport class DebugView extends BaseCustomWebComponentConstructorAppend {\r\n\r\n    public static override readonly template = html`\r\n        <div>\r\n            <table>\r\n                    <tr><th colspan=\"2\">Styling</th></tr>\r\n                    <tr><td>display</td><td>[[?this.computedStyle?.display]]</td></tr>\r\n                    <tr><td>position</td><td>[[?this.computedStyle?.position]]</td></tr>\r\n                    <tr><td>visibility</td><td>[[?this.computedStyle?.visibility]]</td></tr>\r\n                    <tr><td>pointerEvents</td><td>[[?this.computedStyle?.pointerEvents]]</td></tr>\r\n                    <tr><td>zIndex</td><td>[[?this.computedStyle?.zIndex]]</td></tr>\r\n                    <tr><th colspan=\"2\">Context</th></tr>\r\n                    <tr><td>offsetParent</td><td class=\"lnk\" @click=\"[[this._clickLink(event, 'offsetParent')]]\">[[?this.selectedElementOffsetParentText]]</td></tr>\r\n                    <tr><td>createsStackingContext</td><td>[[this.createsStackingContext]]</td></tr>\r\n                    <tr><td>stackingContextReason</td><td>[[this.createsStackingContextReason]]</td></tr>\r\n                    <tr><td>stackingContextParent</td><td class=\"lnk\" @click=\"[[this._clickLink(event, 'stackingContextParent')]]\">[[?this.parentStackingContextText]]</td></tr>\r\n            </table>\r\n        </div> \r\n    `;\r\n\r\n    public static override readonly style = css`\r\n      :host {\r\n        overflow: auto;\r\n        height: 100%;\r\n        display: block;\r\n        font-size: 12px;\r\n      }\r\n      table {\r\n        font-family: Arial, Helvetica, sans-serif;\r\n        border-collapse: collapse;\r\n        width: 100%;\r\n      }\r\n      \r\n      table td, table th {\r\n        border: 1px solid #ddd;\r\n        padding: 2px 4px;\r\n      }\r\n      \r\n      table tr:nth-child(even){background-color: #f2f2f2;}\r\n      \r\n      table tr:hover {background-color: #ddd;}\r\n      \r\n      table th {\r\n        text-align: left;\r\n        background-color: #989898;\r\n        color: white;\r\n      }\r\n      \r\n      .lnk {\r\n        color: blue;\r\n      }\r\n      .lnk:hover {\r\n        text-decoration: underline;\r\n        cursor: pointer;\r\n      }`;\r\n\r\n    private _ready: boolean;\r\n    computedStyle: CSSStyleDeclaration;\r\n    createsStackingContext: boolean = <any>'';\r\n    createsStackingContextReason: string = '';\r\n    parentStackingContext: Element;\r\n    parentStackingContextText: string;\r\n    selectedElementOffsetParent: Element;\r\n    selectedElementOffsetParentText: string;\r\n\r\n    constructor() {\r\n        super();\r\n        this._restoreCachedInititalValues();\r\n    }\r\n\r\n    ready() {\r\n        this._parseAttributesToProperties();\r\n\r\n        this._bindingsParse();\r\n        this._ready = true;\r\n    }\r\n\r\n    _clickLink(e: MouseEvent, target: 'offsetParent' | 'stackingContextParent') {\r\n        if (target == 'offsetParent') {\r\n            if (this.selectedElementOffsetParent) {\r\n                let di = DesignItem.GetDesignItem(this.selectedElementOffsetParent);\r\n                di.instanceServiceContainer.selectionService.setSelectedElements([di]);\r\n            }\r\n        } else if (target == 'stackingContextParent') {\r\n            if (this.parentStackingContext) {\r\n                let di = DesignItem.GetDesignItem(this.parentStackingContext);\r\n                di.instanceServiceContainer.selectionService.setSelectedElements([di]);\r\n            }\r\n        }\r\n    }\r\n\r\n    update(designItem: IDesignItem) {\r\n        if (this._ready) {\r\n            requestAnimationFrame(() => {\r\n                let element = designItem?.element;\r\n                if (element?.nodeType == 3)\r\n                    element = element.parentNode as Element;\r\n                if (element && element.nodeType != 8 && element.nodeType != 11) {\r\n                    this.computedStyle = element.ownerDocument.defaultView.getComputedStyle(element);\r\n                    this.selectedElementOffsetParent = (<HTMLElement>element).offsetParent;\r\n                    if (this.selectedElementOffsetParent == designItem.instanceServiceContainer.designerCanvas.rootDesignItem.element ||\r\n                        this.selectedElementOffsetParent == designItem.instanceServiceContainer.designerCanvas.rootDesignItem.element.parentElement) {\r\n                        this.selectedElementOffsetParentText = null;\r\n                        this.selectedElementOffsetParent = null;\r\n                    } else\r\n                        this.selectedElementOffsetParentText = generateSelector((<HTMLElement>element).offsetParent);\r\n\r\n                    if (element && element.nodeType === 1) {\r\n\r\n                        const closest = getClosestStackingContext(element);\r\n                        this.createsStackingContext = element === closest.node;\r\n                        this.createsStackingContextReason = this.createsStackingContext ? closest.reason : 'not a stacking context';\r\n                         //@ts-ignore\r\n                        this.parentStackingContext = closest.node;\r\n                        if (this.createsStackingContext && element.nodeName !== 'HTML') {\r\n                             //@ts-ignore\r\n                            this.parentStackingContext = getClosestStackingContext(element.parentNode).node;\r\n                        }\r\n                        if (this.parentStackingContext == designItem.instanceServiceContainer.designerCanvas.rootDesignItem.element.parentElement ||\r\n                            this.parentStackingContext == designItem.instanceServiceContainer.designerCanvas.rootDesignItem.element.parentElement.parentElement) {\r\n                            this.parentStackingContextText = null;\r\n                            this.parentStackingContext = null;\r\n                        } else\r\n                            this.parentStackingContextText = generateSelector(this.parentStackingContext);\r\n                    }\r\n                } else {\r\n                    //@ts-ignore\r\n                    this.computedStyle = {};\r\n                    this.createsStackingContext = false;\r\n                    this.createsStackingContextReason = '';\r\n                    this.createsStackingContext = false;\r\n                    this.selectedElementOffsetParent = null;\r\n                    this.selectedElementOffsetParentText = null;\r\n                    this.parentStackingContext = null;\r\n                    this.parentStackingContextText = null;\r\n                }\r\n\r\n                this._bindingsRefresh();\r\n            });\r\n        }\r\n    }\r\n}\r\n\r\ncustomElements.define('node-projects-debug-view', DebugView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/demoView/IDemoView.ts",
    "content": "import { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IDisposable } from '../../../interfaces/IDisposable.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\n\r\nexport interface IDemoView extends IUiCommandHandler, IDisposable, HTMLElement {\r\n  display(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string);\r\n  stopDisplay?: () => void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/demoView/demoView.ts",
    "content": "import { IDemoView } from './IDemoView.js';\r\nimport { BaseCustomWebComponentLazyAppend, css } from '@node-projects/base-custom-webcomponent';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IUiCommand } from '../../../commandHandling/IUiCommand.js';\r\nimport { IDisposable } from '../../../interfaces/IDisposable.js';\r\n\r\nexport class DemoView extends BaseCustomWebComponentLazyAppend implements IDemoView, IUiCommandHandler, IDisposable {\r\n\r\n  private _placeholder: HTMLDivElement;\r\n  private _loading: HTMLDivElement;\r\n\r\n  static override readonly style = css`\r\n  :host {\r\n    display: block;\r\n    overflow: hidden;\r\n    background: white;\r\n    height: 100%;\r\n    width: 100%;\r\n    position: relative;\r\n  }\r\n  #placeholder {\r\n    position: absolute;\r\n    left: 24px;\r\n    height: 100%;\r\n    width: calc(100% - 24px);\r\n  }\r\n  #loading {\r\n    position: absolute;\r\n    top: 60px;\r\n    left: 20px;\r\n  }\r\n  #left {\r\n    position:absolute;\r\n    left: 0;\r\n    top: 0;\r\n    height: 100%;\r\n    width: 24px;\r\n    border-right: solid white 1px;\r\n    box-sizing: border-box;\r\n    background: black;\r\n  }\r\n  span {\r\n    color: white;\r\n    rotate: 270deg;\r\n    display: block;\r\n    position: absolute;\r\n    top: 35px;\r\n    left: -38px;\r\n    font-weight: 600;\r\n    font-family: monospace;\r\n    font-size: 24px;\r\n  }`;\r\n\r\n  constructor() {\r\n    super();\r\n\r\n    this._placeholder = document.createElement('div');\r\n    this._placeholder.id = 'placeholder';\r\n    this._placeholder.style.transform = 'scale(1)'; //to fix position: static alignment\r\n    this.shadowRoot.appendChild(this._placeholder)\r\n    this._loading = document.createElement('div');\r\n    this._loading.id = 'loading';\r\n    this._loading.textContent = '🛀 Hold on, loading...';\r\n    this.shadowRoot.appendChild(this._loading);\r\n    const div = document.createElement(\"div\");\r\n    div.id = \"left\"\r\n    const span = document.createElement(\"span\");\r\n    span.innerText = \"PREVIEW\";\r\n    div.appendChild(span);\r\n    this.shadowRoot.appendChild(div);\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  executeCommand: (command: IUiCommand) => void;\r\n  canExecuteCommand: (command: IUiCommand) => boolean;\r\n\r\n  async display(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string) {\r\n    this._loading.hidden = false;\r\n    await serviceContainer.demoProviderService.provideDemo(this._placeholder, serviceContainer, instanceServiceContainer, code, style);\r\n    this._loading.hidden = true;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-demo-view', DemoView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/DesignContext.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignContext } from './IDesignContext.js';\r\n\r\nexport class DesignContext implements IDesignContext {\r\n  public imports: string[] = [];\r\n  public npmPackages: string[] = [];\r\n  public extensionOptions: {\r\n    [key: string]: any;\r\n    gridExtensionShowOverlay: boolean;\r\n    flexboxExtensionShowOverlay: boolean;\r\n    invisibleElementExtensionShowOverlay: boolean;\r\n    enableStylesheetService: boolean;\r\n    basicStackedToolbarExtensionShowOverlay: boolean;\r\n    simulateHoverOnHover: boolean;\r\n    selectUnhitableElements: boolean;\r\n  } = {\r\n      gridExtensionShowOverlay: false,\r\n      flexboxExtensionShowOverlay: false,\r\n      invisibleElementExtensionShowOverlay: false,\r\n      enableStylesheetService: false,\r\n      basicStackedToolbarExtensionShowOverlay: false,\r\n      simulateHoverOnHover: false,\r\n      selectUnhitableElements: true,\r\n    };\r\n  extensionOptionsChanged = new TypedEvent<void>;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/DomConverter.ts",
    "content": "import { IDesignItem } from \"../../item/IDesignItem.js\";\r\nimport { IndentedTextWriter } from \"../../helper/IndentedTextWriter.js\";\r\nimport { SimpleTextWriter } from \"../../helper/SimpleTextWriter.js\";\r\n\r\nexport class DomConverter {\r\n\r\n  public static normalizeAttributeValue(value: string | number, useSingleQuotes = false) {\r\n    if (typeof value === 'number')\r\n      value = value.toString();\r\n    if (value) {\r\n      if (useSingleQuotes)\r\n        return value.replaceAll('&', '&amp;').replaceAll('\\'', '&#39;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\r\n      return value.replaceAll('&', '&amp;').replaceAll('\"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\r\n    }\r\n    return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\r\n  }\r\n\r\n  public static normalizeContentValue(value: string) {\r\n    if (value)\r\n      return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll(' ', '&nbsp;');  // !caution! -> this is not normal space, it's nbsp\r\n    return value;\r\n  }\r\n\r\n  public static IsSelfClosingElement(tag: string) {\r\n    return tag === 'area' ||\r\n      tag === 'base' ||\r\n      tag === 'br' ||\r\n      tag === 'col' ||\r\n      tag === 'embed' ||\r\n      tag === 'hr' ||\r\n      tag === 'img' ||\r\n      tag === 'input' ||\r\n      tag === 'keygen' ||\r\n      tag === 'link' ||\r\n      tag === 'meta' ||\r\n      tag === 'param' ||\r\n      tag === 'source' ||\r\n      tag === 'track' ||\r\n      tag === 'wbr';\r\n  }\r\n\r\n  public static ConvertToString(designItems: IDesignItem[], beautifyOutput?: boolean, updatePositions:boolean = false) {\r\n    let itw = beautifyOutput !== false ? new IndentedTextWriter() : new SimpleTextWriter();\r\n    //TODO: check how we could support this beautify here, cause it's now a setting of the writer...\r\n    //let options: HtmlWriterOptions = { beautifyOutput: beautifyOutput !== false, writeDesignerProperties: true, compressCssToShorthandProperties: true, parseJsonInAttributes: true, jsonWriteMode: 'beauty' };\r\n    designItems[0].serviceContainer.htmlWriterService.write(itw, designItems, true, updatePositions);\r\n    return itw.getString();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/IDesignContext.ts",
    "content": "import { TypedEvent } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport interface IDesignContext {\r\n  imports: string[];\r\n  npmPackages: string[];\r\n  extensionOptions: {\r\n    [key: string]: any;\r\n    gridExtensionShowOverlay: boolean;\r\n    flexboxExtensionShowOverlay: boolean;\r\n    invisibleElementExtensionShowOverlay: boolean;\r\n    enableStylesheetService: boolean;\r\n    basicStackedToolbarExtensionShowOverlay: boolean;\r\n    simulateHoverOnHover: boolean;\r\n    selectUnhitableElements: boolean;\r\n  };\r\n  extensionOptionsChanged: TypedEvent<void>;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/IDesignerCanvas.ts",
    "content": "//import { PointerActionType } from '../../../enums/PointerActionType.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { Snaplines } from './Snaplines.js';\r\nimport { IExtensionManager } from './extensions/IExtensionManger.js';\r\nimport { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { OverlayLayerView } from './overlayLayerView.js';\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\nimport { ISize } from \"../../../interfaces/ISize.js\";\r\nimport { ITool } from \"./tools/ITool.js\";\r\n\r\nexport interface IDesignerCanvas extends IUiCommandHandler, HTMLElement {\r\n  readOnly: boolean;\r\n  \r\n  readonly serviceContainer: ServiceContainer;\r\n  readonly instanceServiceContainer: InstanceServiceContainer;\r\n  readonly containerBoundingRect: DOMRect;\r\n  readonly outerRect: DOMRect;\r\n  readonly rootDesignItem: IDesignItem;\r\n  readonly overlayLayer: OverlayLayerView;\r\n  readonly extensionManager: IExtensionManager;\r\n  readonly clickOverlay: HTMLDivElement;\r\n\r\n  readonly snapLines: Snaplines;\r\n\r\n  readonly shadowRoot: ShadowRoot | null;\r\n\r\n  readonly alignOnGrid: boolean;\r\n  readonly alignOnSnap: boolean;\r\n  readonly gridSize: number;\r\n\r\n  raiseDesignItemsChanged: (designItems: IDesignItem[], action: 'resize' | 'place', operationFinished: boolean) => void\r\n\r\n  readonly iframes: HTMLIFrameElement[];\r\n\r\n  zoomFactor: number;\r\n  designerOffsetWidth: number;\r\n  designerOffsetHeight: number;\r\n  readonly designerPixelSize: { width: number, height: number };\r\n\r\n  readonly scaleFactor: number;\r\n  canvasOffset: IPoint;\r\n  readonly containerOffset: IPoint;\r\n  canvas: HTMLElement;\r\n  pauseAnimations: boolean;\r\n  additionalStyles: CSSStyleSheet[];\r\n\r\n  ignoreEvent(event: Event);\r\n\r\n  initialize(serviceContainer: ServiceContainer);\r\n\r\n  getNormalizedEventCoordinates(event: MouseEvent): IPoint;\r\n  getViewportCoordinates(event: MouseEvent): IPoint;\r\n  getNormalizedElementCoordinates(element: Element): IRect;\r\n  getNormalizedElementCoordinatesAndRealSizes(element: Element): IRect & { realWidth: number, realHeight: number }\r\n  getNormalizedElementCoordinates(element: Element, ignoreScalefactor?: boolean): IRect;\r\n\r\n  captureActiveTool(tool: ITool);\r\n  releaseActiveTool();\r\n  readonly activeTool: ITool;\r\n\r\n  getDesignSurfaceDimensions(): ISize;\r\n\r\n  getNormalizedOffsetInElement(event: MouseEvent, element: Element): IPoint;\r\n  getElementAtPoint(point: IPoint, ignoreElementCallback?: (element: HTMLElement) => boolean);\r\n  elementsFromPoint(x: number, y: number): Element[];\r\n\r\n  showHoverExtension(element: Element, event: Event);\r\n\r\n  setDesignItems(designItems: IDesignItem[]);\r\n  _internalSetDesignItems(designItems: IDesignItem[]);\r\n\r\n  getDesignItemById(id: string): IDesignItem;\r\n  querySelectorDesignItem(selector: string): IDesignItem;\r\n  querySelectorAllDesignItems(selector: string): IDesignItem[];\r\n\r\n  zoomTowardsPoint(point: IPoint, scalechange: number): void;\r\n  zoomPoint(canvasPoint: IPoint, newZoom: number): void;\r\n  zoomOntoRectangle(startPoint: IPoint, endPoint: IPoint): void;\r\n\r\n  showDesignItemContextMenu(designItem: IDesignItem, event: MouseEvent): void;\r\n\r\n  lazyTriggerReparseDocumentStylesheets();\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/Snaplines.ts",
    "content": "import type { IDesignItem } from '../../item/IDesignItem.js';\r\nimport type { IPoint } from '../../../interfaces/IPoint.js';\r\nimport type { ISize } from '../../../interfaces/ISize.js';\r\nimport { OverlayLayerView } from './overlayLayerView.js';\r\nimport { OverlayLayer } from \"./extensions/OverlayLayer.js\";\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\n\r\nconst overlayLayer = OverlayLayer.Normal;\r\n\r\nexport class Snaplines {\r\n\r\n  public snapOffset = 5;\r\n  private _overlayLayerView: OverlayLayerView;\r\n\r\n  private _containerItem: IDesignItem;\r\n  private _positionsH: [number, IRect][] = [];\r\n  private _positionsMiddleH: [number, IRect][] = [];\r\n  private _positionsV: [number, IRect][] = [];\r\n  private _positionsMiddleV: [number, IRect][] = [];\r\n  private _outerRect: DOMRect;\r\n\r\n  constructor(overlayLayer: OverlayLayerView) {\r\n    this._overlayLayerView = overlayLayer;\r\n  }\r\n\r\n  initialize(containerItem: IDesignItem) {\r\n    //add snapline layer\r\n    this._containerItem = containerItem;\r\n  }\r\n\r\n  clearSnaplines() {\r\n    if (this._overlayLayerView.removeAllNodesWithClass)\r\n      this._overlayLayerView.removeAllNodesWithClass('svg-snapline');\r\n    this._positionsH = [];\r\n    this._positionsMiddleH = [];\r\n    this._positionsV = [];\r\n    this._positionsMiddleV = [];\r\n  }\r\n\r\n  calculateSnaplines(ignoredItems: IDesignItem[]) {\r\n    this.clearSnaplines();\r\n    const providedSnaplines = this._containerItem.serviceContainer.snaplinesProviderService.provideSnaplines(this._containerItem, ignoredItems);\r\n    this._outerRect = providedSnaplines.outerRect;\r\n    this._positionsH = providedSnaplines.positionsH;\r\n    this._positionsMiddleH = providedSnaplines.positionsMiddleH;\r\n    this._positionsV = providedSnaplines.positionsV;\r\n    this._positionsMiddleV = providedSnaplines.positionsMiddleV;\r\n  }\r\n\r\n  //return the snapped position\r\n  snapToPosition(position: IPoint, size: ISize, moveDirection: IPoint): IPoint {\r\n    let minDiff = this.snapOffset + 1;\r\n    let pH = undefined;\r\n    let posH = undefined;\r\n    for (let i = 0; i < this._positionsH.length; i++) {\r\n      let akDiff1 = Math.abs(this._positionsH[i][0] - position.x);\r\n      if (akDiff1 < minDiff || (akDiff1 === minDiff && pH === undefined)) {\r\n        minDiff = akDiff1;\r\n        pH = [];\r\n        posH = this._positionsH[i][0];\r\n      }\r\n      let akDiff2: number;\r\n      if (size) {\r\n        akDiff2 = Math.abs(position.x + size.width - this._positionsH[i][0]);\r\n        if (akDiff2 < minDiff || (akDiff2 === minDiff && pH === undefined)) {\r\n          minDiff = akDiff2;\r\n          pH = [];\r\n          posH = this._positionsH[i][0] - size.width;\r\n        }\r\n      }\r\n      if (akDiff1 === minDiff) {\r\n        pH.push(this._positionsH[i][1]);\r\n      }\r\n      if (size) {\r\n        if (akDiff2 === minDiff && akDiff1 !== minDiff) {\r\n          pH.push(this._positionsH[i][1]);\r\n        }\r\n      }\r\n    }\r\n    if (size) {\r\n      for (let i = 0; i < this._positionsMiddleH.length; i++) {\r\n        let akDiff1 = Math.abs(this._positionsMiddleH[i][0] - (position.x + size.width / 2));\r\n        if (akDiff1 < minDiff || (akDiff1 === minDiff && pH === undefined)) {\r\n          minDiff = akDiff1;\r\n          pH = [];\r\n          posH = this._positionsMiddleH[i][0] - size.width / 2;\r\n        }\r\n        if (akDiff1 === minDiff) {\r\n          pH.push(this._positionsMiddleH[i][1]);\r\n        }\r\n      }\r\n    }\r\n\r\n    minDiff = this.snapOffset + 1;\r\n    let pV = undefined;\r\n    let posV = undefined;\r\n    for (let i = 0; i < this._positionsV.length; i++) {\r\n      let akDiff1 = Math.abs(this._positionsV[i][0] - position.y);\r\n      if (akDiff1 < minDiff || (akDiff1 === minDiff && pV === undefined)) {\r\n        minDiff = akDiff1;\r\n        pV = [];\r\n        posV = this._positionsV[i][0];\r\n      }\r\n      let akDiff2: number;\r\n      if (size) {\r\n        akDiff2 = Math.abs(position.y + size.height - this._positionsV[i][0]);\r\n        if (akDiff2 < minDiff || (akDiff2 === minDiff && pV === undefined)) {\r\n          minDiff = akDiff2;\r\n          pV = [];\r\n          posV = this._positionsV[i][0] - size.height;\r\n        }\r\n      }\r\n      if (akDiff1 === minDiff) {\r\n        pV.push(this._positionsV[i][1]);\r\n      }\r\n      if (size) {\r\n        if (akDiff2 === minDiff && akDiff1 !== minDiff) {\r\n          pV.push(this._positionsV[i][1]);\r\n        }\r\n      }\r\n    }\r\n    if (size) {\r\n      for (let i = 0; i < this._positionsMiddleV.length; i++) {\r\n        let akDiff1 = Math.abs(this._positionsMiddleV[i][0] - (position.y + size.height / 2));\r\n        if (akDiff1 < minDiff || (akDiff1 === minDiff && pV === undefined)) {\r\n          minDiff = akDiff1;\r\n          pV = [];\r\n          posV = this._positionsMiddleV[i][0] - size.height / 2;\r\n        }\r\n        if (akDiff1 === minDiff) {\r\n          pV.push(this._positionsMiddleV[i][1]);\r\n        }\r\n      }\r\n    }\r\n\r\n    this._overlayLayerView.removeAllNodesWithClass('svg-snapline');\r\n\r\n    if (pH !== undefined || pV !== undefined) {\r\n      let pos = { x: pH !== undefined ? posH : position.x, y: pV !== undefined ? posV : position.y };\r\n      this.drawSnaplines(pos, size, pH, pV);\r\n      return { x: pH !== undefined ? posH : null, y: pV !== undefined ? posV : null };\r\n    }\r\n\r\n    return { x: null, y: null };\r\n  }\r\n\r\n  drawSnaplines(position: IPoint, size: ISize, rectsH: IRect[], rectsV: IRect[]) {\r\n    if (rectsH) {\r\n      let minY = position.y;\r\n      let maxY = position.y;\r\n      for (const r of rectsH) {\r\n        let ry = r.y - this._outerRect.top;\r\n        minY = minY < ry ? minY : ry;\r\n        maxY = maxY > ry ? maxY : ry;\r\n      }\r\n      for (const r of rectsH) {\r\n        if (r.x - this._outerRect.left == position.x || (size && r.x - this._outerRect.left == position.x + size.width)) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(r.x - this._outerRect.x));\r\n          line.setAttribute('x2', <string><any>(r.x - this._outerRect.x));\r\n          line.setAttribute('y1', <string><any>(minY));\r\n          line.setAttribute('y2', <string><any>(maxY));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        if (r.x - this._outerRect.left + r.width == position.x || (size && r.x - this._outerRect.left + r.width == position.x + size.width)) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(r.x - this._outerRect.x + r.width));\r\n          line.setAttribute('x2', <string><any>(r.x - this._outerRect.x + r.width));\r\n          line.setAttribute('y1', <string><any>(minY));\r\n          line.setAttribute('y2', <string><any>(maxY));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        if (size && r.x - this._outerRect.left + r.width / 2 == position.x + size.width / 2) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(r.x - this._outerRect.x + r.width / 2));\r\n          line.setAttribute('x2', <string><any>(r.x - this._outerRect.x + r.width / 2));\r\n          line.setAttribute('y1', <string><any>(minY));\r\n          line.setAttribute('y2', <string><any>(maxY));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        let rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        rect.setAttribute('x', <string><any>(r.x - this._outerRect.x));\r\n        rect.setAttribute('width', <string><any>(r.width));\r\n        rect.setAttribute('y', <string><any>(r.y - this._outerRect.y));\r\n        rect.setAttribute('height', <string><any>(r.height));\r\n        rect.setAttribute('class', 'svg-snapline');\r\n        this._overlayLayerView.addOverlay(this.constructor.name, rect, overlayLayer);\r\n      }\r\n    }\r\n\r\n    if (rectsV) {\r\n      let minX = position.x;\r\n      let maxX = position.x;\r\n      for (const r of rectsV) {\r\n        let rx = r.x - this._outerRect.left;\r\n        minX = minX < rx ? minX : rx;\r\n        maxX = maxX > rx ? maxX : rx;\r\n      }\r\n      for (const r of rectsV) {\r\n        if (r.y - this._outerRect.top == position.y || (size && r.y - this._outerRect.top == position.y + size.height)) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(minX));\r\n          line.setAttribute('x2', <string><any>(maxX));\r\n          line.setAttribute('y1', <string><any>(r.y - this._outerRect.y));\r\n          line.setAttribute('y2', <string><any>(r.y - this._outerRect.y));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        if (r.y - this._outerRect.top + r.height == position.y || (size && r.y - this._outerRect.top + r.height == position.y + size.height)) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(minX));\r\n          line.setAttribute('x2', <string><any>(maxX));\r\n          line.setAttribute('y1', <string><any>(r.y - this._outerRect.y + r.height));\r\n          line.setAttribute('y2', <string><any>(r.y - this._outerRect.y + r.height));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        if (size && r.y - this._outerRect.top + r.height / 2 == position.y + size.height / 2) {\r\n          let line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n          line.setAttribute('x1', <string><any>(minX));\r\n          line.setAttribute('x2', <string><any>(maxX));\r\n          line.setAttribute('y1', <string><any>(r.y - this._outerRect.y + r.height / 2));\r\n          line.setAttribute('y2', <string><any>(r.y - this._outerRect.y + r.height / 2));\r\n          line.setAttribute('class', 'svg-snapline');\r\n          this._overlayLayerView.addOverlay(this.constructor.name, line, overlayLayer);\r\n        }\r\n\r\n        let rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        rect.setAttribute('x', <string><any>(r.x - this._outerRect.x));\r\n        rect.setAttribute('width', <string><any>(r.width));\r\n        rect.setAttribute('y', <string><any>(r.y - this._outerRect.y));\r\n        rect.setAttribute('height', <string><any>(r.height));\r\n        rect.setAttribute('class', 'svg-snapline');\r\n        this._overlayLayerView.addOverlay(this.constructor.name, rect, overlayLayer);\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/defaultConfiguredDesignerView.ts",
    "content": "import { DesignerView } from './designerView.js';\r\n\r\nexport class DefaultConfiguredDesignerView extends DesignerView {\r\n  constructor(){\r\n    super();\r\n  }\r\n\r\n  async ready() {\r\n    const createDefaultServiceContainer = await (await import('../../services/DefaultServiceBootstrap.js')).default;\r\n    this.initialize(createDefaultServiceContainer());\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-default-configured-designer-view', DefaultConfiguredDesignerView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/designerCanvas.ts",
    "content": "import { EventNames } from '../../../enums/EventNames.js';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { SelectionService } from '../../services/selectionService/SelectionService.js';\r\nimport { DesignItem, forceHoverAttributeName } from '../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { BaseCustomWebComponentLazyAppend, css, cssFromString, DomHelper, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { dragDropFormatNameBindingObject } from '../../../Constants.js';\r\nimport { InsertAction } from '../../services/undoService/transactionItems/InsertAction.js';\r\nimport { IDesignerCanvas } from './IDesignerCanvas.js';\r\nimport { Snaplines } from './Snaplines.js';\r\nimport { CommandType } from '../../../commandHandling/CommandType.js';\r\nimport { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IUiCommand } from '../../../commandHandling/IUiCommand.js';\r\nimport { DefaultHtmlParserService } from '../../services/htmlParserService/DefaultHtmlParserService.js';\r\nimport { ExtensionType } from './extensions/ExtensionType.js';\r\nimport { IExtensionManager } from './extensions/IExtensionManger.js';\r\nimport { ExtensionManager } from './extensions/ExtensionManager.js';\r\nimport { NamedTools } from './tools/NamedTools.js';\r\nimport { Screenshot } from '../../helper/Screenshot.js';\r\nimport { exportData } from '../../helper/Helper.js';\r\nimport { IContextMenuItem } from '../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IPoint } from '../../../interfaces/IPoint.js';\r\nimport { OverlayLayerView } from './overlayLayerView.js';\r\nimport { IDesignerPointerExtension } from './extensions/pointerExtensions/IDesignerPointerExtension.js';\r\nimport { IRect } from \"../../../interfaces/IRect.js\";\r\nimport { ISize } from \"../../../interfaces/ISize.js\";\r\nimport { ITool } from \"./tools/ITool.js\";\r\nimport { IPlacementService } from \"../../services/placementService/IPlacementService.js\";\r\nimport { ContextMenu } from '../../helper/contextMenu/ContextMenu.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { StylesheetChangedAction } from '../../services/undoService/transactionItems/StylesheetChangedAction.js';\r\nimport { SetDesignItemsAction } from '../../services/undoService/transactionItems/SetDesignItemsAction.js';\r\nimport { IDocumentStylesheet } from '../../services/stylesheetService/IStylesheetService.js';\r\nimport { filterChildPlaceItems } from '../../helper/LayoutHelper.js';\r\nimport { ChangeGroup } from '../../services/undoService/ChangeGroup.js';\r\nimport { TouchGestureHelper } from '../../helper/TouchGestureHelper.js';\r\nimport { stylesheetFromString } from '../../helper/StylesheetHelper.js';\r\nimport { AbstractStylesheetService } from '../../services/stylesheetService/AbstractStylesheetService.js';\r\nimport { addPolyfill as addBoxQuadsPolyfill } from '../../helper/getBoxQuads.js';\r\nimport { hasCommandKey } from '../../helper/KeyboardHelper.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../helper/ElementHelper.js';\r\n\r\nconst disableAnimationsSheet = cssFromString`\r\n  * {\r\n    animation-play-state: paused !important;\r\n  }`\r\n\r\nexport class DesignerCanvas extends BaseCustomWebComponentLazyAppend implements IDesignerCanvas, IUiCommandHandler {\r\n  // Public Properties\r\n  public serviceContainer: ServiceContainer;\r\n  public instanceServiceContainer: InstanceServiceContainer;\r\n  public containerBoundingRect: DOMRect;\r\n  public outerRect: DOMRect;\r\n  public clickOverlay: HTMLDivElement;\r\n\r\n  private _activeTool: ITool;\r\n\r\n  // IPlacementView\r\n  private _gridSize = 10;\r\n  public get gridSize() {\r\n    return this._gridSize;\r\n  }\r\n  public set gridSize(value: number) {\r\n    this._gridSize = value;\r\n    const canvas = document.createElement(\"canvas\");\r\n    const context = canvas.getContext(\"2d\");\r\n    canvas.width = value * 2;\r\n    canvas.height = value * 2;\r\n    const patternSize = value;\r\n    for (let x = 0; x < canvas.width; x += patternSize) {\r\n      for (let y = 0; y < canvas.height; y += patternSize) {\r\n        context.fillStyle = (x + y) % (patternSize * 2) === 0 ? \"#e5e5e5\" : \"white\";\r\n        context.fillRect(x, y, patternSize, patternSize);\r\n      }\r\n    }\r\n    const dataURL = canvas.toDataURL();\r\n    this._backgroundImage = 'url(' + dataURL + ')';\r\n    if (this._canvas.style.backgroundImage != 'none')\r\n      this._canvas.style.backgroundImage = this._backgroundImage;\r\n  }\r\n\r\n  public pasteOffset = 10;\r\n  public alignOnGrid = false;\r\n  public alignOnSnap = true;\r\n  public snapLines: Snaplines;\r\n  public overlayLayer: OverlayLayerView;\r\n  public rootDesignItem: IDesignItem;\r\n\r\n  private _currentPasteOffset = this.pasteOffset;\r\n  private _zoomFactor = 1; //if scale or zoom css property is used this needs to be the value\r\n  private _scaleFactor = 1; //if scale css property is used this need to be the scale value\r\n  private _canvasOffset: IPoint = { x: 0, y: 0 };\r\n\r\n  private _additionalStyle: CSSStyleSheet[];\r\n  private _backgroundImage: string;\r\n\r\n  private _enableSelectTextNodesOnClick = false;\r\n\r\n  private _moveGroup: ChangeGroup;\r\n  private _touchGestureHelper: TouchGestureHelper;\r\n\r\n  public get zoomFactor(): number {\r\n    return this._zoomFactor;\r\n  }\r\n  public set zoomFactor(value: number) {\r\n    this._zoomFactor = value;\r\n    this._zoomFactorChanged();\r\n  }\r\n  public get scaleFactor(): number {\r\n    return this._scaleFactor;\r\n  }\r\n  public get canvasOffset(): IPoint {\r\n    return this._canvasOffset;\r\n  }\r\n  public set canvasOffset(value: IPoint) {\r\n    this._canvasOffset = value;\r\n    this._zoomFactorChanged();\r\n  }\r\n  public get canvasOffsetUnzoomed(): IPoint {\r\n    return { x: this._canvasOffset.x * this.zoomFactor, y: this._canvasOffset.y * this.zoomFactor };\r\n  }\r\n  public set canvasOffsetUnzoomed(value: IPoint) {\r\n    this.canvasOffset = { x: value.x / this.zoomFactor, y: value.y / this.zoomFactor };\r\n  }\r\n\r\n  private _pauseAnimations: boolean;\r\n  public get pauseAnimations() {\r\n    return this._pauseAnimations;\r\n  }\r\n  public set pauseAnimations(value: boolean) {\r\n    this._pauseAnimations = value;\r\n    this.applyAllStyles();\r\n  }\r\n\r\n  /** Offset when using an iframe as container */\r\n  public get containerOffset(): IPoint {\r\n    if (this._useIframe) {\r\n      const rect = this._outercanvas2.getBoundingClientRect();\r\n      return rect;\r\n    }\r\n    return { x: 0, y: 0 }\r\n  }\r\n\r\n  raiseDesignItemsChanged(designItems: IDesignItem[], action: 'resize' | 'place', operationFinished: boolean) {\r\n    if (this.designItemsChanged)\r\n      this.designItemsChanged(designItems, action, operationFinished);\r\n  }\r\n\r\n  public designItemsChanged: (designItems: IDesignItem[], action: 'resize' | 'place', operationFinished: boolean) => void\r\n\r\n  public onZoomFactorChanged = new TypedEvent<number>();\r\n\r\n  public get canvas(): HTMLElement {\r\n    return this._canvas;\r\n  }\r\n\r\n  public get outercanvas2(): HTMLDivElement {\r\n    return this._outercanvas2;\r\n  }\r\n\r\n  // Private Variables\r\n  private _canvas: HTMLDivElement;\r\n  private _canvasShadowRoot: Document | ShadowRoot;\r\n  private _canvasContainer: HTMLDivElement;\r\n  private _outercanvas2: HTMLDivElement;\r\n\r\n  private _lastHoverDesignItem: IDesignItem;\r\n\r\n  private _firstConnect: boolean;\r\n\r\n  //public static cssprefixConstant = '#node-projects-designer-canvas-canvas ';\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      box-sizing: border-box;\r\n      width: 100%;\r\n      position: relative;\r\n      transform: translateZ(0);\r\n      overflow: hidden;\r\n\r\n      font-family: inherit;\r\n      font-size: inherit;\r\n      font-weight: inherit;\r\n      font-style: inherit;\r\n      line-height: inherit;\r\n    }\r\n    * {\r\n      touch-action: none;\r\n    }\r\n    #node-projects-designer-canvas-canvasContainer {\r\n      background: var(--node-projects-web-component-designer-background, border-box fixed 0px 0px repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAFXmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjIwIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMjAiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIyMCIKICAgdGlmZjpJbWFnZUxlbmd0aD0iMjAiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjMwMC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIzMDAvMSIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0xMi0wOFQwOToxNTo0OCswMTowMCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMi0xMi0wOFQwOToxNTo0OCswMTowMCI+CiAgIDxkYzp0aXRsZT4KICAgIDxyZGY6QWx0PgogICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+QmFja2dyb3VuZGdyaWRfMTBweDwvcmRmOmxpPgogICAgPC9yZGY6QWx0PgogICA8L2RjOnRpdGxlPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgRGVzaWduZXIgMS4xMC42IgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTA4VDA5OjE1OjQ4KzAxOjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cjw/eHBhY2tldCBlbmQ9InIiPz4fvgn+AAABgWlDQ1BzUkdCIElFQzYxOTY2LTIuMQAAKJF1kc8rRFEUxz8GjRhRLCiLl7BCftTExmLkV2ExnvJr8+bNvBk1b+b13kiyVbaKEhu/FvwFbJW1UkRKlrImNug5b2ZqJplzO/d87vfec7r3XPCpSd10KnrATGXs8FhImZtfUPwvBKjCTx/Nmu5YUzOjKiXt854yL952ebVKn/vXaqIxR4eyKuEh3bIzwuPCk6sZy+Md4UY9oUWFz4Q7bbmg8J2nR3L86nE8x98e22p4GHz1wkq8iCNFrCdsU1heTpuZXNHz9/FeEoilZmcktoq34BBmjBAKE4wwTJBeBmUO0iX96ZYVJfJ7svnTpCVXl9liDZtl4iTI0CnqilSPSTREj8lIsub1/29fHaO/L1c9EILKZ9d9bwf/Nvxsue7Xkev+HEP5E1ymCvnpQxj4EH2roLUdQN0GnF8VtMguXGxC06Ol2VpWKhf3GQa8nULtPDTcQPVirmf5fU4eQF2Xr7qGvX3okPN1S790cWfsRnax1QAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAC9JREFUOI1jfPr0KQNuICUlhUeWCY8cQTCqeWRoZvz//z8e6WfPntHK5lHNI0MzAMChCNMTuPcnAAAAAElFTkSuQmCC));\r\n    }\r\n    #node-projects-designer-canvas-canvas {\r\n      image-rendering: pixelated;\r\n      box-sizing: border-box;\r\n      width: 100%;\r\n      height: 100%;\r\n      transform-origin: 0 0;\r\n      position: absolute;\r\n    }\r\n    \r\n    #node-projects-designer-canvas-canvas.dragFileActive {\r\n      outline: blue 4px solid;\r\n      outline-offset: -4px;\r\n    }\r\n    \r\n    node-projects-overlay-layer-view {\r\n      box-sizing: border-box;\r\n      width: 100%;\r\n      height: 100%;\r\n      position: absolute;\r\n      top: 0;\r\n      left: 0;\r\n      pointer-events: none;\r\n      overflow: visible;\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n      z-index: 999999999999;\r\n    }\r\n    \r\n    #node-projects-designer-canvas-canvas * {\r\n      cursor: pointer;\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n    }\r\n\r\n    #node-projects-designer-canvas-canvas iframe {\r\n      cursor: pointer;\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n      border: none;\r\n      width: 100%;\r\n      height: 100%;\r\n    }\r\n    \r\n    #node-projects-designer-canvas-clickOverlay {\r\n      position: absolute;\r\n      width: 100%;\r\n      height: 100%;\r\n      top: 0;\r\n    }\r\n    \r\n    #node-projects-designer-canvas-helper-element {\r\n      height: 0;\r\n      width: 0;\r\n    }\r\n\r\n    #node-projects-designer-search-container {\r\n      position:absolute;\r\n      display: grid;\r\n      grid-template-columns: 1fr auto auto;\r\n      align-items: center;\r\n      justify-content: left;\r\n      gap: 8px;\r\n      width: auto;\r\n      right: 10px;\r\n      top: 0;\r\n      background:rgb(242, 242, 242);\r\n      padding: 5px 6px;\r\n      border-bottom-left-radius: 5px;\r\n      border-bottom-right-radius: 5px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > #node-projects-designer-search-bar {\r\n      border: 1px solid black;\r\n      padding: 0;\r\n      display: grid;\r\n      grid-template-columns: 1fr auto;\r\n      align-items: center;\r\n      background-color: white;\r\n      padding: 1px 2px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > div > input {\r\n      height:20px;\r\n      padding-left: 6px;\r\n      font-size: 13px;\r\n      border: none;\r\n      outline: none;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > div > #node-projects-designer-search-start {\r\n      height:22px;\r\n      border: none;\r\n      background-color: white;\r\n      font-size: 13px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > div > #node-projects-designer-search-start:hover {\r\n      background-color: #D3D3D3;\r\n      transition: 0.9s;\r\n      border-radius: 2px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > #node-projects-designer-search-close {\r\n      position: relative;\r\n      width: 20px;\r\n      height: 20px;\r\n      border: none;\r\n      background-color: transparent;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > span {\r\n      font-family:sans-serif;\r\n      font-size: 12px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > #node-projects-designer-search-close::before,\r\n    #node-projects-designer-search-container > #node-projects-designer-search-close::after {\r\n      content: \"\";\r\n      position: absolute;\r\n      top: 50%;\r\n      left: 50%;\r\n      width: 60%;\r\n      background-color: black;\r\n      height: 1px;\r\n    }\r\n    \r\n    #node-projects-designer-search-container > #node-projects-designer-search-close::before {\r\n      transform: translate(-50%, -50%) rotate(45deg);\r\n    }\r\n    \r\n    #node-projects-designer-search-container > #node-projects-designer-search-close::after {\r\n      transform: translate(-50%, -50%) rotate(-45deg);\r\n    }\r\n  `;\r\n\r\n  static override readonly template = html`\r\n    <div style=\"display: flex;flex-direction: column;width: 100%;height: 100%; margin: 0 !important; padding: 0 !important; border: none !important;\">\r\n      <div style=\"width: 100%;height: 100%; margin: 0 !important; padding: 0 !important; border: none !important;\">\r\n        <div id=\"node-projects-designer-canvas-outercanvas2\" style=\"width:100%;height:100%;position:relative; margin: 0 !important; padding: 0 !important; border: none !important; isolation: isolate !important;\">\r\n          <div id=\"node-projects-designer-canvas-canvasContainer\"\r\n          style=\"width: 100%;height: 100%;position: absolute;top: 0;left: 0;user-select: none; margin: 0 !important; padding: 0 !important; border: none !important;\">\r\n          <div title=\"\" id=\"node-projects-designer-canvas-canvas\" part=\"canvas\"></div>\r\n        </div>\r\n      </div>\r\n      <div id=\"node-projects-designer-canvas-clickOverlay\" title=\"\" tabindex=\"0\" style=\"pointer-events: auto;  margin: 0 !important; padding: 0 !important; border: none !important;\"></div>\r\n      </div>\r\n      \r\n      <div id=\"node-projects-designer-search-container\" style=\"display: none;\">\r\n        <div id=\"node-projects-designer-search-bar\">\r\n          <input id=\"node-projects-designer-search-input\">\r\n          <button id=\"node-projects-designer-search-start\">&darr;</button>\r\n        </div>\r\n        <span id=\"node-projects-designer-search-result\">0 selected</span>\r\n        <button id=\"node-projects-designer-search-close\"></button>\r\n      </div>\r\n    </div>\r\n  `;\r\n\r\n  public extensionManager: IExtensionManager;\r\n  private _pointerextensions: IDesignerPointerExtension[];\r\n\r\n  private _lastCopiedPrimaryItem: IDesignItem;\r\n  private _ignoreEvent: Event;\r\n\r\n  private _useIframe = true;\r\n  private _iframe: HTMLIFrameElement;\r\n  private _window: Window;\r\n  private _resizeObserver: ResizeObserver;\r\n\r\n  public readonly iframes: HTMLIFrameElement[];\r\n\r\n  #readOnly = false;\r\n  get readOnly() {\r\n    return this.#readOnly;\r\n  }\r\n  set readOnly(v) {\r\n    this.#readOnly = v;\r\n  }\r\n\r\n  constructor(useIframe: boolean = false) {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    addBoxQuadsPolyfill();\r\n\r\n    this._useIframe = useIframe;\r\n\r\n    this._canvas = this._getDomElement<HTMLDivElement>('node-projects-designer-canvas-canvas');\r\n    if (this._useIframe) {\r\n      this._iframe = document.createElement('iframe');\r\n      this._iframe.setAttribute(\"sandbox\", \"allow-same-origin\");\r\n      this._iframe.setAttribute(\"scrolling\", \"no\");\r\n      this.iframes = [this._iframe];\r\n      //TODO: add option to allow scripts in iframes...\r\n      //this._iframe.setAttribute(\"sandbox\", \"allow-same-origin allow-scripts\");\r\n      this._canvas.appendChild(this._iframe);\r\n      requestAnimationFrame(() => {\r\n        this._window = this._iframe.contentWindow;\r\n        //@ts-ignore\r\n        addBoxQuadsPolyfill(this._iframe.contentWindow);\r\n        this._canvasShadowRoot = this._iframe.contentWindow.document;\r\n      })\r\n    } else {\r\n      this._window = window;\r\n      this._canvasShadowRoot = this._canvas.attachShadow({ mode: 'open' });\r\n    }\r\n\r\n    this._canvasContainer = this._getDomElement<HTMLDivElement>('node-projects-designer-canvas-canvasContainer');\r\n    this._outercanvas2 = this._getDomElement<HTMLDivElement>('node-projects-designer-canvas-outercanvas2');\r\n    this.clickOverlay = this._getDomElement<HTMLDivElement>('node-projects-designer-canvas-clickOverlay');\r\n\r\n    this.onKeyDown = this.onKeyDown.bind(this);\r\n    this.onKeyUp = this.onKeyUp.bind(this);\r\n    this._onDblClick = this._onDblClick.bind(this);\r\n    this._pointerEventHandler = this._pointerEventHandler.bind(this);\r\n    this._pointerEventHandlerCapture = this._pointerEventHandlerCapture.bind(this);\r\n    this._onWheel = this._onWheel.bind(this);\r\n\r\n    this._getDomElement<HTMLElement>('node-projects-designer-search-close').onclick = () => this._searchHideOverlay();\r\n    this._getDomElement<HTMLElement>('node-projects-designer-search-start').onclick = () => this._searchRun();\r\n    this._getDomElement<HTMLElement>('node-projects-designer-search-container').onkeydown = (event) => {\r\n      if (event.key === \"Enter\")\r\n        this._searchRun();\r\n    };\r\n    this.clickOverlay.oncontextmenu = (e) => e.preventDefault();\r\n\r\n    this._resizeObserver = new ResizeObserver(() => {\r\n      this.extensionManager?.refreshAllAppliedExtentions();\r\n    });\r\n    this._resizeObserver.observe(this);\r\n  }\r\n\r\n  get designerWidth(): string {\r\n    return this._canvasContainer.style.width;\r\n  }\r\n  set designerWidth(value: string) {\r\n    this._canvasContainer.style.width = value;\r\n    this._zoomFactorChanged();\r\n  }\r\n\r\n  get designerHeight(): string {\r\n    return this._canvasContainer.style.height;\r\n  }\r\n  set designerHeight(value: string) {\r\n    this._canvasContainer.style.height = value;\r\n    this._zoomFactorChanged();\r\n  }\r\n\r\n  get designerPixelSize(): { width: number, height: number } {\r\n    return { width: this._canvasContainer.offsetWidth, height: this._canvasContainer.offsetHeight };\r\n  }\r\n\r\n  getDesignSurfaceDimensions(): ISize {\r\n    let ret: ISize = { width: null, height: null };\r\n    const cs = getComputedStyle(this._canvasContainer);\r\n    if (this._canvasContainer.style.width)\r\n      ret.width = parseFloat(cs.width);\r\n    if (this._canvasContainer.style.height)\r\n      ret.height = parseFloat(cs.height);\r\n    return ret;\r\n  }\r\n\r\n  get designerOffsetWidth(): number {\r\n    return this._canvasContainer.offsetWidth;\r\n  }\r\n  get designerOffsetHeight(): number {\r\n    return this._canvasContainer.offsetHeight;\r\n  }\r\n\r\n  set additionalStyles(value: CSSStyleSheet[]) {\r\n    this._additionalStyle = value;\r\n    this.applyAllStyles();\r\n  }\r\n\r\n  get additionalStyles(): CSSStyleSheet[] {\r\n    return this._additionalStyle;\r\n  }\r\n\r\n  private applyAllStyles() {\r\n    if (this._window) {\r\n      let styles: CSSStyleSheet[] = []\r\n      if (this._additionalStyle)\r\n        styles.push(...this._additionalStyle);\r\n      if (this.instanceServiceContainer.stylesheetService) {\r\n        styles.push(...this.instanceServiceContainer.stylesheetService\r\n          .getStylesheets()\r\n          .map(x => stylesheetFromString(this._window, AbstractStylesheetService.patchStylesheetSelectorForDesigner(x.content))));\r\n      }\r\n\r\n      if (this._pauseAnimations) {\r\n        styles.push(disableAnimationsSheet);\r\n      }\r\n\r\n      if (this._useIframe) {\r\n        this._iframe.contentWindow.document.adoptedStyleSheets = styles;\r\n      } else {\r\n        this._canvasShadowRoot.adoptedStyleSheets = styles;\r\n      }\r\n    } else {\r\n      requestAnimationFrame(() => this.applyAllStyles());\r\n    }\r\n  }\r\n\r\n  ignoreEvent(event: Event) {\r\n    this._ignoreEvent = event;\r\n  }\r\n\r\n  /* --- start IUiCommandHandler --- */\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    const modelCommandService = this.serviceContainer.modelCommandService;\r\n    if (modelCommandService) {\r\n      const selection = this.instanceServiceContainer.selectionService.selectedElements;\r\n      let handeled = modelCommandService.canExecuteCommand(this, command, selection);\r\n      if (handeled !== null) {\r\n        return handeled;\r\n      }\r\n    }\r\n\r\n    if (command.type === CommandType.screenshot) {\r\n      return true;\r\n    }\r\n\r\n    if (command.type === CommandType.undo) {\r\n      return this.instanceServiceContainer.undoService.canUndo();\r\n    }\r\n    if (command.type === CommandType.redo) {\r\n      return this.instanceServiceContainer.undoService.canRedo();\r\n    }\r\n    if (command.type === CommandType.setTool) {\r\n      return this.serviceContainer.designerTools.has(command.parameter);\r\n    }\r\n\r\n    if (command.type === CommandType.paste) {\r\n      return true;\r\n    }\r\n    if (command.type === CommandType.copy || command.type === CommandType.cut || command.type === CommandType.delete) {\r\n      return this.instanceServiceContainer.selectionService.primarySelection != null && !this.instanceServiceContainer.selectionService.primarySelection.isRootItem;\r\n    }\r\n\r\n    return false;\r\n  }\r\n\r\n  async executeCommand(command: IUiCommand) {\r\n    const modelCommandService = this.serviceContainer.modelCommandService;\r\n    if (modelCommandService) {\r\n      const selection = this.instanceServiceContainer.selectionService.selectedElements;\r\n      let handeled = await modelCommandService.executeCommand(this, command, selection)\r\n      if (handeled != null) {\r\n        this.instanceServiceContainer.selectionService.setSelectedElements(null);\r\n        this.instanceServiceContainer.selectionService.setSelectedElements(selection);\r\n        return;\r\n      }\r\n    }\r\n    switch (command.type) {\r\n      case CommandType.screenshot: {\r\n        if (!Screenshot.screenshotsEnabled) {\r\n          alert(\"you need to select current tab in next browser dialog, or screenshots will not work correctly\");\r\n        }\r\n        let items = this.instanceServiceContainer.selectionService.selectedElements;\r\n        if (!items?.length) {\r\n          items = [...this.rootDesignItem.children(true)];\r\n        }\r\n        let data = await this.serviceContainer.pngCreatorService.takePng(items, { removeSelection: true });\r\n        await exportData(new Blob([new Uint8Array(data)], { type: 'image/png' }), \"screenshot.png\");\r\n      }\r\n        break;\r\n      case CommandType.setTool: {\r\n        this.serviceContainer.globalContext.tool = <ITool>this.serviceContainer.designerTools.get(command.parameter);\r\n        this.extensionManager.reapplyAllAppliedExtentions();\r\n      }\r\n        break;\r\n      case CommandType.setStrokeColor: {\r\n        this.serviceContainer.globalContext.strokeColor = command.parameter;\r\n      }\r\n        break;\r\n      case CommandType.setFillBrush: {\r\n        this.serviceContainer.globalContext.fillBrush = command.parameter;\r\n      }\r\n        break;\r\n      case CommandType.setStrokeThickness: {\r\n        this.serviceContainer.globalContext.strokeThickness = command.parameter;\r\n      }\r\n        break;\r\n      case CommandType.delete:\r\n        this.handleDeleteCommand();\r\n        break;\r\n      case CommandType.undo:\r\n        this.instanceServiceContainer.undoService.undo();\r\n        break;\r\n      case CommandType.holdUndo:\r\n        let undoEntries = this.instanceServiceContainer.undoService.getUndoEntryNames(20);\r\n        let undoMnu: IContextMenuItem[] = Array.from(undoEntries).map((x, idx) => ({ title: 'undo: ' + x, action: () => { for (let i = 0; i <= idx; i++) this.instanceServiceContainer.undoService.undo() } }));\r\n        if (undoMnu.length > 0)\r\n          ContextMenu.show(undoMnu, <MouseEvent>command.event, { mode: 'undo' });\r\n        break;\r\n      case CommandType.redo:\r\n        this.instanceServiceContainer.undoService.redo();\r\n        break;\r\n      case CommandType.holdRedo:\r\n        let redoEntries = this.instanceServiceContainer.undoService.getRedoEntryNames(20);\r\n        let redoMnu: IContextMenuItem[] = Array.from(redoEntries).map((x, idx) => ({ title: 'redo: ' + x, action: () => { for (let i = 0; i <= idx; i++) this.instanceServiceContainer.undoService.redo() } }));\r\n        if (redoMnu.length > 0)\r\n          ContextMenu.show(redoMnu, <MouseEvent>command.event, { mode: 'undo' });\r\n        break;\r\n      case CommandType.copy:\r\n        this.handleCopyCommand();\r\n        break;\r\n      case CommandType.cut:\r\n        this.handleCopyCommand();\r\n        this.handleDeleteCommand();\r\n        break;\r\n      case CommandType.paste:\r\n        this.handlePasteCommand(command.altKey == true);\r\n        break;\r\n      case CommandType.selectAll:\r\n        this.handleSelectAll();\r\n        break;\r\n    }\r\n  }\r\n\r\n  public disableBackgroud() {\r\n    this._canvasContainer.style.background = 'var(--node-projects-web-component-designer-screenshot-background, white)';\r\n  }\r\n\r\n  public enableBackground() {\r\n    this._canvasContainer.style.background = '';\r\n  }\r\n\r\n  public zoomToFit() {\r\n    const autoZomOffset = 10;\r\n    let maxX = 0, maxY = 0, minX = 0, minY = 0;\r\n\r\n    this.canvasOffset = { x: 0, y: 0 };\r\n    this.zoomFactor = 1;\r\n\r\n    for (let n of this.rootDesignItem.querySelectorAll('*')) {\r\n      if (n instanceof (n.ownerDocument.defaultView ?? window).Element) {\r\n        const rect = getBoundingClientRectAlsoForDisplayContents(n);\r\n        minX = minX < rect.x ? minX : rect.x;\r\n        minY = minY < rect.y ? minY : rect.y;\r\n        maxX = maxX > rect.x + rect.width + autoZomOffset ? maxX : rect.x + rect.width + autoZomOffset;\r\n        maxY = maxY > rect.y + rect.height + autoZomOffset ? maxY : rect.y + rect.height + autoZomOffset;\r\n      }\r\n    }\r\n\r\n    const cvRect = this.getBoundingClientRect();\r\n    maxX -= cvRect.x;\r\n    maxY -= cvRect.y;\r\n\r\n    let scaleX = cvRect.width / (maxX / this.zoomFactor);\r\n    let scaleY = cvRect.height / (maxY / this.zoomFactor);\r\n\r\n    const dimensions = this.getDesignSurfaceDimensions();\r\n    if (dimensions.width)\r\n      scaleX = cvRect.width / dimensions.width;\r\n    if (dimensions.height)\r\n      scaleY = cvRect.height / dimensions.height;\r\n\r\n    let fak = scaleX < scaleY ? scaleX : scaleY;\r\n    if (!isNaN(fak))\r\n      this.zoomFactor = fak;\r\n    //this._zoomInput.value = Math.round(this.zoomFactor * 100) + '%';\r\n  }\r\n\r\n  /* --- end IUiCommandHandler --- */\r\n\r\n  handleSelectAll() {\r\n    const selection = Array.from(this.rootDesignItem.children(true)).filter(x => !x.isEmptyTextNode);\r\n    const primary = this.instanceServiceContainer.selectionService.primarySelection;\r\n    if (primary) {\r\n      const idx = selection.indexOf(primary);\r\n      if (idx >= 0) {\r\n        selection.splice(idx, 1);\r\n        selection.unshift(primary);\r\n      }\r\n    }\r\n    this.instanceServiceContainer.selectionService.setSelectedElements(selection);\r\n  }\r\n\r\n  async handleCopyCommand() {\r\n    this._currentPasteOffset = this.pasteOffset;\r\n    this._lastCopiedPrimaryItem = this.instanceServiceContainer.selectionService.primarySelection;\r\n    await this.serviceContainer.copyPasteService.copyItems(this.instanceServiceContainer.selectionService.selectedElements);\r\n  }\r\n\r\n  async handlePasteCommand(disableRestoreOfPositions: boolean) {\r\n    const [designItems, positions] = await this.serviceContainer.copyPasteService.getPasteItems(this.serviceContainer, this.instanceServiceContainer);\r\n\r\n    let grp = this.rootDesignItem.openGroup(\"Insert\");\r\n    let lastCopiedPrimaryItemBackup = this._lastCopiedPrimaryItem;\r\n    let nextPasteOffset = this._currentPasteOffset + this.pasteOffset;\r\n    let pasteContainer = this.rootDesignItem;\r\n    let pCon = lastCopiedPrimaryItemBackup?.parent ?? this.instanceServiceContainer.selectionService.primarySelection;\r\n    while (pCon != null) {\r\n      const containerStyle = getComputedStyle(pCon.element);\r\n      let newContainerService: IPlacementService;\r\n      newContainerService = this.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(pCon, containerStyle));\r\n      if (newContainerService) {\r\n        if (newContainerService.canEnter(pCon, designItems)) {\r\n          pasteContainer = pCon;\r\n          break;\r\n        } else {\r\n          pCon = pCon.parent;\r\n          continue;\r\n        }\r\n      }\r\n    }\r\n\r\n    if (designItems) {\r\n      let containerPos = this.getNormalizedElementCoordinates(pasteContainer.element);\r\n      for (let i = 0; i < designItems.length; i++) {\r\n        let di = designItems[i];\r\n        let pos = positions ? positions[i] : null;\r\n        this.instanceServiceContainer.undoService.execute(new InsertAction(pasteContainer, pasteContainer.childCount, di));\r\n        if (!disableRestoreOfPositions && pos && di.nodeType == NodeType.Element) {\r\n          di.setStyle('left', (pos.x - containerPos.x + this._currentPasteOffset) + 'px');\r\n          di.setStyle('top', (pos.y - containerPos.y + this._currentPasteOffset) + 'px');\r\n        }\r\n      }\r\n\r\n      const intializationService = this.serviceContainer.intializationService;\r\n      if (intializationService) {\r\n        for (let di of designItems)\r\n          intializationService.init(di);\r\n      }\r\n      this.instanceServiceContainer.selectionService.setSelectedElements(designItems);\r\n    }\r\n    grp.commit();\r\n\r\n    this.snapLines.clearSnaplines();\r\n    this._lastCopiedPrimaryItem = lastCopiedPrimaryItemBackup;\r\n    this._currentPasteOffset = nextPasteOffset;\r\n  }\r\n\r\n  handleDeleteCommand() {\r\n    let items = this.instanceServiceContainer.selectionService.selectedElements;\r\n    this.serviceContainer.deletionService.removeItems(items);\r\n    this.instanceServiceContainer.selectionService.setSelectedElements(null);\r\n  }\r\n\r\n  initialize(serviceContainer: ServiceContainer) {\r\n    this.serviceContainer = serviceContainer;\r\n\r\n    this.instanceServiceContainer = new InstanceServiceContainer(this);\r\n    const undoService = this.serviceContainer.getLastService('undoService')\r\n    if (undoService)\r\n      this.instanceServiceContainer.register(\"undoService\", undoService(this));\r\n    const selectionService = this.serviceContainer.getLastService('selectionService')\r\n    if (selectionService) {\r\n      this.instanceServiceContainer.register(\"selectionService\", selectionService(this));\r\n      this.instanceServiceContainer.selectionService.onSelectionChanged.on(() => {\r\n        this._lastCopiedPrimaryItem = null;\r\n        this._currentPasteOffset = this.pasteOffset;\r\n      });\r\n    }\r\n    const designItemDocumentPositionService = this.serviceContainer.getLastService('designItemDocumentPositionService')\r\n    if (designItemDocumentPositionService) {\r\n      this.instanceServiceContainer.register(\"designItemDocumentPositionService\", designItemDocumentPositionService(this));\r\n    }\r\n    if (this._useIframe) {\r\n      this.rootDesignItem = DesignItem.GetOrCreateDesignItem(this._iframe, this._iframe, this.serviceContainer, this.instanceServiceContainer);\r\n      requestAnimationFrame(() => {\r\n        this.rootDesignItem.updateChildrenFromNodesChildren();\r\n      });\r\n    } else {\r\n      this.rootDesignItem = DesignItem.GetOrCreateDesignItem(this._canvas, this._canvas, this.serviceContainer, this.instanceServiceContainer);\r\n    }\r\n\r\n    const stylesheetService = this.serviceContainer.getLastService('stylesheetService')\r\n    if (stylesheetService) {\r\n      const instance = stylesheetService(this);\r\n      this.instanceServiceContainer.register(\"stylesheetService\", instance);\r\n      this.instanceServiceContainer.stylesheetService.stylesheetChanged.on((ss) => {\r\n        if (this.instanceServiceContainer.collaborationService?.isApplyingRemoteChanges) {\r\n          this.applyAllStyles();\r\n          return;\r\n        }\r\n\r\n        if (ss.changeSource != 'undo') {\r\n          const ssca = new StylesheetChangedAction(this.instanceServiceContainer.stylesheetService, ss.name, ss.newStyle, ss.oldStyle);\r\n          this.instanceServiceContainer.undoService.execute(ssca);\r\n          this.applyAllStyles();\r\n        } else {\r\n          this.applyAllStyles();\r\n        }\r\n      });\r\n      this.instanceServiceContainer.stylesheetService.stylesheetsChanged.on(() => {\r\n        this.applyAllStyles();\r\n      });\r\n    }\r\n\r\n    const collaborationService = this.serviceContainer.getLastService('collaborationService' as never) as ((designerCanvas: IDesignerCanvas) => any) | null\r\n    if (collaborationService) {\r\n      const instance = collaborationService(this);\r\n      this.instanceServiceContainer.collaborationService = instance;\r\n      (this.instanceServiceContainer as any).register(\"collaborationService\", instance);\r\n    }\r\n\r\n    if (serviceContainer.instanceServiceContainerCreatedCallbacks?.length)\r\n      serviceContainer.instanceServiceContainerCreatedCallbacks.forEach(x => x(this.instanceServiceContainer));\r\n\r\n    this.extensionManager = new ExtensionManager(this);\r\n    if (this.instanceServiceContainer.collaborationService) {\r\n      this.instanceServiceContainer.collaborationService.onPeersChanged.on(event => {\r\n        if (event.source === 'remote')\r\n          this.extensionManager.refreshAllAppliedExtentions();\r\n      });\r\n      this.instanceServiceContainer.collaborationService.onCommentsChanged.on(() => {\r\n        this.extensionManager.refreshAllAppliedExtentions();\r\n      });\r\n    }\r\n    this.overlayLayer = new OverlayLayerView(serviceContainer);\r\n    this.overlayLayer.style.pointerEvents = 'none';\r\n    this.overlayLayer.style.setProperty('margin', '0', 'important');\r\n    this.overlayLayer.style.setProperty('padding', '0', 'important');\r\n    this.overlayLayer.style.setProperty('border', 'none', 'important');\r\n    this.clickOverlay.appendChild(this.overlayLayer);\r\n    this.snapLines = new Snaplines(this.overlayLayer);\r\n    this.snapLines.initialize(this.rootDesignItem);\r\n\r\n    if (this.serviceContainer.designerPointerExtensions)\r\n      for (let pe of this.serviceContainer.designerPointerExtensions) {\r\n        if (!this._pointerextensions)\r\n          this._pointerextensions = [];\r\n        this._pointerextensions.push(pe.getExtension(this));\r\n      }\r\n\r\n    if (this.children) {\r\n      let children = this.children;\r\n      if (this.children.length == 1 && this.children[0] instanceof HTMLSlotElement) {\r\n        children = <any>this.children[0].assignedElements();\r\n      }\r\n      if (children?.length > 0) {\r\n        const parser = this.serviceContainer.getLastServiceWhere('htmlParserService', x => x.constructor == DefaultHtmlParserService) as DefaultHtmlParserService;\r\n        this.addDesignItems(parser.createDesignItems(children, this.serviceContainer, this.instanceServiceContainer));\r\n      }\r\n    }\r\n\r\n    if (!this.serviceContainer.options.zoomDesignerBackground) {\r\n      requestAnimationFrame(() => {\r\n        this._resizeBackgroundGrid();\r\n      });\r\n    }\r\n\r\n    if (this.isConnected)\r\n      this.extensionManager.connected();\r\n  }\r\n\r\n  connectedCallback() {\r\n    if (!this._firstConnect) {\r\n      this._firstConnect = true;\r\n      this._touchGestureHelper = TouchGestureHelper.addTouchEvents(this.clickOverlay);\r\n      this.clickOverlay.addEventListener(EventNames.PointerDown, this._pointerEventHandler);\r\n      this.clickOverlay.addEventListener(EventNames.PointerMove, this._pointerEventHandler);\r\n      this.clickOverlay.addEventListener(EventNames.PointerMove, this._pointerEventHandlerCapture, true);\r\n      this.clickOverlay.addEventListener(EventNames.PointerUp, this._pointerEventHandler);\r\n      this.clickOverlay.addEventListener(EventNames.DragEnter, event => this._onDragEnter(event));\r\n      this.clickOverlay.addEventListener(EventNames.DragLeave, event => this._onDragLeave(event));\r\n      this.clickOverlay.addEventListener(EventNames.DragOver, event => this._onDragOver(event));\r\n      this.clickOverlay.addEventListener(EventNames.Drop, event => this._onDrop(event));\r\n      this.clickOverlay.addEventListener(EventNames.KeyDown, this.onKeyDown);\r\n      this.clickOverlay.addEventListener(EventNames.KeyUp, this.onKeyUp);\r\n      this.clickOverlay.addEventListener(EventNames.DblClick, this._onDblClick, true);\r\n      this.clickOverlay.addEventListener(EventNames.Wheel, this._onWheel);\r\n      //@ts-ignore\r\n      this.clickOverlay.addEventListener('zoom', (e: CustomEvent) => {\r\n        this.zoomFactor = this.zoomFactor + (e.detail.diff / 10);\r\n      });\r\n      //@ts-ignore\r\n      this.clickOverlay.addEventListener('pan', (e: CustomEvent) => {\r\n        const newCanvasOffset = {\r\n          x: (this.canvasOffset.x) - e.detail.deltaX,\r\n          y: (this.canvasOffset.y) - e.detail.deltaY\r\n        }\r\n        this.canvasOffset = newCanvasOffset\r\n      });\r\n    }\r\n    if (this.extensionManager) {\r\n      this.extensionManager.connected();\r\n    }\r\n  }\r\n\r\n  disconnectedCallback() {\r\n    this.extensionManager.disconnected();\r\n  }\r\n\r\n  private _zoomFactorChanged() {\r\n    this._canvasContainer.style.bottom = this._outercanvas2.offsetHeight >= this._canvasContainer.offsetHeight ? '0' : '';\r\n    this._canvasContainer.style.right = this._outercanvas2.offsetWidth >= this._canvasContainer.offsetWidth ? '0' : '';\r\n    this._updateTransform();\r\n    this.fillCalculationrects();\r\n    this.onZoomFactorChanged.emit(this._zoomFactor);\r\n    if (!this.serviceContainer.options.zoomDesignerBackground)\r\n      this._resizeBackgroundGrid();\r\n  }\r\n\r\n  private _resizeBackgroundGrid() {\r\n    const backgroundGridSize = 10;\r\n    const backgroundGridFactor = backgroundGridSize * 100 * 2;\r\n    let canvasWidth = this.canvas.getBoundingClientRect().width;\r\n    let backgroundGridZoom = backgroundGridFactor / canvasWidth;\r\n    this.canvas.style.backgroundSize = backgroundGridZoom.toString() + '%';\r\n  }\r\n\r\n  private _updateTransform() {\r\n    this._scaleFactor = this._zoomFactor;\r\n    if (this._useIframe) {\r\n      let offX = this._canvasOffset.x;\r\n      let offY = this._canvasOffset.y;\r\n      this._iframe.contentWindow.scrollTo(offX * -1, offY * -1);\r\n      offX += this._iframe.contentWindow.scrollX;\r\n      offY += this._iframe.contentWindow.scrollY;\r\n      this._canvasContainer.style.transform = 'scale(' + this._zoomFactor + ') translate(' + offX + 'px, ' + offY + 'px)'\r\n\r\n    } else {\r\n      this._canvasContainer.style.transform = 'scale(' + this._zoomFactor + ') translate(' + (isNaN(this._canvasOffset.x) ? '0' : this._canvasOffset.x) + 'px, ' + (isNaN(this._canvasOffset.y) ? '0' : this._canvasOffset.y) + 'px)';\r\n    }\r\n    this._canvasContainer.style.transformOrigin = '0 0';\r\n    this.overlayLayer.style.transform = this._canvasContainer.style.transform;\r\n    this.overlayLayer.style.transformOrigin = '0 0';\r\n    this.snapLines.clearSnaplines();\r\n  }\r\n\r\n  public setDesignItems(designItems: IDesignItem[]) {\r\n    this.instanceServiceContainer.undoService.clearTransactionstackIfNotEmpty();\r\n    const setItemsAction = new SetDesignItemsAction(designItems, [...this.rootDesignItem.children()]);\r\n    this.instanceServiceContainer.undoService.execute(setItemsAction);\r\n  }\r\n\r\n  public _internalSetDesignItems(designItems: IDesignItem[]) {\r\n    this.fillCalculationrects();\r\n    this.extensionManager.removeAllExtensions();\r\n    this.overlayLayer.removeAllOverlays();\r\n    DomHelper.removeAllChildnodes(this.overlayLayer);\r\n    for (let i of [...this.rootDesignItem.children()])\r\n      this.rootDesignItem._removeChildInternal(i);\r\n    this.addDesignItems(designItems);\r\n\r\n    this.lazyTriggerReparseDocumentStylesheets();\r\n\r\n    this.instanceServiceContainer.onContentChanged.emit([{ changeType: 'parsed' }]);\r\n    (<SelectionService>this.instanceServiceContainer.selectionService)._withoutUndoSetSelectedElements(null);\r\n    setTimeout(() => this.extensionManager.refreshAllAppliedExtentions(), 50);\r\n  }\r\n\r\n  reparseTimeout: NodeJS.Timeout | null;\r\n  public lazyTriggerReparseDocumentStylesheets() {\r\n    if (this.reparseTimeout) {\r\n      clearTimeout(this.reparseTimeout);\r\n    }\r\n    this.reparseTimeout = setTimeout(async () => {\r\n      await this.reparseDocumentStylesheets();\r\n      clearTimeout(this.reparseTimeout);\r\n    }, 20);\r\n  }\r\n\r\n  private async reparseDocumentStylesheets() {\r\n    if (this.instanceServiceContainer.stylesheetService) {\r\n      const styleElements = this.rootDesignItem.querySelectorAll('style');\r\n      let i = 1;\r\n      const intStyleSheets: IDocumentStylesheet[] = [...styleElements].map(x => ({ name: '&lt;style&gt; #' + (x.id ? x.id + '(' + i++ + ')' : i++), content: DesignItem.GetDesignItem(x).content, designItem: DesignItem.GetDesignItem(x) }));\r\n      await this.instanceServiceContainer.stylesheetService.setDocumentStylesheets(intStyleSheets);\r\n    }\r\n  }\r\n\r\n  public addDesignItems(designItems: IDesignItem[]) {\r\n    if (designItems) {\r\n      this.rootDesignItem._insertChildsInternal(designItems);\r\n    }\r\n\r\n    const intializationService = this.serviceContainer.intializationService;\r\n    if (intializationService) {\r\n      for (let di of designItems)\r\n        intializationService.init(di);\r\n    }\r\n\r\n    this.snapLines.clearSnaplines();\r\n  }\r\n\r\n  private _onDragEnter(event: DragEvent) {\r\n    this.fillCalculationrects();\r\n    event.preventDefault();\r\n\r\n    const hasTransferDataBindingObject = event.dataTransfer.types.indexOf(dragDropFormatNameBindingObject) >= 0;\r\n    if (hasTransferDataBindingObject) {\r\n      const ddService = this.serviceContainer.bindableObjectDragDropService;\r\n      if (ddService) {\r\n        const el = this.getElementAtPoint({ x: event.x, y: event.y });\r\n        if (this._lastDdElement != el) {\r\n          ddService.dragLeave(this, event, this._lastDdElement);\r\n          ddService.dragEnter(this, event, el);\r\n          this._lastDdElement = el;\r\n        }\r\n      } else {\r\n        this._lastDdElement = null;\r\n      }\r\n    } else {\r\n      this._lastDdElement = null;\r\n\r\n      const dragDropService = this.serviceContainer.dragDropService;\r\n      if (dragDropService) {\r\n        dragDropService.dragEnter(this, event);\r\n      }\r\n    }\r\n  }\r\n\r\n  private _onDragLeave(event: DragEvent) {\r\n    this.fillCalculationrects();\r\n    event.preventDefault();\r\n    this._canvas.classList.remove('dragFileActive');\r\n\r\n    const dragDropService = this.serviceContainer.dragDropService;\r\n    if (dragDropService) {\r\n      dragDropService.dragLeave(this, event);\r\n    }\r\n  }\r\n\r\n  private _lastDdElement = null;\r\n  private _onDragOver(event: DragEvent) {\r\n    event.preventDefault();\r\n\r\n    this.fillCalculationrects();\r\n\r\n    if (event.dataTransfer.types.length > 0 && event.dataTransfer.types[0] == 'Files') {\r\n      const ddService = this.serviceContainer.externalDragDropService;\r\n      if (ddService) {\r\n        const effect = ddService.dragOver(this, event);\r\n        event.dataTransfer.dropEffect = effect;\r\n        if (effect !== 'none')\r\n          this._canvas.classList.add('dragFileActive');\r\n      }\r\n    } else {\r\n      const hasTransferDataBindingObject = event.dataTransfer.types.indexOf(dragDropFormatNameBindingObject) >= 0;\r\n      if (hasTransferDataBindingObject) {\r\n        const bindableDdService = this.serviceContainer.bindableObjectDragDropService;\r\n        if (bindableDdService) {\r\n          const el = this.getElementAtPoint({ x: event.x, y: event.y });\r\n          if (this._lastDdElement != el) {\r\n            bindableDdService.dragLeave(this, event, this._lastDdElement);\r\n            bindableDdService.dragEnter(this, event, el);\r\n            this._lastDdElement = el;\r\n          }\r\n          const effect = bindableDdService.dragOver(this, event, el);\r\n          event.dataTransfer.dropEffect = effect;\r\n        }\r\n      } else {\r\n        const dragDropService = this.serviceContainer.dragDropService;\r\n        if (dragDropService) {\r\n          dragDropService.dragOver(this, event);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n\r\n  private async _onDrop(event: DragEvent) {\r\n    this.serviceContainer.globalContext.tool = <ITool>this.serviceContainer.designerTools.get(NamedTools.Pointer);\r\n    this._lastDdElement = null;\r\n    event.preventDefault();\r\n    this._canvas.classList.remove('dragFileActive');\r\n\r\n    this.fillCalculationrects();\r\n\r\n    if (event.dataTransfer.files?.length > 0) {\r\n      const ddService = this.serviceContainer.externalDragDropService;\r\n      if (ddService) {\r\n        ddService.drop(this, event);\r\n      }\r\n    }\r\n    else {\r\n      const transferDataBindingObject = event.dataTransfer.getData(dragDropFormatNameBindingObject)\r\n      if (transferDataBindingObject) {\r\n        const bo = JSON.parse(transferDataBindingObject);\r\n        const ddService = this.serviceContainer.bindableObjectDragDropService;\r\n        if (ddService) {\r\n          const el = this.getElementAtPoint({ x: event.x, y: event.y });\r\n          ddService.drop(this, event, bo, el);\r\n        }\r\n      }\r\n      else {\r\n        const dragDropService = this.serviceContainer.dragDropService;\r\n        if (dragDropService) {\r\n          this.fillCalculationrects();\r\n          dragDropService.drop(this, event);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  public showDesignItemContextMenu(designItem: IDesignItem, event: MouseEvent) {\r\n    const mnuItems: IContextMenuItem[] = [];\r\n    for (let cme of this.serviceContainer.designerContextMenuExtensions) {\r\n      if (cme.shouldProvideContextmenu(event, this, designItem, 'designer')) {\r\n        mnuItems.push(...cme.provideContextMenuItems(event, this, designItem, 'designer'));\r\n      }\r\n    }\r\n    let ctxMenu = new ContextMenu(mnuItems, null)\r\n    ctxMenu.display(event);\r\n\r\n    return ctxMenu;\r\n  }\r\n\r\n  private _onDblClick(event: MouseEvent) {\r\n    event.preventDefault();\r\n    if (event.target === this.overlayLayer)\r\n      return;\r\n    if (event.altKey)\r\n      return;\r\n    if (this.serviceContainer.globalContext.tool == null || this.serviceContainer.globalContext.tool === this.serviceContainer.designerTools.get(NamedTools.Pointer)) {\r\n      this.extensionManager.removeExtension(this.instanceServiceContainer.selectionService.primarySelection, ExtensionType.PrimarySelectionRefreshed);\r\n      this.extensionManager.applyExtension(this.instanceServiceContainer.selectionService.primarySelection, ExtensionType.Doubleclick, event);\r\n    }\r\n  }\r\n\r\n  private _searchShowOverlay() {\r\n    if (this.serviceContainer.searchService) {\r\n      let divElement = this._getDomElement('node-projects-designer-search-container') as HTMLDivElement;\r\n      divElement.style.display = '';\r\n      this._getDomElement<HTMLInputElement>('node-projects-designer-search-input').focus();\r\n    }\r\n  }\r\n  private _searchHideOverlay() {\r\n    let divElement = this._getDomElement('node-projects-designer-search-container') as HTMLDivElement;\r\n    divElement.style.display = 'none';\r\n  }\r\n\r\n  private async _searchRun() {\r\n    let input = this._getDomElement<HTMLInputElement>('node-projects-designer-search-input');\r\n    this._getDomElement<HTMLSpanElement>('node-projects-designer-search-result').innerHTML = \"0 selected\";\r\n    if (input.value != \"\") {\r\n      const result = await this.serviceContainer.searchService.search(this, input.value);\r\n      if (result?.length > 0) {\r\n        this.instanceServiceContainer.selectionService.setSelectedElements(result.map(r => r.designItem));\r\n        this._getDomElement<HTMLSpanElement>('node-projects-designer-search-result').innerHTML = result.length.toString() + \" selected\";\r\n      }\r\n    }\r\n  }\r\n\r\n  private onKeyUp(event: KeyboardEvent) {\r\n    if (this._ignoreEvent === event)\r\n      return;\r\n\r\n    if (this.activeTool?.keyboardEventHandler) {\r\n      this.activeTool.keyboardEventHandler(this, event, null);\r\n      if (event.defaultPrevented)\r\n        return;\r\n    }\r\n\r\n    if (this._moveGroup) {\r\n      this._moveGroup.commit()\r\n      this._moveGroup = null;\r\n    }\r\n\r\n    event.preventDefault();\r\n  }\r\n\r\n  private onKeyDown(event: KeyboardEvent) {\r\n    if (this._ignoreEvent === event)\r\n      return;\r\n\r\n    if (this.activeTool?.keyboardEventHandler) {\r\n      this.activeTool.keyboardEventHandler(this, event, null);\r\n      if (event.defaultPrevented)\r\n        return;\r\n    }\r\n\r\n    if (hasCommandKey(event) && event.key === 'z' && !event.shiftKey)\r\n      this.executeCommand({ type: CommandType.undo, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'z' && event.shiftKey)\r\n      this.executeCommand({ type: CommandType.redo, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'y')\r\n      this.executeCommand({ type: CommandType.redo, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'a')\r\n      this.executeCommand({ type: CommandType.selectAll, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'c')\r\n      this.executeCommand({ type: CommandType.copy, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'v')\r\n      this.executeCommand({ type: CommandType.paste, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'x')\r\n      this.executeCommand({ type: CommandType.cut, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n    else if (hasCommandKey(event) && event.key === 'f')\r\n      this._searchShowOverlay();\r\n    else {\r\n      let primarySelection = this.instanceServiceContainer.selectionService.primarySelection;\r\n      if (!primarySelection) {\r\n        return;\r\n      }\r\n\r\n      let moveOffset = 1;\r\n      if (event.shiftKey)\r\n        moveOffset = 10;\r\n      switch (event.key) {\r\n        case 'Delete':\r\n        case 'Backspace':\r\n          this.executeCommand({ type: CommandType.delete, metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey });\r\n          break;\r\n        case 'ArrowDown':\r\n        case 'ArrowUp':\r\n        case 'ArrowLeft':\r\n        case 'ArrowRight':\r\n          {\r\n            if (!this._moveGroup)\r\n              this._moveGroup = this.rootDesignItem.openGroup(\"move items\");\r\n\r\n            let offset = { x: 0, y: 0 };\r\n            if (event.key == 'ArrowDown')\r\n              offset.y = -moveOffset;\r\n            if (event.key == 'ArrowUp')\r\n              offset.y = moveOffset;\r\n            if (event.key == 'ArrowRight')\r\n              offset.x = -moveOffset;\r\n            if (event.key == 'ArrowLeft')\r\n              offset.x = moveOffset;\r\n\r\n            for (let x of filterChildPlaceItems(this.instanceServiceContainer.selectionService.selectedElements)) {\r\n              const containerStyle = getComputedStyle(x.parent.element);\r\n              x.serviceContainer.getLastServiceWhere('containerService', y => y.serviceForContainer(x.parent, containerStyle)).moveElements([x], offset, false);\r\n            };\r\n          }\r\n          break;\r\n      }\r\n    }\r\n\r\n    event.preventDefault();\r\n  }\r\n\r\n  /**\r\n   * Converts the Event x/y coordinates, to the mouse position on the canvas\r\n   */\r\n  public getNormalizedEventCoordinates(event: MouseEvent): IPoint {\r\n    const offsetOfOuterX = (event.clientX - this.outerRect.x) / this.zoomFactor;\r\n    const offsetOfCanvasX = this.containerBoundingRect.x - this.outerRect.x;\r\n\r\n    const offsetOfOuterY = (event.clientY - this.outerRect.y) / this.zoomFactor;\r\n    const offsetOfCanvasY = this.containerBoundingRect.y - this.outerRect.y;\r\n\r\n    return {\r\n      x: offsetOfOuterX - offsetOfCanvasX / this.zoomFactor,\r\n      y: offsetOfOuterY - offsetOfCanvasY / this.zoomFactor\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Converts the Event x/y coordinates, to the mouse position in the viewport\r\n   */\r\n  public getViewportCoordinates(event: MouseEvent): IPoint {\r\n    return {\r\n      x: (event.clientX - this.outerRect.x),\r\n      y: (event.clientY - this.outerRect.y)\r\n    };\r\n  }\r\n\r\n  public getNormalizedTextNodeCoordinates(element: Text, ignoreScalefactor?: boolean): IRect {\r\n    let range = document.createRange();\r\n    range.selectNodeContents(element);\r\n    let targetRect = range.getBoundingClientRect();\r\n    const offset = this.containerOffset;\r\n    return { x: offset.x + (targetRect.x - this.containerBoundingRect.x) / (ignoreScalefactor ? 1 : this.scaleFactor), y: offset.y + (targetRect.y - this.containerBoundingRect.y) / (ignoreScalefactor ? 1 : this.scaleFactor), width: targetRect.width / (ignoreScalefactor ? 1 : this.scaleFactor), height: targetRect.height / (ignoreScalefactor ? 1 : this.scaleFactor) };\r\n  }\r\n\r\n  public getNormalizedElementCoordinates(element: Element, ignoreScalefactor?: boolean): IRect {\r\n    if (element.nodeType == NodeType.TextNode) {\r\n      return this.getNormalizedTextNodeCoordinates(<Text><any>element, ignoreScalefactor)\r\n    }\r\n    const targetRect = getBoundingClientRectAlsoForDisplayContents(element);\r\n    const offset = this.containerOffset;\r\n    return { x: offset.x + (targetRect.x - this.containerBoundingRect.x) / (ignoreScalefactor ? 1 : this.scaleFactor), y: offset.y + (targetRect.y - this.containerBoundingRect.y) / (ignoreScalefactor ? 1 : this.scaleFactor), width: targetRect.width / (ignoreScalefactor ? 1 : this.scaleFactor), height: targetRect.height / (ignoreScalefactor ? 1 : this.scaleFactor) };\r\n  }\r\n\r\n  public getNormalizedElementCoordinatesAndRealSizes(element: Element): IRect & { realWidth: number, realHeight: number } {\r\n    let ret = this.getNormalizedElementCoordinates(element);\r\n    const st = getComputedStyle(element);\r\n    let realWidth = ret.width;\r\n    let realHeight = ret.height;\r\n    if (st.boxSizing != 'border-box') {\r\n      realWidth = realWidth - (parseFloat(st.borderLeft) + parseFloat(st.paddingLeft) + parseFloat(st.paddingRight) + parseFloat(st.borderRight));\r\n      realHeight = realHeight - (parseFloat(st.borderTop) + parseFloat(st.paddingTop) + parseFloat(st.paddingBottom) + parseFloat(st.borderBottom));\r\n    }\r\n    return { ...ret, realWidth, realHeight };\r\n  }\r\n\r\n  public getNormalizedOffsetInElement(event: MouseEvent, element: Element): IPoint {\r\n    const normEvt = this.getNormalizedEventCoordinates(event);\r\n    const normEl = this.getNormalizedElementCoordinates(element);\r\n    return { x: normEvt.x - normEl.x, y: normEvt.y - normEl.y };\r\n  }\r\n\r\n  private transformPoint(point: IPoint) {\r\n    if (this._useIframe) {\r\n      const rect = this._canvasContainer.getBoundingClientRect();\r\n      return { x: point.x - rect.x, y: point.y - rect.y };\r\n    }\r\n    return point;\r\n  }\r\n\r\n  public elementsFromPoint(x: number, y: number): Element[] {\r\n    let retVal: Element[] = [];\r\n    const t = this.transformPoint({ x, y });\r\n    const elements = this._canvasShadowRoot.elementsFromPoint(t.x, t.y);\r\n    for (let e of elements) {\r\n      if (e.getRootNode() !== this._canvasShadowRoot)\r\n        continue;\r\n      retVal.push(e);\r\n    }\r\n    return retVal;\r\n  }\r\n\r\n  public getElementAtPoint(point: IPoint, ignoreElementCallback?: (element: HTMLElement) => boolean) {\r\n    const t = this.transformPoint(point);\r\n    const elements = this._canvasShadowRoot.elementsFromPoint(t.x, t.y);\r\n    let currentElement: HTMLElement = null;\r\n\r\n    for (let i = 0; i < elements.length; i++) {\r\n      currentElement = <HTMLElement>elements[i];\r\n      if (ignoreElementCallback && ignoreElementCallback(currentElement)) {\r\n        currentElement = null;\r\n        continue;\r\n      }\r\n      if (currentElement.getRootNode() !== this._canvasShadowRoot) {\r\n        currentElement = null;\r\n        continue;\r\n      }\r\n      if (!this.instanceServiceContainer.designContext.extensionOptions.selectUnhitableElements && DesignItem.GetDesignItem(currentElement).getStyleFromSheetOrLocal('pointer-events') == 'none') {\r\n        currentElement = null;\r\n        continue;\r\n      }\r\n      break;\r\n    }\r\n\r\n    return currentElement;\r\n  }\r\n\r\n  public getDesignItemById(id: string) {\r\n    return DesignItem.GetDesignItem(this._canvasShadowRoot.getElementById(id));\r\n  }\r\n\r\n  public querySelectorDesignItem(selector: string) {\r\n    return DesignItem.GetDesignItem(this._canvasShadowRoot.querySelector(selector));\r\n  }\r\n\r\n  public querySelectorAllDesignItems(selector: string) {\r\n    return [...this._canvasShadowRoot.querySelectorAll(selector)].map(x => DesignItem.GetDesignItem(x));\r\n  }\r\n\r\n  private _hoverElement: Element;\r\n  public showHoverExtension(element: Element, event: Event) {\r\n    const currentDesignItem = DesignItem.GetOrCreateDesignItem(element, element, this.serviceContainer, this.instanceServiceContainer);\r\n    if (this._lastHoverDesignItem != currentDesignItem) {\r\n      if (this._lastHoverDesignItem)\r\n        this.extensionManager.removeExtension(this._lastHoverDesignItem, ExtensionType.MouseOver);\r\n      if (currentDesignItem && currentDesignItem != this.rootDesignItem && (!element.parentNode || DomHelper.getHost(element.parentNode) !== this.overlayLayer))\r\n        this.extensionManager.applyExtension(currentDesignItem, ExtensionType.MouseOver, event);\r\n      this._lastHoverDesignItem = currentDesignItem;\r\n    }\r\n\r\n    if (this.instanceServiceContainer.designContext.extensionOptions.simulateHoverOnHover && this._hoverElement !== element) {\r\n      let el = this._hoverElement;\r\n      while (el && el !== this._canvas) {\r\n        el.removeAttribute(forceHoverAttributeName);\r\n        el = el.parentElement;\r\n      }\r\n      this._hoverElement = null;\r\n      if (element) {\r\n        if (element.nodeType == NodeType.TextNode)\r\n          element = element.parentElement;\r\n        el = element;\r\n        while (el && el !== this._canvas) {\r\n          el.setAttribute(forceHoverAttributeName, '');\r\n          el = el.parentElement;\r\n        }\r\n        this._hoverElement = element;\r\n      }\r\n    }\r\n  }\r\n\r\n  private _onWheel(event: WheelEvent) {\r\n    let el = this.getElementAtPoint({ x: event.clientX, y: event.clientY });\r\n    while (el) {\r\n      const cs = getComputedStyle(el);\r\n      if (cs.overflowY === 'scroll' || cs.overflowY === 'auto') {\r\n        const target = el;\r\n        if (target.scrollBy)\r\n          target.scrollBy(event.deltaX, event.deltaY);\r\n        else {\r\n          target.scrollLeft += event.deltaX;\r\n          target.scrollTop += event.deltaY;\r\n        }\r\n\r\n        event.stopPropagation();\r\n        break;\r\n      }\r\n      el = el.parentElement;\r\n    }\r\n  }\r\n\r\n  private _pointerEventHandlerCapture(event: PointerEvent, forceElement: Node = null) {\r\n    this.fillCalculationrects();\r\n    if (this._pointerextensions) {\r\n      for (let pe of this._pointerextensions)\r\n        pe.refresh(event);\r\n    }\r\n  }\r\n\r\n  private _pointerEventHandler(event: PointerEvent, forceElement: Node = null) {\r\n    if (this._ignoreEvent === event)\r\n      return;\r\n\r\n    if (!this.serviceContainer)\r\n      return;\r\n\r\n    if (this._touchGestureHelper.multitouchEventActive)\r\n      return;\r\n\r\n    let currentElement: Node;\r\n    if (forceElement)\r\n      currentElement = forceElement;\r\n    else {\r\n      currentElement = this.serviceContainer.elementAtPointService.getElementAtPoint(this, { x: event.x, y: event.y });\r\n      if (!currentElement) {\r\n        currentElement = this._canvas;\r\n      } else if (this._enableSelectTextNodesOnClick) {\r\n        const norm = this.getNormalizedEventCoordinates(event);\r\n        for (let n of currentElement.childNodes) {\r\n          if (n.nodeType == NodeType.TextNode) {\r\n            let nc = this.getNormalizedElementCoordinates(<Element>n);\r\n            if (nc.x <= norm.x && nc.x + nc.width >= norm.x && nc.y <= norm.y && nc.y + nc.height >= norm.y) {\r\n              currentElement = n;\r\n              break;\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    if (this._activeTool) {\r\n      this._activeTool.pointerEventHandler(this, event, <Element>currentElement);\r\n      return;\r\n    }\r\n\r\n    this.clickOverlay.style.cursor = this._canvas.style.cursor;\r\n\r\n    const currentDesignItem = DesignItem.GetOrCreateDesignItem(currentElement, currentElement, this.serviceContainer, this.instanceServiceContainer);\r\n    this.showHoverExtension(currentDesignItem.element, event);\r\n\r\n    //TODO: needed ??\r\n    if (currentElement && DomHelper.getHost(currentElement.parentNode) === this.overlayLayer) {\r\n      currentElement = this.instanceServiceContainer.selectionService.primarySelection?.element ?? this._canvas;\r\n    }\r\n\r\n    let tool = this.serviceContainer.globalContext.tool ?? <ITool>this.serviceContainer.designerTools.get(NamedTools.Pointer);\r\n\r\n    tool.pointerEventHandler(this, event, <Element>currentElement);\r\n    this._canvas.style.cursor = tool.cursor;\r\n  }\r\n\r\n  public captureActiveTool(tool: ITool) {\r\n    this._activeTool = tool;\r\n  }\r\n\r\n  public releaseActiveTool() {\r\n    this._activeTool = null;\r\n  }\r\n\r\n  public get activeTool() {\r\n    return this.serviceContainer.globalContext.tool;\r\n  }\r\n\r\n  public fillCalculationrects() {\r\n    this.containerBoundingRect = this._canvasContainer.getBoundingClientRect();\r\n    this.outerRect = this._outercanvas2.getBoundingClientRect();\r\n  }\r\n\r\n  public removeOverlay(element: SVGGraphicsElement) {\r\n    this.overlayLayer.removeOverlay(element);\r\n  }\r\n\r\n  public zoomOntoRectangle(startPoint: IPoint, endPoint: IPoint) {\r\n    let rect: IRect = {\r\n      x: startPoint.x < endPoint.x ? startPoint.x : endPoint.x,\r\n      y: startPoint.y < endPoint.y ? startPoint.y : endPoint.y,\r\n      width: Math.abs(startPoint.x - endPoint.x),\r\n      height: Math.abs(startPoint.y - endPoint.y),\r\n    }\r\n\r\n    let zFactorWidth = this.outerRect.width / rect.width;\r\n    let zFactorHeight = this.outerRect.height / rect.height;\r\n\r\n    let zoomFactor = zFactorWidth >= zFactorHeight ? zFactorHeight : zFactorWidth;\r\n\r\n    let rectCenter: IPoint = {\r\n      x: (rect.width / 2) + rect.x,\r\n      y: (rect.height / 2) + rect.y\r\n    }\r\n\r\n    this.zoomPoint(rectCenter, zoomFactor);\r\n  }\r\n\r\n  public zoomPoint(canvasPoint: IPoint, newZoom: number) {\r\n    this.zoomFactor = newZoom;\r\n\r\n    const newCanvasOffset = {\r\n      x: -(canvasPoint.x) + this.outerRect.width / this.zoomFactor / 2,\r\n      y: -(canvasPoint.y) + this.outerRect.height / this.zoomFactor / 2\r\n    }\r\n\r\n    this.canvasOffset = newCanvasOffset;\r\n  }\r\n\r\n  private zoomConvertEventToViewPortCoordinates(point: IPoint): IPoint {\r\n    const offsetOfCanvasX = this.containerBoundingRect.x - this.outerRect.x;\r\n    const offsetOfCanvasY = this.containerBoundingRect.y - this.outerRect.y;\r\n\r\n    return {\r\n      x: (point.x + offsetOfCanvasX / this.zoomFactor) * this.zoomFactor,\r\n      y: (point.y + offsetOfCanvasY / this.zoomFactor) * this.zoomFactor\r\n    };\r\n  }\r\n\r\n  public zoomTowardsPoint(canvasPoint: IPoint, newZoom: number) {\r\n    const scaleChange = newZoom / this.zoomFactor;\r\n\r\n    const point = this.zoomConvertEventToViewPortCoordinates(canvasPoint);\r\n\r\n    const newCanvasOffset = {\r\n      x: -(point.x * (scaleChange - 1) + scaleChange * -this.canvasOffsetUnzoomed.x),\r\n      y: -(point.y * (scaleChange - 1) + scaleChange * -this.canvasOffsetUnzoomed.y)\r\n    }\r\n\r\n    this.zoomFactor = newZoom;\r\n    this.canvasOffsetUnzoomed = newCanvasOffset;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-designer-canvas', DesignerCanvas);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/designerView.ts",
    "content": "import { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { css, DomHelper, html, BaseCustomWebComponentConstructorAppend } from '@node-projects/base-custom-webcomponent';\r\nimport { IUiCommandHandler } from '../../../commandHandling/IUiCommandHandler.js';\r\nimport { IUiCommand } from '../../../commandHandling/IUiCommand.js';\r\nimport { DesignerCanvas } from \"./designerCanvas.js\";\r\nimport { DomConverter } from './DomConverter.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DefaultHtmlParserService } from '../../services/htmlParserService/DefaultHtmlParserService.js';\r\nimport { EventNames } from '../../../enums/EventNames.js';\r\nimport { PlainScrollbar } from '../../controls/PlainScrollbar.js';\r\nimport { DesignerToolbar } from './tools/toolBar/DesignerToolbar.js';\r\n\r\n\r\nexport class DesignerView extends BaseCustomWebComponentConstructorAppend implements IUiCommandHandler {\r\n  private _sVert: PlainScrollbar;\r\n  private _sHor: PlainScrollbar;\r\n\r\n  public get serviceContainer(): ServiceContainer {\r\n    return this._designerCanvas.serviceContainer;\r\n  }\r\n  public set serviceContainer(value: ServiceContainer) {\r\n    this._designerCanvas.serviceContainer = value;\r\n  }\r\n\r\n  public get instanceServiceContainer(): InstanceServiceContainer {\r\n    return this._designerCanvas.instanceServiceContainer;\r\n  }\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    this._designerCanvas.instanceServiceContainer = value;\r\n  }\r\n\r\n  private _designerCanvas: DesignerCanvas;\r\n\r\n  public get designerCanvas() {\r\n    return this._designerCanvas;\r\n  }\r\n\r\n  get readOnly() {\r\n    return this._designerCanvas.readOnly;\r\n  }\r\n  set readOnly(v) {\r\n    this._designerCanvas.readOnly = v;\r\n  }\r\n\r\n  private _zoomInput: HTMLInputElement;\r\n  private _lowertoolbar: HTMLDivElement;\r\n  private _toolbar: DesignerToolbar;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      box-sizing: border-box;\r\n      width: 100%;\r\n      position: relative;\r\n      transform: translateZ(0);\r\n      overflow: hidden;\r\n    }\r\n    * {\r\n      touch-action: none;\r\n    }\r\n    #lowertoolbar {\r\n      height: 16px;\r\n      background: #787f82;\r\n      display: flex;\r\n      bottom: 0;\r\n      position: absolute;\r\n      font-size: 12px;\r\n      width: 100%;\r\n    }\r\n    input {\r\n      width: 40px;\r\n      height: 16px;\r\n      padding: 0;\r\n      border: 0;\r\n      font-size: 12px;\r\n      text-align: center;\r\n      margin-right: 1px;\r\n    }\r\n    .toolbar-control {\r\n      min-width: 16px;\r\n      height: 16px;\r\n      display: block;\r\n      margin-right: 1px;\r\n      cursor: default;\r\n      display: flex;\r\n      justify-content: center;\r\n      align-items: center;\r\n    }\r\n    .selected {\r\n      background-color: deepskyblue;\r\n    }\r\n    .toolbar-control:hover {\r\n      background-color:rgba(164,206,249,.6);\r\n    }\r\n    #outer {\r\n      user-select: none;\r\n      display: flex;\r\n      flex-direction: column;\r\n      width: 100%;\r\n      height: 100%;\r\n    }\r\n    #canvas {\r\n      left: 24px;\r\n      width: calc(100% - 24px - 16px);\r\n      height: calc(100% - 32px);\r\n    }\r\n\r\n    #tool-bar {\r\n      width: 24px;\r\n      height: calc(100% - 32px);\r\n      position: absolute;\r\n      background-color: lightgray;      \r\n    }\r\n  \r\n    .zoom-in {\r\n      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAn9JREFUeNqkU11o01AUTtI06U/6tzHWbnNVS+k67FAG/gwfZEOcCrpNEBwEH2Q+Otqxh+5VEaHYV18UwQd9sk4Q+6CUKogbtSoM10qftJS2iGuT/iRpshvvDZu4DHHggY+TnHu+795zz7m4qqrYxfA6pjerlcHMZvMQQRCL8Hccoh+iBJECAMQEQci3Wk2MxP5iqgqmJUmIB3xkdTRoKgQG6Wr1JxBX1oTQx3UxCdcjMO2ZJiCK4g4y3HUIACV+bsL5dSRAWfwe636z0WBzWEFzn5vku5xyIZGsx2UF5AhEaDYbO8Dz3KLPi1X9B2iTy0pRiDx5/Z2bJgmGwDH8WMhuDxzEf6A8TYDneT3GR4JMzcVQThNlcCtANaM48pICutodxXTkkI1HeVslSPor6B/wWL7B3Z1n2KR3O3h05rkDOkd2eYoSxM025PVrAgAQeoFSuSLI3Q66jpJNRsIWPLvM5JJTTUkGGsqVtgJ5JU3AaLTqBVKZT9xh7yDT2Gh05G4bNQBzGETkBbkMVAxbzdadMJbSBDzCHXgKgKGZQJCp0IP32dnEaKhd6nOZAay7mXk6UUGeIHDDh88NKb0qumjaHiN391/1GjtrJ6ZPSb33H9eJYomuTJ601Qf7SKVYVlqplZblxRvOThrtEaC28qSOPAwncCYSWbzJsrO3XP4br9KZa1fTmcaOSTQYLDFFaeURh/yDfLynp/fCwkI0yrKX79ZqG49sUr2A44a3u+d08/fXVhfAeZ8vcDocjs6z7KV7xeL3JzBcwPZgmoAkScGlpdvzc3NXHuZyXxIwlMX2aJoAfFkvx8aGnRzH5WEpr7cXuerKPwVw1Lb/sV8CDACbf0U37X3NqwAAAABJRU5ErkJggg==);\r\n    }\r\n    .zoom-out {\r\n      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmVJREFUeNqkU01oE0EUnv3Nf0xsS01jEzWUNCUplh7U4kEiVVEQqyBYWDxor5am5JBeFRGCuXpRBA8KHqIHMYISohdTYlQo2kguaghJUNvd/O1udjvr7NKWZHuw4IMHM/v9zHu8t5iiKCAYDAKi/xQ4MHEdbIXFYgUmk2kUx/EouoZRulGWUaYhhHGe5wutVhOQKtk3/RToQ1HgjCjyCb+PrE0GjEW/x1Cr/YFCdoUPffwqpBAeQbRnmoEgCD1i9OoohHLi7EnHt3E/bfb0m/pwDGBeF04P77Mpex1SMZliE5IMV3FV0Gw2erJe56I+L6iNHDQYnRaaVsXb5uh8JGS3+w9hv1SeVkG9Xtd3EB4PDBecVtplpAmHKMMesN2R2Ymg7fer9PfwZgui3sC932X+gV53nGZSXj2Yf36B5oWNNtK5NQMIcT2nXKnyUt8eA6uSUf1YNyhKsFmptmWkK2sGFGXRG6Rzn7jDXo+1sdboSBSJEd0gVABYzrMOpEtrBi7+DqoCAnUn1JTo0IP3+dnkZKhdHnKaIEUSXRPCiA+fG2JmWXAaDPY4uXP+ipfqrBybOSEO3n/M4qWyoXrmuE30DJHWUkVupbMt44u3nJ2k7BGotAqkTjyGNvBiJBK9yTCzt5wjN15ncteuZnKNnk0kCHNcllsFVUN2iY8ODAyeX1yMxRjm8t319bVHNpEtYhjxbseago3t0+YU4Dmfzz+9sBCbZ5hL90qln0/Q5yLYRWgGoigGlpZuz8/NXXm4uvolqY4a7DI0A/RnvZyaGnNwHFdArbzZArla9p8GmDq2/4m/AgwATHQSD48kJDUAAAAASUVORK5CYII=);\r\n    }\r\n    .snap-grid {\r\n      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAblJREFUeNqkUjtLA0EQnntFyKNKJdiFgIlGC5G0FlaaIsYgiKAgKIKdjbV/wM7GImBhI6JFMFWKK3yBimBAhJDuUEgimMSYS0hunbncHRtFjLjw7X7z7ezszM4KjDEQRRG+jnlEgvYAAgLAJpoT1tYdA9gzAAqLuC/DD4N1EZC87v1IYnZ0MBj0k/6Szw/nTs7GO+8f62gWBCuDHTRG+ABxgGScsd3JlYWlJogu9TSjkT41NzM0AEbr5uDocJmxLTv3CCJpgbRkAKcWpq14vP7scVqrVKsbBOKktayS7BL4RzB5uxvAo7++SfV6vYnmBenESaM9/qCESFt4pPUJJzrlLpe/vQ9pTeex8A0EQcggwILJo9EohMPhNVW9ZqFQaJvsWCzWozGuCxJ3gck1TYNGo+Hy+XxQKpXalUoFisUi1Go1RyM/uwtZLsADYkxRlGnMZLXT6QQkSSqglsIV0HY0XddTdgkqV4LD6YPJsgwYzLSJEygQraz70YDvRg9Hh3O8DdptM1uTEwzDMDnvTHVfWfy+D8592W4Jt1wJfXOnC0h8XMw/cbuEHOKZc/iNX9qC2cb/jE8BBgAvhdOb37HVsgAAAABJRU5ErkJggg==);\r\n    }\r\n    .snap-guide {\r\n      background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNqkU7tKA1EQnbs3CnlVqbQNARMjFilS+AnZQhIbW0ER/AA/wlYbhXR2wUKx0iKNYGMKDViEdEIgieBujGvCPpyz7sK6iWLIwJk798zsuXsfIxzHoXksAqcoij+vMbYq7MoszGxaEB3wtODlH3i5E5uojck210RCgq7SJie4MC0TsdO1cim/lMmkwHdarZWni+t16/1jz/FElJBAGc5imLzyakXNG0IuVo+OnwHE4JCzgisG7BJu/I3CQjyRuq1dvWi6vg8gBofcOHgGAZO+AFv88/VNDofDEcd3IBCD43ycfhEowY28Sazfnzh1cKMggWsUQvi4waiqKuVyud16/d7JZrOHxWJxKudMuQV3C81mk3RdX0wmk9Tr9UxN06jb7dJgMPjBwQRUAu/AtWg0SpZl7TDSUso2/1UVNWHOMIwJARzWBhcQwMXu1oJb9Ufbtsk0zYktPLrvgD8E/mWeYoNxxlj2xoZ3qI2/YrePQs10PksjTRPozCog5m3nLwEGABrLzseuHT6IAAAAAElFTkSuQmCC);\r\n    }\r\n    \r\n    .bottom-scroll {\r\n      width: calc(100% - 16px);\r\n      position: absolute;\r\n      bottom: 16px;\r\n      height: 16px;\r\n      box-sizing: border-box;\r\n      z-index: 1;\r\n    }\r\n    .right-scroll {\r\n      height: calc(100% - 32px);\r\n      position: absolute;\r\n      right: 0;\r\n      top: 0;\r\n      width: 16px;\r\n      box-sizing: border-box;\r\n      z-index: 1;\r\n    }\r\n    .bottom-right {\r\n      width: 16px;\r\n      height: 16px;\r\n      bottom: 16px;\r\n      right: 0;\r\n      position: absolute;\r\n      background: #f0f0f0;\r\n    }`;\r\n\r\n  static override readonly template = html`\r\n    <div id=\"outer\">\r\n      <node-projects-plain-scrollbar id=\"s-hor\" value=\"0.5\" class=\"bottom-scroll\"></node-projects-plain-scrollbar>\r\n      <node-projects-plain-scrollbar id=\"s-vert\" value=\"0.5\" orientation=\"vertical\" class=\"right-scroll\">\r\n      </node-projects-plain-scrollbar>\r\n      <div class=\"bottom-right\"></div>\r\n      <div id=\"lowertoolbar\">\r\n        <input id=\"zoomInput\" type=\"text\" value=\"100%\">\r\n        <div title=\"decrease zoom\" id=\"zoomIncrease\" class=\"toolbar-control zoom-in\"></div>\r\n        <div title=\"increase zoom\" id=\"zoomDecrease\" class=\"toolbar-control zoom-out\"></div>\r\n        <div title=\"reset zoom\" id=\"zoomReset\" class=\"toolbar-control\"\r\n          style=\"width: 16px; height: 16px; font-size: 14px; display: flex; align-items: center; justify-content: center;\">1\r\n        </div>\r\n        <div title=\"zoom to fit\" id=\"zoomFit\" class=\"toolbar-control\"\r\n          style=\"width: 16px; height: 16px; font-size: 8px; display: flex; align-items: center; justify-content: center;\">\r\n          100%</div>\r\n        <div title=\"snap to grid\" id=\"alignGrid\" class=\"toolbar-control snap-grid\"></div>\r\n        <div title=\"snap to elements\" id=\"alignSnap\" class=\"toolbar-control snap-guide\"></div>\r\n      </div>\r\n    </div>`;\r\n\r\n  constructor(useIframe: boolean = false) {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this._sVert = this._getDomElement<PlainScrollbar>('s-vert');\r\n    this._sHor = this._getDomElement<PlainScrollbar>('s-hor');\r\n\r\n    const outer = this._getDomElement<DesignerCanvas>('outer');\r\n    this._designerCanvas = new DesignerCanvas(useIframe);\r\n    this._designerCanvas.id = \"canvas\";\r\n    this._designerCanvas.appendChild(document.createElement(\"slot\"));\r\n    outer.insertAdjacentElement('afterbegin', this._designerCanvas);\r\n\r\n    this._toolbar = new DesignerToolbar();\r\n    this._toolbar.id = 'tool-bar';\r\n    this._sVert.insertAdjacentElement('afterend', this._toolbar);\r\n\r\n    this._designerCanvas.onZoomFactorChanged.on(() => {\r\n      this._zoomInput.value = Math.round(this._designerCanvas.zoomFactor * 100) + '%';\r\n\r\n      const pos = this.designerCanvas.canvasOffset;\r\n\r\n      const w = this.designerCanvas.designerOffsetWidth > this.designerCanvas.offsetWidth ? this.designerCanvas.designerOffsetWidth : this.designerCanvas.offsetWidth;\r\n      const h = this.designerCanvas.designerOffsetHeight > this.designerCanvas.offsetHeight ? this.designerCanvas.designerOffsetHeight : this.designerCanvas.offsetHeight;\r\n      this._sHor.value = (pos.x / (-2 * w)) + 0.5;\r\n      this._sVert.value = (pos.y / (-2 * h)) + 0.5;\r\n    });\r\n\r\n    this._zoomInput = this._getDomElement<HTMLInputElement>('zoomInput');\r\n    this._zoomInput.onkeydown = (e) => {\r\n      if (e.key == 'Enter')\r\n        this._designerCanvas.zoomFactor = parseFloat(this._zoomInput.value) / 100;\r\n    }\r\n    this._zoomInput.onblur = () => {\r\n      this._designerCanvas.zoomFactor = parseFloat(this._zoomInput.value) / 100;\r\n    }\r\n    this._zoomInput.onclick = this._zoomInput.select\r\n    let zoomIncrease = this._getDomElement<HTMLDivElement>('zoomIncrease');\r\n    zoomIncrease.onclick = () => {\r\n      const w = this.designerCanvas.designerOffsetWidth > this.designerCanvas.offsetWidth ? this.designerCanvas.designerOffsetWidth : this.designerCanvas.offsetWidth;\r\n      const h = this.designerCanvas.designerOffsetHeight > this.designerCanvas.offsetHeight ? this.designerCanvas.designerOffsetHeight : this.designerCanvas.offsetHeight;\r\n      if (this._designerCanvas.zoomFactor > 0.1)\r\n        this._designerCanvas.zoomPoint({ x: w / 2, y: h / 2 }, this._designerCanvas.zoomFactor + 0.1)\r\n      else\r\n        this._designerCanvas.zoomPoint({ x: w / 2, y: h / 2 }, this._designerCanvas.zoomFactor + 0.01)\r\n    }\r\n    let zoomDecrease = this._getDomElement<HTMLDivElement>('zoomDecrease');\r\n    zoomDecrease.onclick = () => {\r\n      const w = this.designerCanvas.designerOffsetWidth > this.designerCanvas.offsetWidth ? this.designerCanvas.designerOffsetWidth : this.designerCanvas.offsetWidth;\r\n      const h = this.designerCanvas.designerOffsetHeight > this.designerCanvas.offsetHeight ? this.designerCanvas.designerOffsetHeight : this.designerCanvas.offsetHeight;\r\n\r\n      if (this._designerCanvas.zoomFactor > 0.11)\r\n        this._designerCanvas.zoomPoint({ x: w / 2, y: h / 2 }, this._designerCanvas.zoomFactor - 0.1)\r\n      else\r\n        this._designerCanvas.zoomPoint({ x: w / 2, y: h / 2 }, this._designerCanvas.zoomFactor - 0.01)\r\n\r\n      if (this._designerCanvas.zoomFactor < 0.001)\r\n        this._designerCanvas.zoomPoint({ x: w / 2, y: h / 2 }, 0.001)\r\n    }\r\n    let zoomReset = this._getDomElement<HTMLDivElement>('zoomReset');\r\n    zoomReset.onclick = () => {\r\n      this.zoomReset();\r\n    }\r\n    let zoomFit = this._getDomElement<HTMLDivElement>('zoomFit');\r\n    zoomFit.onclick = () => {\r\n      this.zoomToFit();\r\n    }\r\n    this.addEventListener(EventNames.Wheel, event => this._onWheel(event));\r\n\r\n    let alignSnap = this._getDomElement<HTMLDivElement>('alignSnap');\r\n    alignSnap.onclick = () => { this._designerCanvas.alignOnSnap = !this._designerCanvas.alignOnSnap; alignSnap.style.backgroundColor = this._designerCanvas.alignOnSnap ? 'deepskyblue' : ''; }\r\n    alignSnap.style.backgroundColor = this._designerCanvas.alignOnSnap ? 'deepskyblue' : '';\r\n    alignSnap.oncontextmenu = e => { e.preventDefault(); }\r\n    let alignGrid = this._getDomElement<HTMLDivElement>('alignGrid');\r\n    alignGrid.onclick = () => { this._designerCanvas.alignOnGrid = !this._designerCanvas.alignOnGrid; alignGrid.style.backgroundColor = this._designerCanvas.alignOnGrid ? 'deepskyblue' : ''; }\r\n    alignGrid.style.backgroundColor = this._designerCanvas.alignOnGrid ? 'deepskyblue' : '';\r\n    alignGrid.oncontextmenu = e => {\r\n      e.preventDefault();\r\n      let res = prompt(\"raster size\", this.designerCanvas.gridSize.toString());\r\n      if (res) {\r\n        let r = parseInt(res);\r\n        if (r > 0)\r\n          this.designerCanvas.gridSize = r;\r\n      }\r\n    }\r\n\r\n    this._lowertoolbar = this._getDomElement<HTMLDivElement>('lowertoolbar');\r\n\r\n    this._sVert.addEventListener('scrollbar-input', (e) => this._onScrollbar(e));\r\n    this._sHor.addEventListener('scrollbar-input', (e) => this._onScrollbar(e));\r\n  }\r\n\r\n  public zoomReset() {\r\n    this._designerCanvas.canvasOffset = { x: 0, y: 0 };\r\n    this._designerCanvas.zoomFactor = 1;\r\n    this._sVert.value = 0.5;\r\n    this._sHor.value = 0.5;\r\n    this._zoomInput.value = Math.round(this._designerCanvas.zoomFactor * 100) + '%';\r\n  }\r\n\r\n  public zoomToFit() {\r\n    this._designerCanvas.zoomToFit()\r\n  }\r\n\r\n  private _onScrollbar(e) {\r\n    if (e?.detail == 'incrementLarge')\r\n      e.target.value += 0.25;\r\n    else if (e?.detail == 'decrementLarge')\r\n      e.target.value -= 0.25;\r\n    else if (e?.detail == 'incrementSmall')\r\n      e.target.value += 0.05;\r\n    else if (e?.detail == 'decrementSmall')\r\n      e.target.value -= 0.05;\r\n    const w = this.designerCanvas.designerOffsetWidth > this.designerCanvas.offsetWidth ? this.designerCanvas.designerOffsetWidth : this.designerCanvas.offsetWidth;\r\n    const h = this.designerCanvas.designerOffsetHeight > this.designerCanvas.offsetHeight ? this.designerCanvas.designerOffsetHeight : this.designerCanvas.offsetHeight;\r\n    const x = w * (this._sHor.value - 0.5) * -2;\r\n    const y = h * (this._sVert.value - 0.5) * -2;\r\n    this.designerCanvas.canvasOffset = { x, y };\r\n  }\r\n\r\n  private _onWheel(event: WheelEvent) {\r\n    event.preventDefault();\r\n    if (event.ctrlKey) {\r\n      let zf = this._designerCanvas.zoomFactor;\r\n      const wheel = event.deltaY < 0 ? 1 : (-1);\r\n      zf *= Math.exp(wheel * 0.2);\r\n      if (zf < 0.02)\r\n        zf = 0.02;\r\n      const vp = this.designerCanvas.getNormalizedEventCoordinates(event)\r\n      this.designerCanvas.zoomTowardsPoint(vp, zf);\r\n    }\r\n    else if (event.shiftKey) {\r\n      this._sHor.value += event.deltaY / 10000;\r\n      this._onScrollbar(null);\r\n    }\r\n    else {\r\n      this._sVert.value += event.deltaY / 10000;\r\n      this._onScrollbar(null);\r\n      this._sHor.value += event.deltaX / 10000;\r\n      this._onScrollbar(null);\r\n    }\r\n  }\r\n\r\n  get designerWidth(): string {\r\n    return this._designerCanvas.designerWidth;\r\n  }\r\n  set designerWidth(value: string) {\r\n    this._designerCanvas.designerWidth = value;\r\n  }\r\n  get designerHeight(): string {\r\n    return this._designerCanvas.designerHeight;\r\n  }\r\n  set designerHeight(value: string) {\r\n    this._designerCanvas.designerHeight = value;\r\n  }\r\n\r\n  set additionalStyles(value: CSSStyleSheet[]) {\r\n    this._designerCanvas.additionalStyles = value;\r\n  }\r\n\r\n  public setDesignItems(designItems: IDesignItem[]) {\r\n    this._designerCanvas.setDesignItems(designItems);\r\n  }\r\n\r\n  /* --- start IUiCommandHandler --- */\r\n\r\n  async executeCommand(command: IUiCommand) {\r\n    this._designerCanvas.executeCommand(command);\r\n  }\r\n  canExecuteCommand(command: IUiCommand) {\r\n    return this._designerCanvas.canExecuteCommand(command);\r\n  }\r\n\r\n  /* --- end IUiCommandHandler --- */\r\n\r\n\r\n  initialize(serviceContainer: ServiceContainer) {\r\n    this.serviceContainer = serviceContainer;\r\n    this._designerCanvas.initialize(serviceContainer);\r\n    if (serviceContainer.designViewConfigButtons) {\r\n      for (let provider of serviceContainer.designViewConfigButtons) {\r\n        for (let btn of provider.provideButtons(this, this._designerCanvas))\r\n          this._lowertoolbar.appendChild(btn);\r\n      }\r\n    }\r\n    this._toolbar.initialize(this.serviceContainer, this);\r\n  }\r\n\r\n  public getDesignerHTML() {\n    if (this._designerCanvas.rootDesignItem.childCount > 0) {\n      return DomConverter.ConvertToString(Array.from(this._designerCanvas.rootDesignItem.children()), true, true);\n    }\n    if (this.serviceContainer.htmlWriterService.supportsRootItemWrite) {\n      return DomConverter.ConvertToString([this._designerCanvas.rootDesignItem], true, true);\n    }\n    return '';\n  }\n\r\n  public async parseDesignerHTML(html: string, disableUndo: boolean = false) {\r\n    const parserService = this.serviceContainer.htmlParserService;\r\n    if (!html) {\r\n      this._designerCanvas.overlayLayer.removeAllOverlays();\r\n      DomHelper.removeAllChildnodes(this._designerCanvas.overlayLayer);\r\n      this._designerCanvas.rootDesignItem.clearChildren();\r\n    }\r\n    else {\r\n      const designItems = await parserService.parse(html, this.serviceContainer, this.instanceServiceContainer, false);\r\n      if (disableUndo) {\r\n        this._designerCanvas._internalSetDesignItems(designItems);\r\n      } else {\r\n        this._designerCanvas.setDesignItems(designItems);\r\n      }\r\n    }\r\n  }\r\n\r\n  static wrapInDesigner(elements: HTMLCollection | HTMLElement[], serviceContainer: ServiceContainer): DesignerCanvas {\r\n    const designerCanvas = new DesignerCanvas();\r\n    designerCanvas.initialize(serviceContainer);\r\n    const parser = designerCanvas.serviceContainer.getLastServiceWhere('htmlParserService', x => x.constructor == DefaultHtmlParserService) as DefaultHtmlParserService;\r\n    designerCanvas.addDesignItems(parser.createDesignItems(elements, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer))\r\n    return designerCanvas;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-designer-view', DesignerView);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/AbstractExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { AbstractExtensionBase } from \"./AbstractExtensionBase.js\";\r\nimport { OverlayLayer } from './OverlayLayer.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\n\r\nexport type toolbarObject = SVGForeignObjectElement &\r\n{\r\n  updatePosition: (position: IPoint) => void,\r\n  getById: <T extends HTMLElement>(id: string) => T\r\n}\r\n\r\nexport abstract class AbstractExtension extends AbstractExtensionBase implements IDesignerExtension {\r\n  protected extendedItem: IDesignItem;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView)\r\n    this.extendedItem = extendedItem;\r\n  }\r\n\r\n  abstract extend(cache: Record<string | symbol, any>, event?: Event);\r\n  abstract refresh(cache: Record<string | symbol, any>, event?: Event);\r\n  abstract dispose();\r\n\r\n  remove() {\r\n    this.extensionManager.removeExtensionInstance(this.extendedItem, this);\r\n  }\r\n\r\n  createToolbar(template: HTMLTemplateElement, width: number, height: number, overlayLayer: OverlayLayer = OverlayLayer.Foreground) {\r\n    const element = <SVGGraphicsElement & {}>(<any>template.content.cloneNode(true));\r\n    element.querySelectorAll('*').forEach(x => {\r\n      (<HTMLElement>x).onpointerdown = (e) => {\r\n        this.designerCanvas.ignoreEvent(e);\r\n      }\r\n      if (x instanceof HTMLInputElement) {\r\n        x.addEventListener('keydown', (e) => {\r\n          this.designerCanvas.ignoreEvent(e);\r\n        }, { capture: true });\r\n        x.addEventListener('keyup', (e) => {\r\n          this.designerCanvas.ignoreEvent(e);\r\n        }, { capture: true });\r\n      }\r\n    });\r\n\r\n    const foreignObject = <toolbarObject><any>document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');\r\n    foreignObject.classList.add('svg-toolbar-container');\r\n    foreignObject.setAttribute('width', '' + width);\r\n    foreignObject.setAttribute('height', '' + height);\r\n    foreignObject.appendChild(element);\r\n\r\n    foreignObject.style.scale = '' + 1 / this.designerCanvas.zoomFactor;\r\n    foreignObject.style.transformBox = 'fill-box';\r\n\r\n    this._addOverlay(foreignObject, overlayLayer);\r\n\r\n    foreignObject.updatePosition = (position: IPoint) => {\r\n      foreignObject.style.scale = '' + 1 / this.designerCanvas.zoomFactor;\r\n      const rect = this.overlayLayerView.getBoundingClientRect();\r\n      const innerRect = foreignObject.children[0].getBoundingClientRect();\r\n\r\n      const scaleFactor = this.designerCanvas.scaleFactor;\r\n      const effectiveRectWidth = (rect.width / scaleFactor) - this.designerCanvas.canvasOffset.x * scaleFactor;\r\n      if (innerRect.width + (position.x * scaleFactor) > effectiveRectWidth) {\r\n        position.x = (effectiveRectWidth - innerRect.width) / scaleFactor;\r\n      }\r\n      if (position.x < -this.designerCanvas.canvasOffset.x) {\r\n        position.x = -this.designerCanvas.canvasOffset.x;\r\n      }\r\n      foreignObject.setAttribute('x', '' + position.x);\r\n      foreignObject.setAttribute('y', '' + position.y);\r\n    }\r\n    foreignObject.getById = <T>(id: string) => {\r\n      return <T>foreignObject.querySelector('#' + id);\r\n    }\r\n    return foreignObject;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/AbstractExtensionBase.ts",
    "content": "import { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayerView } from '../overlayLayerView.js';\r\nimport { OverlayLayer } from './OverlayLayer.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\n\r\nexport abstract class AbstractExtensionBase {\r\n  protected overlays: SVGElement[] = [];\r\n  protected overlayLayerView: OverlayLayerView;\r\n  protected extensionManager: IExtensionManager;\r\n  protected designerCanvas: IDesignerCanvas;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas) {\r\n    this.extensionManager = extensionManager;\r\n    this.designerCanvas = designerCanvas;\r\n\r\n    this.overlayLayerView = designerCanvas.overlayLayer;\r\n  }\r\n\r\n  private _backup;\r\n  protected _valuesHaveChanges(...values) {\r\n    if (this._backup == null) {\r\n      this._backup = values\r\n      return true;\r\n    }\r\n    for (let i = 0; i < values.length; i++) {\r\n      if (values[i] !== this._backup[i]) {\r\n        this._backup = values\r\n        return true;\r\n      }\r\n    }\r\n    return false;\r\n  }\r\n\r\n  protected _removeAllOverlays() {\r\n    for (let o of this.overlays) {\r\n      try {\r\n        this.overlayLayerView.removeOverlay(o);\r\n      }\r\n      catch (err) {\r\n        console.error(err);\r\n      }\r\n    }\r\n    this.overlays = [];\r\n  }\r\n\r\n  protected _addOverlay(element: SVGGraphicsElement, overlayLayer: OverlayLayer = OverlayLayer.Normal) {\r\n    this.overlayLayerView.addOverlay(this.constructor.name, element, overlayLayer);\r\n    this.overlays.push(element);\r\n  }\r\n\r\n  protected _drawGroup(className?: string, group?: SVGGElement, overlayLayer?: OverlayLayer) {\r\n    const newGroup = this.overlayLayerView.drawGroup(this.constructor.name, className, group, overlayLayer);\r\n    if (!group) {\r\n      this.overlays.push(newGroup);\r\n    }\r\n    return newGroup;\r\n  }\r\n\r\n  protected _drawLine(x1: number, y1: number, x2: number, y2: number, className?: string, line?: SVGLineElement, overlayLayer?: OverlayLayer) {\r\n    const newLine = this.overlayLayerView.drawLine(this.constructor.name, x1, y1, x2, y2, className, line, overlayLayer);\r\n    if (!line) {\r\n      this.overlays.push(newLine);\r\n    }\r\n    return newLine;\r\n  }\r\n\r\n  protected _drawCircle(x: number, y: number, radius: number, className?: string, circle?: SVGCircleElement, overlayLayer?: OverlayLayer) {\r\n    const newCircle = this.overlayLayerView.drawCircle(this.constructor.name, x, y, radius, className, circle, overlayLayer);\r\n    if (!circle) {\r\n      this.overlays.push(newCircle);\r\n    }\r\n    return newCircle;\r\n  }\r\n\r\n  protected _drawRect(x: number, y: number, w: number, h: number, className?: string, rect?: SVGRectElement, overlayLayer?: OverlayLayer) {\r\n    const newRect = this.overlayLayerView.drawRect(this.constructor.name, x, y, w, h, className, rect, overlayLayer);\r\n    if (!rect) {\r\n      this.overlays.push(newRect);\r\n    }\r\n    return newRect;\r\n  }\r\n\r\n  protected _drawComplexRect(points: [IPoint, IPoint, IPoint, IPoint], className?: string, rect?: SVGPathElement, overlayLayer?: OverlayLayer) {\r\n    const d = \"M\" + points.map(x => x.x + ',' + x.y).join(' ') + 'Z';\r\n    const newRect = this.overlayLayerView.drawPath(this.constructor.name, d, className, rect, overlayLayer);\r\n    if (!rect) {\r\n      this.overlays.push(newRect);\r\n    }\r\n    return newRect;\r\n  }\r\n\r\n  protected _drawPath(data: string, className?: string, path?: SVGPathElement, overlayLayer?: OverlayLayer) {\r\n    const newPath = this.overlayLayerView.drawPath(this.constructor.name, data, className, path, overlayLayer);\r\n    if (!path) {\r\n      this.overlays.push(newPath);\r\n    }\r\n    return newPath;\r\n  }\r\n\r\n  protected _drawText(text: string, x: number, y: number, className?: string, textEl?: SVGTextElement, overlayLayer?: OverlayLayer) {\r\n    const newText = this.overlayLayerView.drawText(this.constructor.name, text, x, y, className, textEl, overlayLayer);\r\n    if (!textEl) {\r\n      this.overlays.push(newText);\r\n    }\r\n    return newText;\r\n  }\r\n\r\n  protected _drawHTML(html: string | HTMLElement, x: number, y: number, w: number, h: number, className?: string, htmlObj?: SVGForeignObjectElement, overlayLayer?: OverlayLayer) {\r\n    const newHtml = this.overlayLayerView.drawHTML(this.constructor.name, html, x, y, w, h, className, htmlObj, overlayLayer);\r\n    if (!htmlObj) {\r\n      this.overlays.push(newHtml);\r\n    }\r\n    return newHtml;\r\n  }\r\n\r\n  protected _drawTextWithBackground(text: string, x: number, y: number, backgroundColor: string, className?: string, existingEls?: [SVGFilterElement, SVGFEFloodElement, SVGTextElement, SVGTextElement], overlayLayer?: OverlayLayer) {\r\n    const newEls = this.overlayLayerView.drawTextWithBackground(this.constructor.name, text, x, y, backgroundColor, className, existingEls, overlayLayer);\r\n    if (!existingEls) {\r\n      this.overlays.push(...newEls);\r\n    }\r\n    return newEls;\r\n  }\r\n\r\n  protected _drawTransformedRect(points: DOMQuad, className?: string, path?: SVGPathElement, overlayLayer?: OverlayLayer) {\r\n    let data = \"M\" + points.p1.x + \" \" + points.p1.y + \" L\" + points.p2.x + \" \" + points.p2.y + \" L\" + points.p3.x + \" \" + points.p3.y + \" L\" + points.p4.x + \" \" + points.p4.y + \"Z\";\r\n    const newPath = this.overlayLayerView.drawPath(this.constructor.name, data, className, path, overlayLayer);\r\n    if (!path) {\r\n      this.overlays.push(newPath);\r\n    }\r\n    return newPath;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/AltToEnterContainerExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from \"./OverlayLayer.js\";\r\n\r\nexport class AltToEnterContainerExtension extends AbstractExtension {\r\n\r\n  private _text: SVGTextElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  override refresh() {\r\n    const itemRect = this.designerCanvas.getNormalizedElementCoordinates(this.extendedItem.element);\r\n    this._text = this._drawText(\"Press ALT (or hold) to enter container\", itemRect.x + 5, itemRect.y + 12, 'svg-text-enter-container', this._text, OverlayLayer.Foreground);\r\n    this._text.style.fontSize = (14 / this.designerCanvas.scaleFactor) + 'px';\r\n    this._text.setAttribute('x', '' + (itemRect.x + 5 / this.designerCanvas.scaleFactor));\r\n    this._text.setAttribute('y', '' + (itemRect.y + 12 / this.designerCanvas.scaleFactor));\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/AltToEnterContainerExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { AltToEnterContainerExtension } from './AltToEnterContainerExtension.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class AltToEnterContainerExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new AltToEnterContainerExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-text-enter-container { stroke: none; fill: black; stroke-width: 1; font-weight:800; font-family: monospace; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/BasicStackedToolbarExtension.ts",
    "content": "import { assetsPath } from \"../../../../Constants.js\";\r\nimport { AbstractExtension, toolbarObject } from \"./AbstractExtension.js\";\r\nimport { IExtensionManager } from \"./IExtensionManger.js\";\r\nimport { IDesignerCanvas } from \"../IDesignerCanvas.js\";\r\nimport { IDesignItem } from \"../../../item/IDesignItem.js\";\r\nimport { ImageButtonListSelector } from \"../../../controls/ImageButtonListSelector.js\";\r\nimport { htmlAsString } from \"../../../helper/Helper.js\";\r\n\r\nexport const basicStackedToolbarExtensionOverlayOptionName = 'basicStackedToolbarExtensionShowOverlay';\r\n\r\nexport class BasicStackedToolbarExtension extends AbstractExtension {\r\n\r\n  protected static basicTemplate = htmlAsString`\r\n      <select title=\"position\" id=\"position\" style=\"pointer-events: auto; height: 24px; width: 80px; padding: 0; font-weight: 900; text-transform: uppercase; margin-left: 5px; margin-right: 10px;\">\r\n        <option>static</option>\r\n        <option>relative</option>\r\n        <option>absolute</option>\r\n        <option>fixed</option>\r\n      </select>\r\n      <node-projects-image-button-list-selector id=\"inline\" no-value-in-header property=\"inline\">\r\n        <img data-value=\"block\" title=\"block\" src=\"${assetsPath}images/display/block.svg\">\r\n        <img data-value=\"inline\" title=\"inline\" src=\"${assetsPath}images/display/inline.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <select title=\"display\" id=\"displayType\" style=\"pointer-events: auto; height: 24px; width: 80px; padding: 0; font-weight: 900; text-transform: uppercase; margin-left: 5px; margin-right: 10px;\">\r\n        <option>block</option>\r\n        <option>flex</option>\r\n        <option>grid</option>\r\n      </select>\r\n  `;\r\n\r\n  protected static toolBars: toolbarObject[] = [];\r\n\r\n  protected _toolbar: toolbarObject;\r\n  protected _size = { width: 220, height: 30 };\r\n\r\n  protected _display: string;\r\n  protected _inline: string;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event: MouseEvent) {\r\n    const cs = getComputedStyle(this.extendedItem.element);\r\n    this._display = cs.display.replace('inline-', '').replace('inline', 'block');\r\n    this._inline = cs.display.startsWith('inline') ? 'inline' : 'block';\r\n\r\n    //@ts-ignore\r\n    this._toolbar = this.createToolbar(this.constructor.template, this._size.width, this._size.height);\r\n    BasicStackedToolbarExtension.toolBars.push(this._toolbar);\r\n\r\n    const positionEl = this._toolbar.getById<HTMLSelectElement>('position');\r\n    if (positionEl) {\r\n      positionEl.value = cs.position;\r\n      positionEl.onchange = async () => {\r\n        await this.extendedItem.updateStyleInSheetOrLocalAsync('position',  positionEl.value);\r\n        this.extensionManager.reapplyAllAppliedExtentions([this.extendedItem]);\r\n      }\r\n    }\r\n\r\n    const displayTypeEl = this._toolbar.getById<HTMLSelectElement>('displayType');\r\n    if (displayTypeEl) {\r\n      displayTypeEl.value = this._display;\r\n      displayTypeEl.onchange = async () => {\r\n        this._display = displayTypeEl.value;\r\n        await this.updateDisplayValue();\r\n        this.extensionManager.reapplyAllAppliedExtentions([this.extendedItem]);\r\n      }\r\n    }\r\n\r\n    const inlineEl = this._toolbar.getById<ImageButtonListSelector>('inline');\r\n    if (inlineEl) {\r\n      inlineEl.value = this._inline;\r\n      inlineEl.addEventListener('value-changed', async () => {\r\n        this._inline = inlineEl.value;\r\n        if (this._inline && cs.position === 'absolute')\r\n          this.extendedItem.setStyle('position', 'static');\r\n        await this.updateDisplayValue();\r\n        this.extensionManager.reapplyAllAppliedExtentions([this.extendedItem]);\r\n      });\r\n    }\r\n  }\r\n\r\n  async updateDisplayValue() {\r\n    let v = (this._inline == 'inline' ? 'inline ' : '') + this._display;\r\n    if (v === 'inline block')\r\n      v = 'inline';\r\n    await this.extendedItem.updateStyleInSheetOrLocalAsync('display', v);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: MouseEvent) {\r\n    if (event) {\r\n      const pos = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n      let tbOffset = 0;\r\n      for (let i = 0; i < BasicStackedToolbarExtension.toolBars.length - 1; i++) {\r\n        if (BasicStackedToolbarExtension.toolBars[i] === this._toolbar)\r\n          break;\r\n        tbOffset += this._toolbar.children[0].getBoundingClientRect().height + 4;\r\n      }\r\n      this._toolbar.updatePosition({ x: (pos.x - (16 / this.designerCanvas.zoomFactor)), y: (pos.y - tbOffset - ((this._size.height + 14) / this.designerCanvas.zoomFactor)) });\r\n    }\r\n  }\r\n\r\n  protected _addStyleButton(styleAndControlName: string) {\r\n    const cs = getComputedStyle(this.extendedItem.element);\r\n    const ctl = this._toolbar.getById<ImageButtonListSelector>(styleAndControlName)\r\n    ctl.addEventListener('value-changed', async () => {\r\n      await this.extendedItem.updateStyleInSheetOrLocalAsync(styleAndControlName, ctl.value);\r\n    });\r\n    ctl.value = cs[styleAndControlName];\r\n  }\r\n\r\n  override dispose() {\r\n    BasicStackedToolbarExtension.toolBars.splice(BasicStackedToolbarExtension.toolBars.indexOf(this._toolbar), 1);\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/EditText/EditTextExtension.ts",
    "content": "import { html, Disposable } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension, toolbarObject } from \"../AbstractExtension.js\";\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { OverlayLayer } from \"../OverlayLayer.js\";\r\nimport { shadowrootGetSelection, wrapSelectionInSpans } from \"../../../../helper/SelectionHelper.js\";\r\nimport { FontPropertyEditor } from \"../../../../services/propertiesService/propertyEditors/FontPropertyEditor.js\";\r\nimport { getBoundingClientRectAlsoForDisplayContents } from \"../../../../helper/ElementHelper.js\";\r\n\r\nexport type handlesPointerEvent = { handlesPointerEvent(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element): boolean }\r\n\r\nexport class EditTextExtension extends AbstractExtension implements handlesPointerEvent {\r\n\r\n  private static template = html`\r\n    <div style=\"height: 100%; display: flex; gap: 2px; width: 100%;\">\r\n      <button data-command=\"font-weight\" data-command-parameter=\"800\" style=\"pointer-events: auto; height: 24px; width: 24px; padding: 0; font-weight: 900;\">b</button>\r\n      <button data-command=\"font-style\" data-command-parameter=\"italic\" style=\"pointer-events: auto; height: 24px; width: 24px; padding: 0;\"><em>i</em></button>\r\n      <button data-command=\"text-decoration\" data-command-parameter=\"underline\" style=\"pointer-events: auto; height: 24px; width: 24px; padding: 0;\"><ins>u</ins></button>\r\n      <button data-command=\"text-decoration\" data-command-parameter=\"line-through\" style=\"pointer-events: auto; height: 24px; width: 24px; padding: 0;\"><del>s</del></button>\r\n      <button data-command=\"text-decoration\" data-command-parameter=\"overline\" style=\"pointer-events: auto; height: 24px; width: 24px; padding: 0;\"><span style=\"text-decoration: overline\">o</span></button>\r\n      <select data-command=\"fontSize\" style=\"pointer-events: auto; height: 24px; width: 60px; padding: 0;\">\r\n        <option>8px</option>\r\n        <option>9px</option>\r\n        <option>10px</option>\r\n        <option>11px</option>\r\n        <option>12px</option>\r\n        <option>14px</option>\r\n        <option>16px</option>\r\n        <option>18px</option>\r\n        <option>20px</option>\r\n        <option>24px</option>\r\n        <option>28px</option>\r\n        <option>32px</option>\r\n        <option>36px</option>\r\n      </select>\r\n      <select id=\"fontFamily\" data-command=\"font-family\" style=\"pointer-events: auto; height: 24px; width: 90px; padding: 0;\">\r\n        \r\n      </select>\r\n    </div>\r\n  `;\r\n\r\n  private _path!: SVGPathElement;\r\n  private _toolbar!: toolbarObject;\r\n  private _selectionChangedListener?: Disposable;\r\n  private _toolbarStateAnimationFrame?: number;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._keyDown = this._keyDown.bind(this);\r\n    this._scheduleToolbarStateRefresh = this._scheduleToolbarStateRefresh.bind(this);\r\n  }\r\n\r\n  private _keyDown(e: KeyboardEvent) {\r\n    if (e.key === 'Escape') {\r\n      this.dispose();\r\n    }\r\n  }\r\n\r\n  override extend() {\r\n    //TODO: -> check what to do with extensions, do not loose edit on mouse click,...\r\n    //maybe use a html edit framework\r\n    this.extendedItem.instanceServiceContainer.selectionService.clearSelectedElements();\r\n    this.extendedItem.removeDesignerAttributesAndStylesFromChildren();\r\n    window.addEventListener('keydown', this._keyDown, true);\r\n    //@ts-ignore\r\n    this.extendedItem.editContent();\r\n\r\n    this._selectionChangedListener = this.extendedItem.instanceServiceContainer.selectionService.onSelectionChanged.on(() => {\r\n      this.commitchanges();\r\n      this.extensionManager.removeExtensionInstance(this.extendedItem, this);\r\n    });\r\n\r\n    (<HTMLElement>this.extendedItem.element).focus();\r\n\r\n    let itemRect = getBoundingClientRectAlsoForDisplayContents(this.extendedItem.element);\r\n\r\n    this._toolbar = this.createToolbar(EditTextExtension.template, 300, 24);\r\n    this._toolbar.updatePosition({ x: (itemRect.x - this.designerCanvas.containerBoundingRect.x) / this.designerCanvas.scaleFactor, y: ((itemRect.y - this.designerCanvas.containerBoundingRect.y) / this.designerCanvas.scaleFactor - 36) });\r\n\r\n    FontPropertyEditor.addFontsToSelect(this._toolbar.getById<HTMLSelectElement>('fontFamily'));\r\n    this._toolbar.querySelectorAll('button').forEach(x => x.onpointerdown = (e) => {\r\n      const command = x.dataset['command'];\r\n      if (command)\r\n        this._formatSelection(command, x.dataset['commandParameter']);\r\n    });\r\n    this._toolbar.querySelectorAll('select').forEach(x => x.onchange = () => {\r\n      const command = x.dataset['command'];\r\n      if (command)\r\n        this._formatSelection(command, x.value);\r\n    });\r\n\r\n    document.addEventListener('selectionchange', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.addEventListener('keyup', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.addEventListener('pointerup', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.addEventListener('input', this._scheduleToolbarStateRefresh, true);\r\n\r\n    //TODO - nice way to disable click overlay\r\n    this.designerCanvas.clickOverlay.style.pointerEvents = 'none';\r\n\r\n    //overlay to detect click outside\r\n    this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n    this._path.setAttribute('class', 'svg-edit-text-clickoutside');\r\n    this._path.setAttribute('fill-rule', 'evenodd');\r\n    this._path.style.pointerEvents = 'auto';\r\n    this._path.onpointerdown = (e) => {\r\n      this.designerCanvas.ignoreEvent(e);\r\n      this.commitchanges();\r\n      this.extensionManager.removeExtensionInstance(this.extendedItem, this);\r\n    }\r\n    this._addOverlay(this._path, OverlayLayer.Background);\r\n    this.refresh();\r\n    this._refreshToolbarState();\r\n  }\r\n\r\n  override refresh() {\r\n    const p = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    if (this._valuesHaveChanges(this.designerCanvas.containerBoundingRect.width, this.designerCanvas.containerBoundingRect.height, this.designerCanvas.scaleFactor, p.p1.x, p.p1.y, p.p2.x, p.p2.y, p.p3.x, p.p3.y, p.p4.x, p.p4.y)) {\r\n      let outsideRect = { width: this.designerCanvas.containerBoundingRect.width / this.designerCanvas.scaleFactor, height: this.designerCanvas.containerBoundingRect.height / this.designerCanvas.scaleFactor };\r\n      let data = \"M0 0 L\" + outsideRect.width + \" 0 L\" + outsideRect.width + ' ' + outsideRect.height + \" L0 \" + outsideRect.height + \" Z \";\r\n      data += \"M\" + [p.p1, p.p2, p.p3, p.p4].map(x => x.x + ',' + x.y).join(' ') + 'Z ';\r\n      this._path.setAttribute(\"d\", data);\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    window.removeEventListener('keydown', this._keyDown, true);\r\n    document.removeEventListener('selectionchange', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.removeEventListener('keyup', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.removeEventListener('pointerup', this._scheduleToolbarStateRefresh, true);\r\n    this.extendedItem.element.removeEventListener('input', this._scheduleToolbarStateRefresh, true);\r\n    if (this._toolbarStateAnimationFrame != null)\r\n      cancelAnimationFrame(this._toolbarStateAnimationFrame);\r\n    this._selectionChangedListener?.dispose();\r\n    this._removeAllOverlays();\r\n    this.extendedItem.editContentFinish();\r\n    this.designerCanvas.clickOverlay.style.pointerEvents = 'auto';\r\n  }\r\n\r\n  commitchanges() {\r\n    this._removeAllOverlays();\r\n\r\n    this.extendedItem.element.normalize();\r\n    this._cleanupFormattingMarkup(this.extendedItem.element);\r\n    this.extendedItem.element.normalize();\r\n    let stop = false;\r\n    outer:\r\n    while (!stop) {\r\n      for (let e of this.extendedItem.element.querySelectorAll('*')) {\r\n        if (e.childNodes.length == 0) {\r\n          e.remove();\r\n          continue outer;\r\n        }\r\n      }\r\n      stop = true;\r\n    }\r\n    this._cleanupFormattingMarkup(this.extendedItem.element);\r\n    this.extendedItem.element.normalize();\r\n\r\n    const newHTML = this.extendedItem.element.innerHTML;\r\n\r\n    this.extendedItem.editContentFinish();\r\n    this.extendedItem.innerHTML = newHTML;\r\n\r\n    this.designerCanvas.clickOverlay.style.pointerEvents = 'auto';\r\n  }\r\n\r\n  private _cleanupFormattingMarkup(root: Element) {\r\n    let changed = false;\r\n    do {\r\n      changed = this._cleanupFormattingNode(root);\r\n      root.normalize();\r\n    } while (changed);\r\n  }\r\n\r\n  private _cleanupFormattingNode(node: Node): boolean {\r\n    let changed = false;\r\n    for (const child of Array.from(node.childNodes)) {\r\n      changed = this._cleanupFormattingNode(child) || changed;\r\n    }\r\n\r\n    if (node instanceof HTMLElement && node.localName === 'span') {\r\n      const span = node as HTMLSpanElement;\r\n      if (this._isRedundantSpan(span)) {\r\n        this._unwrapElement(span);\r\n        return true;\r\n      }\r\n\r\n      const parent = span.parentElement;\r\n      if (parent instanceof HTMLElement && parent.localName === 'span' && this._haveSameAttributes(parent, span)) {\r\n        this._unwrapElement(span);\r\n        return true;\r\n      }\r\n    }\r\n\r\n    return this._mergeAdjacentSpans(node) || changed;\r\n  }\r\n\r\n  private _mergeAdjacentSpans(node: Node): boolean {\r\n    let changed = false;\r\n    let current = node.firstChild;\r\n    while (current) {\r\n      if (current instanceof HTMLElement && current.localName === 'span') {\r\n        let next = current.nextSibling;\r\n        while (next instanceof HTMLElement && next.localName === 'span' && this._haveSameAttributes(current, next)) {\r\n          while (next.firstChild) {\r\n            current.appendChild(next.firstChild);\r\n          }\r\n          next.remove();\r\n          changed = true;\r\n          next = current.nextSibling;\r\n        }\r\n      }\r\n      current = current.nextSibling;\r\n    }\r\n    return changed;\r\n  }\r\n\r\n  private _isRedundantSpan(span: HTMLSpanElement): boolean {\r\n    return this._getAttributeSignature(span) === '';\r\n  }\r\n\r\n  private _haveSameAttributes(left: HTMLSpanElement, right: HTMLSpanElement): boolean {\r\n    return this._getAttributeSignature(left) === this._getAttributeSignature(right);\r\n  }\r\n\r\n  private _getAttributeSignature(element: Element): string {\r\n    return Array.from(element.attributes)\r\n      .filter(attribute => attribute.name !== 'style' || attribute.value.trim() !== '')\r\n      .sort((a, b) => a.name.localeCompare(b.name))\r\n      .map(attribute => `${attribute.name}=${attribute.value}`)\r\n      .join(';');\r\n  }\r\n\r\n  private _unwrapElement(element: Element) {\r\n    const parent = element.parentNode;\r\n    if (!parent)\r\n      return;\r\n\r\n    while (element.firstChild) {\r\n      parent.insertBefore(element.firstChild, element);\r\n    }\r\n    element.remove();\r\n  }\r\n\r\n  handlesPointerEvent(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element): boolean {\r\n    const p = event.composedPath();\r\n    const stylo = this._toolbar?.querySelector('stylo-editor');\r\n    return stylo != null && p.indexOf(stylo) >= 0;\r\n  }\r\n\r\n  _formatSelection(type: string, value?: string) {\r\n    const shadowRoot = this.designerCanvas.rootDesignItem.element.shadowRoot;\r\n    if (!shadowRoot)\r\n      return;\r\n\r\n    const selection = shadowrootGetSelection(shadowRoot);\r\n    if (!selection)\r\n      return;\r\n\r\n    const unset = this._isFormatApplied(selection, type, value);\r\n\r\n    const spans = wrapSelectionInSpans(selection);\r\n    if (!spans.length)\r\n      return;\r\n\r\n    for (const span of spans) {\r\n      if (unset) {\r\n        const targets = this._findFormatTargets(span, type, value);\r\n        if (targets.length) {\r\n          for (const target of targets)\r\n            this._unsetFormat(target, type, value);\r\n        } else {\r\n          this._unsetFormat(span, type, value);\r\n        }\r\n      } else {\r\n        this._setFormat(span, type, value);\r\n      }\r\n    }\r\n\r\n    (<HTMLElement>this.extendedItem.element).focus();\r\n    this._restoreSelection(spans);\r\n    this._refreshToolbarState();\r\n  }\r\n\r\n  private _scheduleToolbarStateRefresh() {\r\n    if (this._toolbarStateAnimationFrame != null)\r\n      cancelAnimationFrame(this._toolbarStateAnimationFrame);\r\n\r\n    this._toolbarStateAnimationFrame = requestAnimationFrame(() => {\r\n      this._toolbarStateAnimationFrame = undefined;\r\n      this._refreshToolbarState();\r\n    });\r\n  }\r\n\r\n  private _refreshToolbarState() {\r\n    const element = this._getToolbarStateElement();\r\n    const computedStyle = getComputedStyle(element);\r\n    const decorations = this._getTextDecorationValues(computedStyle.textDecorationLine || computedStyle.textDecoration);\r\n\r\n    this._setToolbarButtonState('font-weight', '800', this._isComputedFormatApplied(computedStyle, 'font-weight', '800'));\r\n    this._setToolbarButtonState('font-style', 'italic', this._isComputedFormatApplied(computedStyle, 'font-style', 'italic'));\r\n    this._setToolbarButtonState('text-decoration', 'underline', decorations.includes('underline'));\r\n    this._setToolbarButtonState('text-decoration', 'line-through', decorations.includes('line-through'));\r\n    this._setToolbarButtonState('text-decoration', 'overline', decorations.includes('overline'));\r\n\r\n    this._setToolbarSelectValue(this._getToolbarSelect('fontSize'), computedStyle.fontSize);\r\n    this._setToolbarFontFamilyValue(this._getToolbarSelect('font-family'), computedStyle.fontFamily);\r\n  }\r\n\r\n  private _getToolbarStateElement(): HTMLElement {\r\n    const selection = this._getEditableSelection();\r\n    const element = selection ? this._getFirstSelectedElement(selection) : null;\r\n    if (element && this.extendedItem.element.contains(element))\r\n      return element;\r\n\r\n    return this.extendedItem.element as HTMLElement;\r\n  }\r\n\r\n  private _isComputedFormatApplied(computedStyle: CSSStyleDeclaration, type: string, value?: string): boolean {\r\n    switch (type) {\r\n      case 'font-weight': {\r\n        if (computedStyle.fontWeight === 'bold')\r\n          return true;\r\n        const numericWeight = parseFloat(computedStyle.fontWeight);\r\n        if (Number.isNaN(numericWeight))\r\n          return false;\r\n        return value ? numericWeight >= parseFloat(value) : numericWeight >= 600;\r\n      }\r\n      case 'font-style':\r\n        return computedStyle.fontStyle === value;\r\n      case 'text-decoration':\r\n        return !!value && this._getTextDecorationValues(computedStyle.textDecorationLine).includes(value);\r\n      case 'fontSize':\r\n        return computedStyle.fontSize === value;\r\n      case 'font-family':\r\n        return !!value && this._normalizeFontFamily(computedStyle.fontFamily) === this._normalizeFontFamily(value);\r\n      default:\r\n        return value != null && computedStyle.getPropertyValue(type) === value;\r\n    }\r\n  }\r\n\r\n  private _getToolbarSelect(command: string): HTMLSelectElement | null {\r\n    return this._toolbar.querySelector(`select[data-command=\"${command}\"]`);\r\n  }\r\n\r\n  private _setToolbarButtonState(command: string, parameter: string, active: boolean) {\r\n    const button = this._toolbar.querySelector<HTMLButtonElement>(`button[data-command=\"${command}\"][data-command-parameter=\"${parameter}\"]`);\r\n    if (!button)\r\n      return;\r\n\r\n    button.toggleAttribute('data-active', active);\r\n    button.ariaPressed = String(active);\r\n    button.style.backgroundColor = active ? 'rgb(191, 219, 254)' : '';\r\n    button.style.boxShadow = active ? 'inset 0 0 0 1px rgb(59, 130, 246)' : '';\r\n  }\r\n\r\n  private _setToolbarSelectValue(select: HTMLSelectElement | null, value: string) {\r\n    if (!select)\r\n      return;\r\n\r\n    this._removeTemporarySelectOptions(select);\r\n    const option = Array.from(select.options).find(x => x.value === value);\r\n    if (option) {\r\n      select.value = option.value;\r\n      return;\r\n    }\r\n\r\n    if (!value) {\r\n      select.selectedIndex = -1;\r\n      return;\r\n    }\r\n\r\n    const dynamicOption = document.createElement('option');\r\n    dynamicOption.value = value;\r\n    dynamicOption.text = value;\r\n    dynamicOption.dataset['temporaryToolbarValue'] = 'true';\r\n    select.appendChild(dynamicOption);\r\n    select.value = value;\r\n  }\r\n\r\n  private _setToolbarFontFamilyValue(select: HTMLSelectElement | null, value: string) {\r\n    if (!select)\r\n      return;\r\n\r\n    const primaryFamily = this._getPrimaryFontFamily(value);\r\n    const normalizedPrimaryFamily = this._normalizeFontFamily(primaryFamily);\r\n\r\n    this._removeTemporarySelectOptions(select);\r\n    const option = Array.from(select.options).find(x => this._normalizeFontFamily(x.value) === normalizedPrimaryFamily);\r\n    if (option) {\r\n      select.value = option.value;\r\n      return;\r\n    }\r\n\r\n    if (!primaryFamily) {\r\n      select.selectedIndex = -1;\r\n      return;\r\n    }\r\n\r\n    const dynamicOption = document.createElement('option');\r\n    dynamicOption.value = primaryFamily;\r\n    dynamicOption.text = primaryFamily;\r\n    dynamicOption.dataset['temporaryToolbarValue'] = 'true';\r\n    select.appendChild(dynamicOption);\r\n    select.value = primaryFamily;\r\n  }\r\n\r\n  private _removeTemporarySelectOptions(select: HTMLSelectElement) {\r\n    Array.from(select.options)\r\n      .filter(x => x.dataset['temporaryToolbarValue'] === 'true')\r\n      .forEach(x => x.remove());\r\n  }\r\n\r\n  private _getPrimaryFontFamily(value: string): string {\r\n    return value.replaceAll(/['\"]/g, '').split(',')[0].trim();\r\n  }\r\n\r\n  private _restoreSelection(spans: HTMLSpanElement[]) {\r\n    const selection = this._getEditableSelection();\r\n    if (!selection)\r\n      return;\r\n\r\n    const range = document.createRange();\r\n    range.setStartBefore(spans[0]);\r\n    range.setEndAfter(spans[spans.length - 1]);\r\n    selection.removeAllRanges();\r\n    selection.addRange(range);\r\n  }\r\n\r\n  private _getEditableSelection(): Selection | null {\r\n    const shadowRoot = this.designerCanvas.rootDesignItem.element.shadowRoot;\r\n    if (!shadowRoot)\r\n      return document.getSelection();\r\n\r\n    const selection = shadowrootGetSelection(shadowRoot);\r\n    if (selection && 'addRange' in selection && 'removeAllRanges' in selection)\r\n      return selection;\r\n    return document.getSelection();\r\n  }\r\n\r\n  private _isFormatApplied(selection: Selection | ArrayLike<StaticRange>, type: string, value?: string): boolean {\r\n    const element = this._getFirstSelectedElement(selection);\r\n    if (!element)\r\n      return false;\r\n\r\n    return this._isComputedFormatApplied(getComputedStyle(element), type, value);\r\n  }\r\n\r\n  private _getFirstSelectedElement(selection: Selection | ArrayLike<StaticRange>): HTMLElement | null {\r\n    const range = this._getSelectionRange(selection);\r\n    if (!range)\r\n      return null;\r\n\r\n    if (range.startContainer.nodeType === Node.TEXT_NODE)\r\n      return range.startContainer.parentElement;\r\n\r\n    const root = range.commonAncestorContainer.nodeType === Node.TEXT_NODE\r\n      ? range.commonAncestorContainer.parentNode\r\n      : range.commonAncestorContainer;\r\n\r\n    if (!root)\r\n      return range.startContainer instanceof HTMLElement ? range.startContainer : null;\r\n\r\n    const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\r\n      acceptNode: node => {\r\n        const textNode = node as Text;\r\n        if (!textNode.textContent?.length)\r\n          return NodeFilter.FILTER_SKIP;\r\n        return range.intersectsNode(textNode) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\r\n      }\r\n    });\r\n\r\n    const firstTextNode = (root.nodeType === Node.TEXT_NODE && range.intersectsNode(root))\r\n      ? root as Text\r\n      : walker.nextNode() as Text | null;\r\n\r\n    if (firstTextNode?.parentElement)\r\n      return firstTextNode.parentElement;\r\n\r\n    return range.startContainer instanceof HTMLElement ? range.startContainer : range.startContainer.parentElement;\r\n  }\r\n\r\n  private _getSelectionRange(selection: Selection | ArrayLike<StaticRange>): Range | null {\r\n    if ('getRangeAt' in selection) {\r\n      if (!selection.rangeCount)\r\n        return null;\r\n      return selection.getRangeAt(0);\r\n    }\r\n\r\n    const staticRange = selection[0];\r\n    if (!staticRange)\r\n      return null;\r\n\r\n    const range = document.createRange();\r\n    range.setStart(staticRange.startContainer, staticRange.startOffset);\r\n    range.setEnd(staticRange.endContainer, staticRange.endOffset);\r\n    return range;\r\n  }\r\n\r\n  private _findFormatTargets(span: HTMLSpanElement, type: string, value?: string): HTMLElement[] {\r\n    const targets: HTMLElement[] = [];\r\n    let current: HTMLElement | null = span;\r\n    while (current && current !== this.extendedItem.element) {\r\n      if (this._hasInlineFormat(current, type, value))\r\n        targets.push(current);\r\n      current = current.parentElement;\r\n    }\r\n    return targets;\r\n  }\r\n\r\n  private _hasInlineFormat(element: HTMLElement, type: string, value?: string): boolean {\r\n    switch (type) {\r\n      case 'font-weight':\r\n        return element.style.fontWeight !== '';\r\n      case 'font-style':\r\n        return element.style.fontStyle !== '';\r\n      case 'text-decoration':\r\n        return !!value && this._getTextDecorationValues(element.style.textDecorationLine || element.style.textDecoration).includes(value);\r\n      case 'fontSize':\r\n        return element.style.fontSize !== '';\r\n      case 'font-family':\r\n        return element.style.fontFamily !== '';\r\n      default:\r\n        return element.style.getPropertyValue(type) !== '';\r\n    }\r\n  }\r\n\r\n  private _setFormat(element: HTMLElement, type: string, value?: string) {\r\n    switch (type) {\r\n      case 'font-weight':\r\n        element.style.fontWeight = value ?? '';\r\n        break;\r\n      case 'font-style':\r\n        element.style.fontStyle = value ?? '';\r\n        break;\r\n      case 'text-decoration': {\r\n        const decorations = this._getTextDecorationValues(element.style.textDecorationLine || element.style.textDecoration);\r\n        if (value && !decorations.includes(value))\r\n          decorations.push(value);\r\n        element.style.textDecorationLine = decorations.join(' ');\r\n        break;\r\n      }\r\n      case 'fontSize':\r\n        element.style.fontSize = value ?? '';\r\n        break;\r\n      case 'font-family':\r\n        element.style.fontFamily = value ?? '';\r\n        break;\r\n      default:\r\n        if (value == null)\r\n          element.style.removeProperty(type);\r\n        else\r\n          element.style.setProperty(type, value);\r\n        break;\r\n    }\r\n  }\r\n\r\n  private _unsetFormat(element: HTMLElement, type: string, value?: string) {\r\n    switch (type) {\r\n      case 'font-weight':\r\n        element.style.removeProperty('font-weight');\r\n        break;\r\n      case 'font-style':\r\n        element.style.removeProperty('font-style');\r\n        break;\r\n      case 'text-decoration': {\r\n        const decorations = this._getTextDecorationValues(element.style.textDecorationLine || element.style.textDecoration)\r\n          .filter(decoration => decoration !== value);\r\n        if (decorations.length)\r\n          element.style.textDecorationLine = decorations.join(' ');\r\n        else {\r\n          element.style.removeProperty('text-decoration-line');\r\n          element.style.removeProperty('text-decoration');\r\n        }\r\n        break;\r\n      }\r\n      case 'fontSize':\r\n        element.style.removeProperty('font-size');\r\n        break;\r\n      case 'font-family':\r\n        element.style.removeProperty('font-family');\r\n        break;\r\n      default:\r\n        element.style.removeProperty(type);\r\n        break;\r\n    }\r\n  }\r\n\r\n  private _getTextDecorationValues(value: string | null | undefined): string[] {\r\n    if (!value)\r\n      return [];\r\n    return value.split(' ').map(x => x.trim()).filter(x => x.length > 0 && x !== 'none' && x !== 'solid');\r\n  }\r\n\r\n  private _normalizeFontFamily(value: string): string {\r\n    return value.replaceAll(/['\"]/g, '').split(',')[0].trim().toLowerCase();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/EditText/EditTextExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { EditTextExtension } from \"./EditTextExtension.js\";\r\nimport { css } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class EditTextExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.isRootItem)\r\n      return false;\r\n    if (designItem.name === 'input')\r\n      return false;\r\n    return true;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new EditTextExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-edit-text-clickoutside { stroke: transparent; fill: lightgray; opacity: 0.7 }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ElementDragTitleExtension.ts",
    "content": "import { getBoundingClientRectAlsoForDisplayContents } from '../../../helper/ElementHelper.js';\r\nimport { getTextWidth } from '../../../helper/TextHelper.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from '../tools/ITool.js';\r\nimport { NamedTools } from \"../tools/NamedTools.js\";\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nconst extensionWidth = 60;\r\n\r\nexport class ElementDragTitleExtension extends AbstractExtension {\r\n  private _rect: SVGRectElement;\r\n  private _clickRect: SVGRectElement;\r\n  private _text: SVGForeignObjectElement;\r\n  private _width: number;\r\n  private _createTitleText: (designItem: IDesignItem) => string;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem, createTitleText?: (designItem: IDesignItem) => string) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._createTitleText = createTitleText;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!transformedCornerPoints)\r\n      return;\r\n\r\n    if (!isNaN(transformedCornerPoints.p1.x)) {\r\n      const boundRect = getBoundingClientRectAlsoForDisplayContents(this.extendedItem.element);\r\n      let w = getTextWidth(this.extendedItem.name, '10px monospace');\r\n      let elementWidth = Math.sqrt(Math.pow(transformedCornerPoints.p2.x - transformedCornerPoints.p1.x, 2) + Math.pow(transformedCornerPoints.p2.y - transformedCornerPoints.p1.y, 2));\r\n      let text = this.extendedItem.name;\r\n      if (this.extendedItem.id)\r\n        text = '#' + this.extendedItem.id;\r\n      if (this._createTitleText)\r\n        text = this._createTitleText(this.extendedItem);\r\n      this._width = Math.max(Math.min(elementWidth, w), extensionWidth);\r\n      this._rect = this._drawRect(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, this._width, 15, 'svg-primary-selection-move', this._rect);\r\n      this._clickRect = this._drawRect(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, this._width, 18, 'svg-invisible', this._clickRect);\r\n      this._clickRect.style.background = 'transparent';\r\n      this._text = this._drawHTML('<div style=\"position:relative; pointer-events: none;\"><span style=\"width: 100%; position: absolute; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transform-origin: 0 0; padding-left: 2px;\">' + text + '</span></div>', (boundRect.x - this.designerCanvas.containerBoundingRect.x) / this.designerCanvas.scaleFactor, transformedCornerPoints.p1.y - 16, this._width, 15, 'svg-text-primary', this._text);\r\n      this._text.style.overflow = 'visible';\r\n\r\n      this._clickRect.addEventListener('pointerdown', (e) => this._pointerEvent(e));\r\n      this._clickRect.addEventListener('pointermove', (e) => this._pointerEvent(e));\r\n      this._clickRect.addEventListener('pointerup', (e) => this._pointerEvent(e));\r\n      this._clickRect.addEventListener('contextmenu', (e) => {\r\n        e.preventDefault();\r\n        this.designerCanvas.showDesignItemContextMenu(this.extendedItem, e);\r\n      });\r\n      this.refresh(cache, event);\r\n    }\r\n  }\r\n\r\n  _drawMoveOverlay(itemRect: DOMRect) {\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!isNaN(transformedCornerPoints.p1.x)) {\r\n      const angle = Math.atan2((transformedCornerPoints.p2.y - transformedCornerPoints.p1.y), (transformedCornerPoints.p2.x - transformedCornerPoints.p1.x)) * 180 / Math.PI;\r\n      if (this._valuesHaveChanges(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, angle, this.designerCanvas.scaleFactor)) {\r\n        const h = (15 / this.designerCanvas.scaleFactor);\r\n        const o = -(16 / this.designerCanvas.scaleFactor);\r\n        const w = (this._width / this.designerCanvas.scaleFactor);\r\n        this._rect.setAttribute('x', '' + transformedCornerPoints.p1.x);\r\n        this._rect.setAttribute('y', '' + transformedCornerPoints.p1.y);\r\n        this._rect.style.rotate = angle + 'deg';\r\n        this._rect.style.translate = '0 ' + o + 'px';\r\n        this._rect.style.transformOrigin = '0 100%';\r\n        this._rect.style.transformBox = 'fill-box';\r\n        this._rect.setAttribute('height', '' + h);\r\n        this._rect.setAttribute('width', '' + w);\r\n        this._rect.style.strokeWidth = (1 / this.designerCanvas.scaleFactor).toString();\r\n        this._clickRect.setAttribute('x', '' + transformedCornerPoints.p1.x);\r\n        this._clickRect.setAttribute('y', '' + transformedCornerPoints.p1.y);\r\n        this._clickRect.style.rotate = angle + 'deg';\r\n        this._clickRect.style.translate = '0 ' + o + 'px';\r\n        this._clickRect.style.transformOrigin = '0 100%';\r\n        this._clickRect.style.transformBox = 'fill-box';\r\n        this._clickRect.setAttribute('height', '' + (h + 3));\r\n        this._clickRect.setAttribute('width', '' + w);\r\n        this._clickRect.style.strokeWidth = (1 / this.designerCanvas.scaleFactor).toString();\r\n        this._text.setAttribute('x', '' + transformedCornerPoints.p1.x);\r\n        this._text.setAttribute('y', '' + transformedCornerPoints.p1.y);\r\n        this._text.style.fontSize = (10 / this.designerCanvas.scaleFactor) + 'px';\r\n        this._text.setAttribute('height', '' + h);\r\n        this._text.setAttribute('width', '' + w);\r\n        (<HTMLElement>this._text.children[0].children[0]).style.rotate = angle + 'deg';\r\n        (<HTMLElement>this._text.children[0].children[0]).style.translate = '0 ' + o + 'px';\r\n        (<HTMLElement>this._text.children[0].children[0]).style.transformOrigin = '0 100%';\r\n      }\r\n    }\r\n  }\r\n\r\n  _pointerEvent(event: PointerEvent) {\r\n    event.preventDefault();\r\n    event.stopPropagation();\r\n    if (event.button != 2)\r\n      (<ITool>this.designerCanvas.serviceContainer.designerTools.get(NamedTools.Pointer)).pointerEventHandler(this.designerCanvas, event, this.extendedItem.element);\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ElementDragTitleExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { ElementDragTitleExtension } from './ElementDragTitleExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class ElementDragTitleExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  private _createTitleText: (designItem: IDesignItem) => string;\r\n  \r\n  constructor(createTitleText?: (designItem: IDesignItem) => string) {\r\n    this._createTitleText = createTitleText;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem && !(designItem.element instanceof HTMLTemplateElement);\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new ElementDragTitleExtension(extensionManager, designerView, designItem, this._createTitleText);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-text-primary { stroke: none; color: white; font-family: monospace; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ExtensionManager.ts",
    "content": "import { DesignItem } from '../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { NodeType } from \"../../../item/NodeType.js\";\r\nimport { ISelectionChangedEvent } from '../../../services/selectionService/ISelectionChangedEvent.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ExtensionType } from './ExtensionType.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { DesignerCanvas } from '../designerCanvas.js';\r\nimport { ISelectionRefreshEvent } from '../../../services/selectionService/ISelectionRefreshEvent.js';\r\nimport { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { clearCache as clearBoxQuadsCache, useCache as useBoxQuadsCache } from '../../../helper/getBoxQuads.js';\r\nimport { IContentChanged } from '../../../services/InstanceServiceContainer.js';\r\n\r\nfunction wmGet<T extends Map<any, any>>(designItem: IDesignItem, weakMap: WeakMap<IDesignItem, T>) {\r\n  let val = weakMap.get(designItem);\r\n  if (val) return val;\r\n  val = <any>new Map<any, any>();\r\n  weakMap.set(designItem, val);\r\n  return val;\r\n}\r\n\r\nexport class ExtensionManager implements IExtensionManager {\r\n\r\n  designerCanvas: IDesignerCanvas;\r\n  designItemsWithExtentions: Set<IDesignItem> = new Set();\r\n  _timeout: ReturnType<typeof setTimeout>;\r\n\r\n  _appliedDesignerExtensions = new WeakMap<IDesignItem, Map<ExtensionType, IDesignerExtension[]>>\r\n  _shouldAppliedDesignerExtensions = new WeakMap<IDesignItem, Map<ExtensionType, IDesignerExtensionProvider[]>>\r\n  _lastApplyEventPerType = new WeakMap<IDesignItem, Map<ExtensionType, Event>>\r\n  _lastPrimarySelectionRefreshItem: WeakRef<IDesignItem>\r\n\r\n  constructor(designerCanvas: IDesignerCanvas) {\r\n    useBoxQuadsCache();\r\n\r\n    this.designerCanvas = designerCanvas;\r\n\r\n    designerCanvas.instanceServiceContainer.selectionService.onSelectionChanged.on(this._selectedElementsChanged.bind(this));\r\n    designerCanvas.instanceServiceContainer.selectionService.onSelectionRefresh.on(this._selectedElementsRefresh.bind(this));\r\n    designerCanvas.instanceServiceContainer.onContentChanged.on(this._contentChanged.bind(this));\r\n\r\n    designerCanvas.serviceContainer.globalContext.onToolChanged.on(() => {\r\n      this.removeExtension(designerCanvas.instanceServiceContainer.selectionService.primarySelection, ExtensionType.PrimarySelectionRefreshed);\r\n      this._lastPrimarySelectionRefreshItem = null;\r\n    });\r\n  }\r\n\r\n  connected() {\r\n    this.ensurePermanentRootExtensionsApplied();\r\n    if (!this._timeout)\r\n      this._timeout = setTimeout(() => this.refreshAllExtensionsTimeout(), 20);\r\n  }\r\n\r\n  disconnected() {\r\n    if (this._timeout)\r\n      clearTimeout(this._timeout);\r\n    this._timeout = null;\r\n  }\r\n\r\n\r\n\r\n  private refreshAllExtensionsTimeout() {\r\n    this.refreshAllAppliedExtentions();\r\n    this._timeout = setTimeout(() => this.refreshAllExtensionsTimeout(), 20);\r\n  }\r\n\r\n  private ensurePermanentRootExtensionsApplied() {\r\n    const rootDesignItem = this.designerCanvas.rootDesignItem;\r\n    if (!rootDesignItem)\r\n      return;\r\n\r\n    if (wmGet(rootDesignItem, this._appliedDesignerExtensions).get(ExtensionType.Permanent)?.length)\r\n      return;\r\n\r\n    this.applyExtension(rootDesignItem, ExtensionType.Permanent);\r\n  }\r\n\r\n  private _contentChanged(contentChanges: IContentChanged[]) {\r\n    requestAnimationFrame(() => {\r\n      for (let contentChanged of contentChanges) {\r\n        switch (contentChanged.changeType) {\r\n          case 'added':\r\n            this.applyExtensions(contentChanged.designItems, ExtensionType.Permanent, null, true);\r\n            break;\r\n          case 'moved':\r\n            this.refreshExtensions(contentChanged.designItems, ExtensionType.Permanent);\r\n            break;\r\n          case 'parsed':\r\n            this.ensurePermanentRootExtensionsApplied();\r\n            this.applyExtensions(Array.from(this.designerCanvas.rootDesignItem.children()), ExtensionType.Permanent, null, true);\r\n            break;\r\n          case 'removed':\r\n            this.removeExtensions(contentChanged.designItems, true, ExtensionType.Permanent);\r\n            break;\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  private _selectedElementsChanged(selectionChangedEvent: ISelectionChangedEvent) {\r\n    this._lastPrimarySelectionRefreshItem = null;\r\n\r\n    if (selectionChangedEvent.oldSelectedElements && selectionChangedEvent.oldSelectedElements.length) {\r\n      this.removeExtension(selectionChangedEvent.oldSelectedElements[0], ExtensionType.PrimarySelectionRefreshed);\r\n      this.removeExtension(selectionChangedEvent.oldSelectedElements[0], ExtensionType.PrimarySelection);\r\n      this.removeExtension(selectionChangedEvent.oldSelectedElements[0], ExtensionType.PrimarySelectionAndCanBeEntered);\r\n      this.removeExtension(selectionChangedEvent.oldSelectedElements[0], ExtensionType.OnlyOneItemSelected);\r\n      this.removeExtensions(selectionChangedEvent.oldSelectedElements, false, ExtensionType.Selection);\r\n      this.removeExtensions(selectionChangedEvent.oldSelectedElements, false, ExtensionType.MultipleItemsSelected);\r\n      if (selectionChangedEvent.oldSelectedElements[0].parent) {\r\n        const primaryContainer = DesignItem.GetOrCreateDesignItem(selectionChangedEvent.oldSelectedElements[0].parent.element, selectionChangedEvent.oldSelectedElements[0].parent.element, this.designerCanvas.serviceContainer, this.designerCanvas.instanceServiceContainer)\r\n        this.removeExtension(primaryContainer, ExtensionType.PrimarySelectionContainer);\r\n        this.removeExtension(primaryContainer, ExtensionType.PrimarySelectionContainerAndCanBeEntered);\r\n      }\r\n    }\r\n\r\n    if (selectionChangedEvent.selectedElements && selectionChangedEvent.selectedElements.length) {\r\n      this.applyExtensions(selectionChangedEvent.selectedElements, ExtensionType.Selection, selectionChangedEvent.event);\r\n      this.applyExtension(selectionChangedEvent.selectedElements[0], ExtensionType.PrimarySelection, selectionChangedEvent.event);\r\n      if (selectionChangedEvent.selectedElements.length === 1)\r\n        this.applyExtension(selectionChangedEvent.selectedElements[0], ExtensionType.OnlyOneItemSelected, selectionChangedEvent.event);\r\n      else if (selectionChangedEvent.selectedElements.length > 1)\r\n        this.applyExtension(selectionChangedEvent.selectedElements[0], ExtensionType.MultipleItemsSelected, selectionChangedEvent.event);\r\n      if (selectionChangedEvent.selectedElements[0].getPlacementService()?.isEnterableContainer(selectionChangedEvent.selectedElements[0]))\r\n        this.applyExtension(selectionChangedEvent.selectedElements[0], ExtensionType.PrimarySelectionAndCanBeEntered, selectionChangedEvent.event);\r\n      if (selectionChangedEvent.selectedElements[0].parent) {\r\n        const primaryContainer = DesignItem.GetOrCreateDesignItem(selectionChangedEvent.selectedElements[0].parent.element, selectionChangedEvent.selectedElements[0].parent.element, this.designerCanvas.serviceContainer, this.designerCanvas.instanceServiceContainer)\r\n        this.applyExtension(primaryContainer, ExtensionType.PrimarySelectionContainer, selectionChangedEvent.event);\r\n        if (primaryContainer.getPlacementService()?.isEnterableContainer(primaryContainer))\r\n          this.applyExtension(primaryContainer, ExtensionType.PrimarySelectionContainerAndCanBeEntered, selectionChangedEvent.event);\r\n      }\r\n    }\r\n  }\r\n\r\n  private _selectedElementsRefresh(selectionChangedEvent: ISelectionRefreshEvent) {\r\n    this.refreshAllAppliedExtentions(selectionChangedEvent.event);\r\n\r\n    if (selectionChangedEvent.selectedElements && selectionChangedEvent.selectedElements.length && this._lastPrimarySelectionRefreshItem?.deref() === selectionChangedEvent.selectedElements[0]) {\r\n      if (!this._appliedDesignerExtensions.get(selectionChangedEvent.selectedElements[0])?.get(ExtensionType.PrimarySelectionRefreshed))\r\n        this.applyExtension(selectionChangedEvent.selectedElements[0], ExtensionType.PrimarySelectionRefreshed, selectionChangedEvent.event);\r\n    }\r\n    this._lastPrimarySelectionRefreshItem = new WeakRef(selectionChangedEvent.selectedElements[0]);\r\n  }\r\n\r\n  applyExtension(designItem: IDesignItem, extensionType: ExtensionType, event?: Event, recursive: boolean = false) {\r\n    if (designItem && designItem.nodeType == NodeType.Element) {\r\n      const extProv = this.designerCanvas.serviceContainer.designerExtensions.get(extensionType);\r\n      let extensions: IDesignerExtension[] = [];\r\n      if (extProv) {\r\n        const cache = {};\r\n        clearBoxQuadsCache();\r\n        for (let e of extProv) {\r\n          let shouldAppE = wmGet(designItem, this._shouldAppliedDesignerExtensions).get(extensionType);\r\n          if (!shouldAppE)\r\n            shouldAppE = [];\r\n          shouldAppE.push(e);\r\n          wmGet(designItem, this._shouldAppliedDesignerExtensions).set(extensionType, shouldAppE);\r\n\r\n          if (e.shouldExtend(this, this.designerCanvas, designItem)) {\r\n            let appE = wmGet(designItem, this._appliedDesignerExtensions).get(extensionType);\r\n            if (!appE)\r\n              appE = [];\r\n            const ext = e.getExtension(this, this.designerCanvas, designItem);\r\n            try {\r\n              ext.extend(cache, event);\r\n              extensions.push(ext);\r\n              if (event)\r\n                wmGet(designItem, this._lastApplyEventPerType).set(extensionType, event);\r\n              else wmGet(designItem, this._lastApplyEventPerType).delete(extensionType);\r\n            }\r\n            catch (err) {\r\n              console.error(err);\r\n            }\r\n            appE.push(ext);\r\n            wmGet(designItem, this._appliedDesignerExtensions).set(extensionType, appE);\r\n\r\n            this.designItemsWithExtentions.add(designItem);\r\n          }\r\n        }\r\n      }\r\n\r\n      if (recursive) {\r\n        for (const d of designItem.children()) {\r\n          this.applyExtension(d, extensionType, event, recursive);\r\n        }\r\n      }\r\n      return extensions;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  applyExtensions(designItems: IDesignItem[], extensionType: ExtensionType, event?: Event, recursive: boolean = false) {\r\n    this.designerCanvas.overlayLayer.startBatch();\r\n    if (designItems) {\r\n      const extProv = this.designerCanvas.serviceContainer.designerExtensions.get(extensionType);\r\n      if (extProv) {\r\n        const cache = {};\r\n        clearBoxQuadsCache();\r\n        for (let e of extProv) {\r\n          for (let i of designItems) {\r\n            let shouldAppE = wmGet(i, this._shouldAppliedDesignerExtensions).get(extensionType);\r\n            if (!shouldAppE)\r\n              shouldAppE = [];\r\n            shouldAppE.push(e);\r\n            wmGet(i, this._shouldAppliedDesignerExtensions).set(extensionType, shouldAppE);\r\n\r\n            if (e.shouldExtend(this, this.designerCanvas, i)) {\r\n              let appE = wmGet(i, this._appliedDesignerExtensions).get(extensionType);\r\n              if (!appE)\r\n                appE = [];\r\n              const ext = e.getExtension(this, this.designerCanvas, i);\r\n              try {\r\n                ext.extend(cache, event);\r\n                if (event)\r\n                  wmGet(i, this._lastApplyEventPerType).set(extensionType, event);\r\n                else wmGet(i, this._lastApplyEventPerType).delete(extensionType);\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n              }\r\n              appE.push(ext);\r\n              wmGet(i, this._appliedDesignerExtensions).set(extensionType, appE);\r\n              this.designItemsWithExtentions.add(i);\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      if (recursive) {\r\n        for (const d of designItems) {\r\n          this.applyExtensions(Array.from(d.children()), extensionType, event, recursive);\r\n        }\r\n      }\r\n    }\r\n    this.designerCanvas.overlayLayer.endBatch();\r\n  }\r\n\r\n  applyExtensionInstance(designItem: IDesignItem, extension: IDesignerExtension, extensionType: ExtensionType = ExtensionType.Directly) {\r\n    let appE = wmGet(designItem, this._appliedDesignerExtensions).get(ExtensionType.Directly);\r\n    if (!appE)\r\n      appE = [];\r\n    try {\r\n      extension.extend(null, null);\r\n    }\r\n    catch (err) {\r\n      console.error(err);\r\n    }\r\n    appE.push(extension);\r\n    wmGet(designItem, this._appliedDesignerExtensions).set(extensionType, appE);\r\n    this.designItemsWithExtentions.add(designItem);\r\n  }\r\n\r\n  removeExtensionInstance(designItem: IDesignItem, extension: IDesignerExtension) {\r\n    for (let e of wmGet(designItem, this._appliedDesignerExtensions)) {\r\n      const idx = e[1].indexOf(extension);\r\n      if (idx >= 0) {\r\n        try {\r\n          extension.dispose();\r\n        }\r\n        catch (err) {\r\n          console.error(err);\r\n        }\r\n        e[1].splice(idx, 1);\r\n        if (e[1].length == 0) {\r\n          wmGet(designItem, this._appliedDesignerExtensions).delete(e[0]);\r\n          wmGet(designItem, this._shouldAppliedDesignerExtensions).delete(e[0]);\r\n        }\r\n        if (!wmGet(designItem, this._appliedDesignerExtensions).size)\r\n          this.designItemsWithExtentions.delete(designItem);\r\n      }\r\n    }\r\n  }\r\n\r\n  removeExtension(designItem: IDesignItem, extensionType?: ExtensionType) {\r\n    if (designItem) {\r\n      if (extensionType) {\r\n        wmGet(designItem, this._shouldAppliedDesignerExtensions).delete(extensionType);\r\n\r\n        let exts = wmGet(designItem, this._appliedDesignerExtensions).get(extensionType);\r\n        if (exts) {\r\n          for (let e of exts) {\r\n            try {\r\n              e.dispose();\r\n              wmGet(designItem, this._lastApplyEventPerType).delete(extensionType);\r\n            }\r\n            catch (err) {\r\n              console.error(err);\r\n            }\r\n          }\r\n          wmGet(designItem, this._appliedDesignerExtensions).delete(extensionType);\r\n          if (!wmGet(designItem, this._appliedDesignerExtensions).size)\r\n            this.designItemsWithExtentions.delete(designItem);\r\n        }\r\n      } else {\r\n        wmGet(designItem, this._shouldAppliedDesignerExtensions).clear();\r\n        for (let appE of wmGet(designItem, this._appliedDesignerExtensions)) {\r\n          for (let e of appE[1]) {\r\n            try {\r\n              e.dispose();\r\n            }\r\n            catch (err) {\r\n              console.error(err);\r\n            }\r\n          }\r\n        }\r\n        wmGet(designItem, this._appliedDesignerExtensions).clear();\r\n        this.designItemsWithExtentions.delete(designItem);\r\n      }\r\n    }\r\n  }\r\n\r\n  removeExtensions(designItems: IDesignItem[], recursive: boolean, extensionType?: ExtensionType) {\r\n    if (designItems) {\r\n      if (extensionType) {\r\n        for (let i of designItems) {\r\n          if (recursive && i.hasChildren) {\r\n            this.removeExtensions([...i.children()], true, extensionType);\r\n          }\r\n          wmGet(i, this._shouldAppliedDesignerExtensions).delete(extensionType);\r\n          let exts = wmGet(i, this._appliedDesignerExtensions).get(extensionType);\r\n          if (exts) {\r\n            for (let e of exts) {\r\n              try {\r\n                e.dispose();\r\n                wmGet(i, this._lastApplyEventPerType).delete(extensionType);\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n              }\r\n            }\r\n            wmGet(i, this._appliedDesignerExtensions).delete(extensionType);\r\n            if (!wmGet(i, this._appliedDesignerExtensions).size)\r\n              this.designItemsWithExtentions.delete(i);\r\n          }\r\n        }\r\n      } else {\r\n        for (let i of designItems) {\r\n          if (recursive && i.hasChildren) {\r\n            this.removeExtensions([...i.children()], true, extensionType);\r\n          }\r\n          wmGet(i, this._shouldAppliedDesignerExtensions).clear();\r\n          for (let appE of wmGet(i, this._appliedDesignerExtensions)) {\r\n            for (let e of appE[1]) {\r\n              try {\r\n                e.dispose();\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n              }\r\n            }\r\n          }\r\n          wmGet(i, this._appliedDesignerExtensions).clear();\r\n          this.designItemsWithExtentions.delete(i);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  removeAllExtensions() {\r\n    this.designItemsWithExtentions.forEach(i => {\r\n      wmGet(i, this._shouldAppliedDesignerExtensions).clear();\r\n      for (let appE of wmGet(i, this._appliedDesignerExtensions)) {\r\n        for (let e of appE[1]) {\r\n          try {\r\n            e.dispose();\r\n          }\r\n          catch (err) {\r\n            console.error(err);\r\n          }\r\n        }\r\n      }\r\n      wmGet(i, this._appliedDesignerExtensions).clear();\r\n    });\r\n    this.designItemsWithExtentions.clear();\r\n  }\r\n\r\n  refreshExtension(designItem: IDesignItem, extensionType?: ExtensionType, event?: Event) {\r\n    if (this.designerCanvas.checkVisibility && !this.designerCanvas.checkVisibility())\r\n      return;\r\n    if (designItem) {\r\n      if (extensionType) {\r\n        if (!designItem.element.isConnected) {\r\n          this.removeExtension(designItem, extensionType);\r\n        } else {\r\n          let exts = wmGet(designItem, this._appliedDesignerExtensions).get(extensionType);\r\n          if (exts) {\r\n            const cache = {};\r\n            clearBoxQuadsCache();\r\n            for (let e of exts) {\r\n              try {\r\n                e.refresh(cache, event);\r\n                if (event)\r\n                  wmGet(designItem, this._lastApplyEventPerType).set(extensionType, event);\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n              }\r\n            }\r\n          }\r\n        }\r\n      } else {\r\n        const cache = {};\r\n        clearBoxQuadsCache();\r\n        for (let appE of wmGet(designItem, this._appliedDesignerExtensions)) {\r\n          for (let e of appE[1]) {\r\n            try {\r\n              e.refresh(cache, event);\r\n            }\r\n            catch (err) {\r\n              console.error(err);\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  refreshExtensions(designItems: IDesignItem[], extensionType?: ExtensionType, event?: Event, ignoredExtension?: IDesignerExtension, timeout?: number) {\r\n    if (this.designerCanvas.checkVisibility && !this.designerCanvas.checkVisibility())\r\n      return;\r\n    this.designerCanvas.overlayLayer.startBatch();\r\n    const start = performance.now();\r\n    if (designItems) {\r\n      if (extensionType) {\r\n        const cache = {};\r\n        clearBoxQuadsCache();\r\n        outer1:\r\n        for (let i of designItems) {\r\n          if (!i.element.isConnected) {\r\n            this.removeExtension(i, extensionType);\r\n          } else {\r\n            let exts = wmGet(i, this._appliedDesignerExtensions).get(extensionType);\r\n            if (exts) {\r\n              for (let e of exts) {\r\n                try {\r\n                  if (e != ignoredExtension)\r\n                    e.refresh(cache, event);\r\n                  if (timeout) {\r\n                    const end = performance.now();\r\n                    if (end - start > timeout) {\r\n                      console.warn(\"refreshExtensions() took too long, stopped refreshing\");\r\n                      break outer1;\r\n                    }\r\n                  }\r\n                }\r\n                catch (err) {\r\n                  console.error(err);\r\n                }\r\n              }\r\n            }\r\n          }\r\n        }\r\n      } else {\r\n        const cache = {};\r\n        clearBoxQuadsCache();\r\n        outer2:\r\n        for (let i of designItems) {\r\n          for (let appE of wmGet(i, this._appliedDesignerExtensions)) {\r\n            for (let e of appE[1]) {\r\n              try {\r\n                if (e != ignoredExtension) {\r\n                  e.refresh(cache, event);\r\n                  if (event)\r\n                    wmGet(i, this._lastApplyEventPerType).set(extensionType, event);\r\n                  if (timeout) {\r\n                    const end = performance.now();\r\n                    if (end - start > timeout) {\r\n                      console.warn(\"refreshExtensions() took too long, stopped refreshing\");\r\n                      break outer2;\r\n                    }\r\n                  }\r\n                }\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n    this.designerCanvas.overlayLayer.endBatch();\r\n  }\r\n\r\n  refreshAllExtensions(designItems: IDesignItem[], ignoredExtension?: IDesignerExtension, event?: Event) {\r\n    this.designerCanvas.overlayLayer.startBatch();\r\n    if (designItems) {\r\n      this.refreshExtensions(designItems, ExtensionType.Directly, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.Permanent, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.Selection, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.PrimarySelection, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.PrimarySelectionContainer, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.MouseOver, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.OnlyOneItemSelected, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.MultipleItemsSelected, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.ContainerDragOverAndCanBeEntered, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.ContainerDrag, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.Doubleclick, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.Placement, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.PrimarySelectionAndCanBeEntered, event, ignoredExtension);\r\n      this.refreshExtensions(designItems, ExtensionType.PrimarySelectionContainerAndCanBeEntered, event, ignoredExtension);\r\n    }\r\n    this.designerCanvas.overlayLayer.endBatch();\r\n  }\r\n\r\n  refreshAllAppliedExtentions(event?: Event) {\r\n    (<DesignerCanvas>this.designerCanvas).fillCalculationrects();\r\n    this.refreshAllExtensions([...this.designItemsWithExtentions], null, event)\r\n  }\r\n\r\n  //TODO: does not work with permanant, when not applied... maybe we need to do in another way\r\n  //maybe store the \"shouldAppliedExtensions??\"\r\n  reapplyAllAppliedExtentions(filterDesignItems?: IDesignItem[], enabledExtensionTypes?: ExtensionType[]) {\r\n    this.designerCanvas.overlayLayer.startBatch();\r\n    for (let d of ExtensionManager.getAllChildElements(this.designerCanvas.rootDesignItem)) {\r\n      if (!filterDesignItems || filterDesignItems.includes(d)) {\r\n        const keys = [...wmGet(d, this._shouldAppliedDesignerExtensions).keys()];\r\n        for (let t of keys) {\r\n          const evt = wmGet(d, this._lastApplyEventPerType).get(t);\r\n          this.removeExtension(d, t);\r\n          if (enabledExtensionTypes == null || enabledExtensionTypes.includes(t))\r\n            this.applyExtension(d, t, evt);\r\n        }\r\n      }\r\n    }\r\n    this.designerCanvas.overlayLayer.endBatch();\r\n  }\r\n\r\n  private static *getAllChildElements(designItem: IDesignItem): IterableIterator<IDesignItem> {\r\n    if (designItem.nodeType == NodeType.Element)\r\n      yield designItem;\r\n    if (designItem.hasChildren) {\r\n      for (let c of designItem.children())\r\n        for (let di of ExtensionManager.getAllChildElements(c))\r\n          yield di;\r\n    }\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ExtensionType.ts",
    "content": "export enum ExtensionType {\r\n  Directly = 0, //Extensions wich are applied directly, for example from Text Edit Tool\r\n\r\n  Permanent = 1,\r\n  Selection = 2,\r\n  PrimarySelection = 3,\r\n  PrimarySelectionContainer = 4,\r\n  MouseOver = 5,\r\n  OnlyOneItemSelected = 6,\r\n  MultipleItemsSelected = 7,\r\n  /**\r\n   * Extension for the Container wich the dragged element is draged over.\r\n   */\r\n  ContainerDragOverAndCanBeEntered = 8,\r\n  /**\r\n  * Extension for the Container on wich a new element is draged over.\r\n  */\r\n  ContainerExternalDragOverAndCanBeEntered = 9,\r\n  /**\r\n   * Extension for the Current Container wich the dragged element is contained.\r\n   */\r\n  ContainerDrag = 10,\r\n  Doubleclick = 11,\r\n  Placement = 12,\r\n\r\n  /**\r\n   * Extensions only when the container can be Entered.\r\n   * So for Example on a custom webcomponent wich uses a grid layout for it's root, but can not show children,\r\n   * do not display grid extension.\r\n   */\r\n  PrimarySelectionAndCanBeEntered = 13,\r\n  PrimarySelectionContainerAndCanBeEntered = 14,\r\n  /**\r\n   * Extension when the Primary Selection is refreshed, means clicked a second time\r\n   */\r\n  PrimarySelectionRefreshed = 15,\r\n  /**\r\n   * Extension manually applied by the user. But registered, caus we need the styles.\r\n   */\r\n  ManualApplied = 16,\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/GrayOutDragOverContainerExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from \"./OverlayLayer.js\";\r\n\r\nexport class GrayOutDragOverContainerExtension extends AbstractExtension {\r\n\r\n  private _rect: SVGPathElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  override refresh() {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    this._rect = this._drawTransformedRect(transformedCornerPoints, 'svg-rect-enter-container', this._rect, OverlayLayer.Background);\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/GrayOutDragOverContainerExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { GrayOutDragOverContainerExtension } from \"./GrayOutDragOverContainerExtension.js\";\r\n\r\nexport class GrayOutDragOverContainerExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new GrayOutDragOverContainerExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-rect-enter-container { stroke: none; fill: #aa00ff2e; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/GrayOutExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from \"./OverlayLayer.js\";\r\n\r\nexport class GrayOutExtension extends AbstractExtension {\r\n\r\n  private _path: SVGPathElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  override refresh() {\r\n    if (!this._path) {\r\n      this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n      this._path.setAttribute('class', 'svg-gray-out');\r\n      this._path.setAttribute('fill-rule', 'evenodd');\r\n      this._addOverlay(this._path, OverlayLayer.Background);\r\n    }\r\n\r\n    const p = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    let outsideRect = { width: this.designerCanvas.containerBoundingRect.width / this.designerCanvas.scaleFactor, height: this.designerCanvas.containerBoundingRect.height / this.designerCanvas.scaleFactor };\r\n    let data = \"M0 0 L\" + outsideRect.width + \" 0 L\" + outsideRect.width + ' ' + outsideRect.height + \" L0 \" + outsideRect.height + \" Z \";\r\n    data += \"M\" + p.p1.x + \" \" + p.p1.y + \" L\" + p.p2.x + \" \" + p.p2.y + \" L\" + p.p3.x + \" \" + p.p3.y + \" L\" + p.p4.x + \" \" + p.p4.y + \" Z\";\r\n    this._path.setAttribute(\"d\", data);\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n    this._path = null;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/GrayOutExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { GrayOutExtension } from './GrayOutExtension.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class GrayOutExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new GrayOutExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  readonly style = css`\r\n    .svg-gray-out { stroke: transparent; fill: rgba(211, 211, 211, 0.8); pointer-events: none }\r\n  `;  \r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/HighlightElementExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from './OverlayLayer.js';\r\nimport { createOverlayPathDataFromSvgGeometryElement } from '../../../helper/SvgHelper.js';\r\n\r\nexport interface HighlightElementExtensionOptions {\r\n  useSvgGeometryOutline?: boolean;\r\n  includeSvgMarkers?: boolean;\r\n}\r\n\r\nexport class HighlightElementExtension extends AbstractExtension {\r\n\r\n  private _rect: SVGPathElement;\r\n  private _options: HighlightElementExtensionOptions;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem, options?: HighlightElementExtensionOptions) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._options = { useSvgGeometryOutline: true, includeSvgMarkers: true, ...(options ?? {}) };\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  override refresh() {\r\n    if (this._options.useSvgGeometryOutline) {\r\n      const transformedShapePathData = createOverlayPathDataFromSvgGeometryElement(this.extendedItem.element, this.designerCanvas, { includeMarkers: this._options.includeSvgMarkers });\r\n      if (transformedShapePathData) {\r\n        this._rect = this._drawPath(transformedShapePathData, 'svg-hover-fill', this._rect, OverlayLayer.Background);\r\n        this._rect.style.strokeWidth = (3 / this.designerCanvas.scaleFactor).toString();\r\n        return;\r\n      }\r\n    }\r\n\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!isNaN(transformedCornerPoints.p1.x)) {\r\n      this._rect = this._drawTransformedRect(transformedCornerPoints, 'svg-hover', this._rect, OverlayLayer.Background);\r\n      this._rect.style.strokeWidth = (3 / this.designerCanvas.scaleFactor).toString();\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/HighlightElementExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { HighlightElementExtension, HighlightElementExtensionOptions } from './HighlightElementExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class HighlightElementExtensionProvider implements IDesignerExtensionProvider {\r\n  private _options: HighlightElementExtensionOptions;\r\n\r\n  constructor(options?: HighlightElementExtensionOptions) {\r\n    this._options = options ?? {};\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return !(designItem.element instanceof HTMLTemplateElement);\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new HighlightElementExtension(extensionManager, designerView, designItem, this._options);\r\n  }\r\n  \r\n  static readonly style = css`\r\n    .svg-hover { stroke: #90caf966; fill: none; }\r\n    .svg-hover-fill { stroke: #90caf966; fill: #90caf966; }\r\n  `;    \r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/IDesignerExtension.ts",
    "content": "import { IDisposable } from '../../../../interfaces/IDisposable.js';\r\n\r\nexport interface IDesignerExtension extends IDisposable {\r\n  extend(cache: Record<string | symbol, any>, event?: Event);\r\n  refresh(cache: Record<string | symbol, any>, event?: Event);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/IDesignerExtensionProvider.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport interface IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean;\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension;\r\n  style?: CSSStyleSheet | CSSStyleSheet[];\r\n  svgDefs?: string | string[];\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/IExtensionManger.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ExtensionType } from './ExtensionType.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\n\r\nexport interface IExtensionManager {\r\n  applyExtension(designItem: IDesignItem, extensionType: ExtensionType, event?: Event, recursive?: boolean) : IDesignerExtension[];\r\n  applyExtensions(designItems: IDesignItem[], extensionType: ExtensionType, event?: Event, recursive?: boolean);\r\n  applyExtensionInstance(designItem: IDesignItem, extension: IDesignerExtension, extensionType?: ExtensionType);\r\n  removeExtension(designItem: IDesignItem, extensionType?: ExtensionType);\r\n  removeExtensions(designItems: IDesignItem[], recursive: boolean, extensionType?: ExtensionType);\r\n  removeExtensionInstance(designItem: IDesignItem, extension: IDesignerExtension);\r\n  removeAllExtensions();\r\n  refreshExtension(designItem: IDesignItem, extensionType?: ExtensionType, event?: Event);\r\n  refreshExtensions(designItems: IDesignItem[], extensionType?: ExtensionType, event?: Event, ignoredExtension?: IDesignerExtension, timeout?: number);\r\n  refreshAllExtensions(designItems: IDesignItem[], ignoredExtension?: IDesignerExtension, event?: Event);\r\n  refreshAllAppliedExtentions(event?: Event);\r\n  reapplyAllAppliedExtentions(filterDesignItems?: IDesignItem[], enabledExtensionTypes?: ExtensionType[]);\r\n  connected();\r\n  disconnected();\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/InvisibleElementExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from \"./OverlayLayer.js\";\r\n\r\nexport class InvisibleElementExtension extends AbstractExtension {\r\n  private _rect: SVGPathElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerCanvas, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const t = this.extendedItem.element.getBoxQuads({relativeTo: this.designerCanvas.canvas})[0];\r\n    if (this._valuesHaveChanges(t.p1.x, t.p1.y, t.p2.x, t.p2.y, t.p3.x, t.p3.y, t.p4.x, t.p4.y)) {\r\n      this._rect = this._drawTransformedRect(t, 'svg-invisible-div', this._rect, OverlayLayer.Background);\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/InvisibleElementExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { InvisibleElementExtension as InvisibleElementExtension } from './InvisibleElementExtension.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport const invisibleElementExtensionShowOverlayOptionName = 'invisibleElementExtensionShowOverlay';\r\n\r\nexport class InvisibleElementExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  elementFilter: (designItem: IDesignItem) => boolean;\r\n\r\n  constructor(elementFilter: (designItem: IDesignItem) => boolean = (d) => d.name == 'div' && window.getComputedStyle(d.element).display != 'inline') {\r\n    this.elementFilter = elementFilter;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designerCanvas.instanceServiceContainer.designContext.extensionOptions[invisibleElementExtensionShowOverlayOptionName] !== false) {\r\n      if (this.elementFilter(designItem)) {\r\n        const st = window.getComputedStyle(designItem.element);\r\n        return st.backgroundColor == 'rgba(0, 0, 0, 0)' && st.borderStyle == 'none'\r\n      }\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new InvisibleElementExtension(extensionManager, designerCanvas, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-invisible-div { stroke: lightgray; fill: transparent; stroke-width: 1;\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/MarginExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from './OverlayLayer.js';\r\n\r\nexport class MarginExtension extends AbstractExtension {\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  private _path: SVGPathElement;\r\n  private _path2: SVGPathElement;\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const computedStyle = getComputedStyle(this.extendedItem.element);\r\n    if (computedStyle.margin !== '0px') {\r\n      const left = Number.parseFloat(computedStyle.marginLeft.replace('px', ''));\r\n      const top = Number.parseFloat(computedStyle.marginTop.replace('px', ''));\r\n      const right = Number.parseFloat(computedStyle.marginRight.replace('px', ''));\r\n      const bottom = Number.parseFloat(computedStyle.marginBottom.replace('px', ''));\r\n      if (!isNaN(left) && !isNaN(top) && !isNaN(right) && !isNaN(bottom)) {\r\n        const p = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n        if (!isNaN(p.p1.x)) {\r\n          if (this._valuesHaveChanges(left, top, right, bottom, p.p1.x, p.p1.y, p.p2.x, p.p2.y, p.p3.x, p.p3.y, p.p4.x, p.p4.y)) {\r\n            const p2 = this.extendedItem.element.getBoxQuads({ box: 'margin', relativeTo: this.designerCanvas.canvas })[0];\r\n            let d = \"M\" + [p.p1, p.p2, p.p3, p.p4].map(x => x.x + ',' + x.y).join(' ') + 'Z ';\r\n            d += \"M\" + [p2.p1, p2.p2, p2.p3, p2.p4].map(x => x.x + ',' + x.y).join(' ') + 'Z ';\r\n            this._path = this._drawPath(d, 'svg-margin-fill', this._path, OverlayLayer.Background);\r\n            this._path2 = this._drawPath(d, 'svg-margin', this._path2, OverlayLayer.Background);\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/MarginExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { MarginExtension } from './MarginExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { NodeType } from '../../../item/NodeType.js';\r\nimport { svgAsString } from '../../../helper/SvgHelper.js';\r\n\r\nexport class MarginExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType == NodeType.Element)\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new MarginExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  readonly style = css`\r\n    .svg-margin-fill { fill: color(display-p3 1 0 1 / 15%); fill-rule: evenodd; }\r\n    .svg-margin { fill: color(display-p3 1 0 1 / 80%); fill-rule: evenodd; mask: url(#mask-stripe-margin); }\r\n  `;\r\n\r\n  static readonly svgDefs = svgAsString`\r\n    <pattern id=\"pattern-stripe-margin\" patternUnits=\"userSpaceOnUse\" width=\"10\" height=\"10\" patternTransform=\"rotate(45)\" class=\"pattern\">\r\n      <line x1=\"0\" y=\"0\" x2=\"0\" y2=\"10\" stroke=\"color(display-p3 1 0 1 / 80%)\" stroke-width=\"1\"></line>\r\n    </pattern>\r\n    <mask id=\"mask-stripe-margin\">\r\n      <rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" fill=\"url(#pattern-stripe-margin)\" />\r\n    </mask>\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/MultipleSelectionRectExtension.ts",
    "content": "import { calculateOuterRect } from '../../../helper/ElementHelper.js';\nimport { filterChildPlaceItems } from '../../../helper/LayoutHelper.js';\nimport { IDesignItem } from '../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\nimport { AbstractExtension } from './AbstractExtension.js';\nimport { IExtensionManager } from './IExtensionManger.js';\nimport { OverlayLayer } from './OverlayLayer.js';\n\nexport class MultipleSelectionRectExtension extends AbstractExtension {\n  private _line1: SVGLineElement;\n  private _line2: SVGLineElement;\n  private _line3: SVGLineElement;\n  private _line4: SVGLineElement;\n  private _designerView: any;\n\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n    this._designerView = designerView;\n  }\n\n  override extend() {\n    this.refresh();\n  }\n\n  override refresh() {\n    let selection = this._designerView.instanceServiceContainer.selectionService.selectedElements;\n    if (selection.length > 1) {\n      selection = filterChildPlaceItems(selection);\n      let rect = calculateOuterRect(selection, this._designerView)\n\n      this._line1 = this._drawLine(rect.x, rect.y, rect.x + rect.width, rect.y, 'svg-multiple-rect-selection', this._line1, OverlayLayer.Background);\n      this._line2 = this._drawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height, 'svg-multiple-rect-selection', this._line2, OverlayLayer.Background);\n      this._line3 = this._drawLine(rect.x + rect.width, rect.y + rect.height, rect.x, rect.y + rect.height, 'svg-multiple-rect-selection', this._line3, OverlayLayer.Background);\n      this._line4 = this._drawLine(rect.x, rect.y + rect.height, rect.x, rect.y, 'svg-multiple-rect-selection', this._line4, OverlayLayer.Background);\n      this._line1.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\n      this._line2.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\n      this._line3.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\n      this._line4.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\n    }\n  }\n\n  override dispose() {\n    this._removeAllOverlays();\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/MultipleSelectionRectExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { MultipleSelectionRectExtension as MultipleSelectionRectExtension } from './MultipleSelectionRectExtension.js';\r\n\r\nexport class MultipleSelectionRectExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem && !(designItem.element instanceof HTMLTemplateElement);\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new MultipleSelectionRectExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-multiple-rect-selection { stroke: #909090; stroke-dasharray: 3; fill: transparent; stroke-width: 2; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/OverlayLayer.ts",
    "content": "export enum OverlayLayer {\r\n  Background = 10,\r\n  Normal = 20,\r\n  Foreground = 30\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PaddingExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { OverlayLayer } from './OverlayLayer.js';\r\n\r\nexport class PaddingExtension extends AbstractExtension {\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  private _path: SVGPathElement;\r\n  private _path2: SVGPathElement;\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const computedStyle = getComputedStyle(this.extendedItem.element);\r\n    if (computedStyle.padding !== '0px') {\r\n      let left = Number.parseFloat(computedStyle.paddingLeft.replace('px', ''));\r\n      let top = Number.parseFloat(computedStyle.paddingTop.replace('px', ''));\r\n      let right = Number.parseFloat(computedStyle.paddingRight.replace('px', ''));\r\n      let bottom = Number.parseFloat(computedStyle.paddingBottom.replace('px', ''));\r\n\r\n      left += Number.parseFloat(computedStyle.borderLeftWidth.replace('px', ''));\r\n      top += Number.parseFloat(computedStyle.borderTopWidth.replace('px', ''));\r\n      right += Number.parseFloat(computedStyle.borderRightWidth.replace('px', ''));\r\n      bottom += Number.parseFloat(computedStyle.borderBottomWidth.replace('px', ''));\r\n      if (!isNaN(left) && !isNaN(top) && !isNaN(right) && !isNaN(bottom)) {\r\n        const p = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n        if (!isNaN(p.p1.x)) {\r\n          if (this._valuesHaveChanges(left, top, right, bottom, p.p1.x, p.p1.y, p.p2.x, p.p2.y, p.p3.x, p.p3.y, p.p4.x, p.p4.y)) {\r\n            const p2 = this.extendedItem.element.getBoxQuads({ box: 'content', relativeTo: this.designerCanvas.canvas })[0];\r\n            let d = \"M\" + [p.p1, p.p2, p.p3, p.p4].map(x => x.x + ',' + x.y).join(' ') + 'Z ';\r\n            d += \"M\" + [p2.p1, p2.p2, p2.p3, p2.p4].map(x => x.x + ',' + x.y).join(' ') + 'Z ';\r\n            this._path = this._drawPath(d, 'svg-padding-fill', this._path, OverlayLayer.Background);\r\n            this._path2 = this._drawPath(d, 'svg-padding', this._path2, OverlayLayer.Background);\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PaddingExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { NodeType } from '../../../item/NodeType.js';\r\nimport { PaddingExtension } from './PaddingExtension.js';\r\nimport { svgAsString } from '../../../helper/SvgHelper.js';\r\n\r\nexport class PaddingExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType == NodeType.Element)\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new PaddingExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-padding { fill: #32cd3266; fill-rule: evenodd; }\r\n  `;\r\n\r\n  readonly style = css`\r\n    .svg-padding-fill { fill: #32cd3266; fill-rule: evenodd; }\r\n    .svg-padding { fill: #32cd32FF; fill-rule: evenodd; mask: url(#mask-stripe-padding); }\r\n  `;\r\n\r\n  static readonly svgDefs = svgAsString`\r\n    <pattern id=\"pattern-stripe-padding\" patternUnits=\"userSpaceOnUse\" width=\"10\" height=\"10\" patternTransform=\"rotate(45)\" class=\"pattern\">\r\n      <line x1=\"0\" y=\"0\" x2=\"0\" y2=\"10\" stroke=\"color(display-p3 1 0 1 / 80%)\" stroke-width=\"1\"></line>\r\n    </pattern>\r\n    <mask id=\"mask-stripe-padding\">\r\n      <rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" fill=\"url(#pattern-stripe-padding)\" />\r\n    </mask>\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PlacementExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport class PlacementExtension extends AbstractExtension {\r\n  private _rect: SVGRectElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  override refresh() {\r\n    const itemRect = this.designerCanvas.getNormalizedElementCoordinates(this.extendedItem.element);\r\n    const computedStyle = getComputedStyle(this.extendedItem.element);\r\n    const left = Number.parseFloat(computedStyle.marginLeft.replace('px', ''));\r\n    const top = Number.parseFloat(computedStyle.marginTop.replace('px', ''));\r\n    const right = Number.parseFloat(computedStyle.marginRight.replace('px', ''));\r\n    const bottom = Number.parseFloat(computedStyle.marginBottom.replace('px', ''));\r\n    this._rect = this._drawRect(itemRect.x - left, itemRect.y - top, left + itemRect.width + right, top + itemRect.height + bottom, 'svg-placement', this._rect);\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PlacementExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { PlacementExtension } from './PlacementExtension.js';\r\n\r\nexport class PlacementExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return true;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new PlacementExtension(extensionManager, designerView, designItem);\r\n  }\r\n  \r\n  static readonly style = css`\r\n    .svg-placement { stroke: #90caf9; fill: none; }\r\n  `;    \r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PositionExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport class PositionExtension extends AbstractExtension {\r\n  private _line1: SVGLineElement;\r\n  private _line2: SVGLineElement;\r\n  private _line3: SVGLineElement;\r\n  private _line4: SVGLineElement;\r\n  private _textX: [SVGFilterElement, SVGFEFloodElement, SVGTextElement, SVGTextElement];\r\n  private _textY: [SVGFilterElement, SVGFEFloodElement, SVGTextElement, SVGTextElement];\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    this.refresh();\r\n  }\r\n\r\n  _oldValues = [];\r\n  override refresh() {\r\n\r\n    //TODO: show wich position is docked (no dashed line, but full). klick on value disables or enables docking? Disabling enables parent side, enabling both removes width or height.\r\n\r\n    const itemRect = this.designerCanvas.getNormalizedElementCoordinates(this.extendedItem.element);\r\n    const itemParentRect = this.designerCanvas.getNormalizedElementCoordinates(this.extendedItem.parent.element);\r\n\r\n    if (this._valuesHaveChanges(this.designerCanvas.scaleFactor, itemRect.x, itemRect.y, itemParentRect.x, itemParentRect.y, itemRect.height, itemRect.width, itemParentRect.height, itemParentRect.width)) {\r\n      this._line1 = this._drawLine(itemParentRect.x, itemRect.y + itemRect.height / 2, itemRect.x, itemRect.y + itemRect.height / 2, 'svg-position', this._line1);\r\n      this._line2 = this._drawLine(itemParentRect.x + itemParentRect.width, itemRect.y + itemRect.height / 2, itemRect.x + itemRect.width, itemRect.y + itemRect.height / 2, 'svg-position', this._line2);\r\n      this._line3 = this._drawLine(itemRect.x + itemRect.width / 2, itemParentRect.y, itemRect.x + itemRect.width / 2, itemRect.y, 'svg-position', this._line3);\r\n      this._line4 = this._drawLine(itemRect.x + itemRect.width / 2, itemParentRect.y + itemParentRect.height, itemRect.x + itemRect.width / 2, itemRect.y + itemRect.height, 'svg-position', this._line4);\r\n      this._line1.style.strokeWidth = '' + (1 / this.designerCanvas.scaleFactor);\r\n      this._line2.style.strokeWidth = '' + (1 / this.designerCanvas.scaleFactor);\r\n      this._line3.style.strokeWidth = '' + (1 / this.designerCanvas.scaleFactor);\r\n      this._line4.style.strokeWidth = '' + (1 / this.designerCanvas.scaleFactor);\r\n      this._line1.style.strokeDasharray = '' + (4 / this.designerCanvas.scaleFactor);\r\n      this._line2.style.strokeDasharray = '' + (4 / this.designerCanvas.scaleFactor);\r\n      this._line3.style.strokeDasharray = '' + (4 / this.designerCanvas.scaleFactor);\r\n      this._line4.style.strokeDasharray = '' + (4 / this.designerCanvas.scaleFactor);\r\n\r\n      let rightIsUnit, bottomIsUnit;\r\n      if (this.extendedItem.element.computedStyleMap) {\r\n        const cm = this.extendedItem.element.computedStyleMap();\r\n        rightIsUnit = cm.get('right') instanceof CSSUnitValue;\r\n        bottomIsUnit = cm.get('bottom') instanceof CSSUnitValue;\r\n      } else {\r\n        rightIsUnit = !!(<HTMLElement>this.extendedItem.element)?.style?.right;\r\n        bottomIsUnit = !!(<HTMLElement>this.extendedItem.element)?.style?.bottom;\r\n      }\r\n\r\n      if (!rightIsUnit) {\r\n        let x = Math.round(itemRect.x - itemParentRect.x);\r\n        this._textX = this._drawTextWithBackground('' + x, itemParentRect.x + x / 2, itemRect.y + itemRect.height / 2, 'white', 'svg-position-text', this._textX);\r\n        this._textX[2].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n        this._textX[3].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n      } else {\r\n        let x = Math.round(itemParentRect.x + itemParentRect.width - (itemRect.x + itemRect.width));\r\n        this._textX = this._drawTextWithBackground('' + x, itemParentRect.x + itemParentRect.width - x / 2, itemRect.y + itemRect.height / 2, 'white', 'svg-position-text', this._textX);\r\n        this._textX[2].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n        this._textX[3].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n      }\r\n\r\n      if (!bottomIsUnit) {\r\n        let y = Math.round(itemRect.y - itemParentRect.y);\r\n        this._textY = this._drawTextWithBackground('' + y, itemRect.x + itemRect.width / 2, itemParentRect.y + y / 2, 'white', 'svg-position-text', this._textY);\r\n        this._textY[2].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n        this._textY[3].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n      } else {\r\n        let y = Math.round(itemParentRect.y + itemParentRect.height - (itemRect.y + itemRect.height));\r\n        this._textY = this._drawTextWithBackground('' + y, itemRect.x + itemRect.width / 2, itemParentRect.y + itemParentRect.height - y / 2, 'white', 'svg-position-text', this._textY);\r\n        this._textY[2].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n        this._textY[3].style.fontSize = (12 / this.designerCanvas.scaleFactor) + 'px';\r\n      }\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PositionExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { PositionExtension } from './PositionExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class PositionExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (!designItem?.parent || designItem.element instanceof HTMLTemplateElement)\r\n      return false;\r\n    const cs = getComputedStyle((<HTMLElement>designItem.element));\r\n    if (cs.position === 'relative' || cs.position === 'absolute')\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new PositionExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-position-text { text-anchor: middle; alignment-baseline: central; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PreviousElementSelectExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport class PreviousElementSelectExtension extends AbstractExtension {\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  private _path: SVGPathElement;\r\n  private _rect: SVGRectElement;\r\n  private _clickRect: SVGRectElement;\r\n  private _g: SVGGElement;\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!transformedCornerPoints)\r\n      return;\r\n\r\n    if (!isNaN(transformedCornerPoints.p2.x)) {\r\n      if (this._valuesHaveChanges(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, this.designerCanvas.scaleFactor)) {\r\n        const angle = Math.atan2((transformedCornerPoints.p2.y - transformedCornerPoints.p1.y), (transformedCornerPoints.p2.x - transformedCornerPoints.p1.x)) * 180 / Math.PI;\r\n        const h = (15 / this.designerCanvas.scaleFactor);\r\n        this._rect = this._drawRect(0, 0, h, h, 'svg-previous-select', this._rect);\r\n        this._clickRect = this._drawRect(0, 0, h, h, 'svg-invisible', this._clickRect);\r\n        if (!this._g) {\r\n          this._g = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\r\n          this._g.setAttribute('class', 'svg-previous-select');\r\n          this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n          this._path.setAttribute('d', 'm4 12 1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z');\r\n          this._g.appendChild(this._rect);\r\n          this._g.appendChild(this._path);\r\n          this._addOverlay(this._g);\r\n          this._clickRect.onpointerdown = (e) => {\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n            this.extendedItem.instanceServiceContainer.selectionService.setSelectedElements([this.extendedItem.parent]);\r\n          };\r\n          this._clickRect.onpointermove = (e) => {\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n          }\r\n          this._g.appendChild(this._clickRect);\r\n        }\r\n        this._path.style.scale = (0.6 / this.designerCanvas.scaleFactor).toString();\r\n        this._g.style.translate = (transformedCornerPoints.p2.x - (14.5 / this.designerCanvas.scaleFactor)) + 'px ' + (transformedCornerPoints.p2.y - (15 / this.designerCanvas.scaleFactor)) + 'px';\r\n        this._g.style.rotate =  angle + 'deg';\r\n        this._g.style.transformOrigin = '100% 100%';\r\n        this._g.style.transformBox = 'fill-box'\r\n      }\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/PreviousElementSelectExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { PreviousElementSelectExtension } from './PreviousElementSelectExtension.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class PreviousElementSelectExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem && !(designItem.element instanceof HTMLTemplateElement);\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas,  designItem: IDesignItem): IDesignerExtension {\r\n    return new PreviousElementSelectExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    rect.svg-previous-select { stroke: none; fill: #3899ec; pointer-events: auto; }\r\n    g.svg-previous-select { fill: white; pointer-events: auto; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ResizeExtension.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IPoint } from \"../../../../interfaces/IPoint.js\";\r\nimport { ISize } from '../../../../interfaces/ISize.js';\r\nimport { getContentBoxContentOffsets } from '../../../helper/ElementHelper.js';\r\nimport { getElementLocalToCanvasMatrix } from '../../../helper/GridHelper.js';\r\nimport { roundValue } from '../../../helper/LayoutHelper.js';\r\nimport { getElementSize } from '../../../helper/getBoxQuads.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport function normalizeToAbsolutePosition(element: HTMLElement, normalizeProperty: \"left\" | \"top\") {\r\n  switch (normalizeProperty) {\r\n    case \"left\":\r\n      let left = getComputedStyle(element).left;\r\n      (<HTMLElement>element).style.removeProperty('right');\r\n      (<HTMLElement>element).style.left = left;\r\n      return left;\r\n    case \"top\":\r\n      let top = getComputedStyle(element).top;\r\n      (<HTMLElement>element).style.removeProperty('bottom');\r\n      (<HTMLElement>element).style.top = top;\r\n      return top;\r\n  }\r\n}\r\n\r\n//TODO: use PlacementService, size is not always width/height could also be margin etc...\r\n//      also when elment aligned to bottom, will it later also be?\r\nexport class ResizeExtension extends AbstractExtension {\r\n\r\n  private resizeAllSelected: boolean;\r\n  private _initialSizes: ISize[] | null = null;\r\n  private _actionModeStarted!: string;\r\n  private _initialPoint: IPoint | null = null;\r\n  private _offsetPoint!: IPoint;\r\n  private _circle1?: SVGCircleElement;\r\n  private _circle2?: SVGCircleElement;\r\n  private _circle3?: SVGCircleElement;\r\n  private _circle4?: SVGCircleElement;\r\n  private _circle5?: SVGCircleElement;\r\n  private _circle6?: SVGCircleElement;\r\n  private _circle7?: SVGCircleElement;\r\n  private _circle8?: SVGCircleElement;\r\n  private _initialHandleCanvasPoint: DOMPoint | null = null;\r\n  private _initialLocalToCanvasMatrix: DOMMatrix | null = null;\r\n  private _initialBorderBoxSize: ISize | null = null;\r\n  private _initialFixedResizeAnchor: DOMPoint | null = null;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, extendedItem: IDesignItem, resizeAllSelected: boolean) {\r\n    super(extensionManager, designerCanvas, extendedItem);\r\n    this.resizeAllSelected = resizeAllSelected;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    let transformedCornerPoints = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!transformedCornerPoints)\r\n      return;\r\n\r\n    if (isNaN(transformedCornerPoints.p1.x) || isNaN(transformedCornerPoints.p1.x)) {\r\n      this.remove();\r\n      return;\r\n    }\r\n    if (this._valuesHaveChanges(this.designerCanvas.zoomFactor, transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, transformedCornerPoints.p4.x, transformedCornerPoints.p4.y)) {\r\n      this._circle1 = this._drawResizerOverlay(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, 'nw-resize', this._circle1);\r\n      this._circle2 = this._drawResizerOverlay((transformedCornerPoints.p1.x + (transformedCornerPoints.p2.x - transformedCornerPoints.p1.x) / 2), (transformedCornerPoints.p1.y + (transformedCornerPoints.p2.y - transformedCornerPoints.p1.y) / 2), 'n-resize', this._circle2);\r\n      this._circle3 = this._drawResizerOverlay(transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, 'ne-resize', this._circle3);\r\n\r\n      this._circle4 = this._drawResizerOverlay((transformedCornerPoints.p1.x + (transformedCornerPoints.p4.x - transformedCornerPoints.p1.x) / 2), (transformedCornerPoints.p1.y + (transformedCornerPoints.p4.y - transformedCornerPoints.p1.y) / 2), 'w-resize', this._circle4);\r\n      this._circle5 = this._drawResizerOverlay(transformedCornerPoints.p4.x, transformedCornerPoints.p4.y, 'sw-resize', this._circle5);\r\n\r\n      this._circle6 = this._drawResizerOverlay((transformedCornerPoints.p4.x + (transformedCornerPoints.p3.x - transformedCornerPoints.p4.x) / 2), (transformedCornerPoints.p4.y + (transformedCornerPoints.p3.y - transformedCornerPoints.p4.y) / 2), 's-resize', this._circle6);\r\n      this._circle8 = this._drawResizerOverlay((transformedCornerPoints.p2.x + (transformedCornerPoints.p3.x - transformedCornerPoints.p2.x) / 2), (transformedCornerPoints.p2.y + (transformedCornerPoints.p3.y - transformedCornerPoints.p2.y) / 2), 'e-resize', this._circle8);\r\n\r\n      this._circle7 = this._drawResizerOverlay(transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, 'se-resize', this._circle7);\r\n    }\r\n  }\r\n\r\n  _drawResizerOverlay(x: number, y: number, cursor: string, oldCircle?: SVGCircleElement): SVGCircleElement {\r\n    let circle = this._drawCircle(x, y, this.designerCanvas.serviceContainer.options.resizerPixelSize / this.designerCanvas.zoomFactor, 'svg-primary-resizer', oldCircle);\r\n    circle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n    if (!oldCircle) {\r\n      circle.addEventListener(EventNames.PointerDown, event => this._pointerActionTypeResize(circle, event, cursor));\r\n      circle.addEventListener(EventNames.PointerMove, event => this._pointerActionTypeResize(circle, event, cursor));\r\n      circle.addEventListener(EventNames.PointerUp, event => this._pointerActionTypeResize(circle, event, cursor));\r\n    }\r\n    circle.style.cursor = cursor;\r\n    return circle;\r\n  }\r\n\r\n  _pointerActionTypeResize(circle: SVGCircleElement, event: PointerEvent, actionMode: string) {\r\n    event.stopPropagation();\r\n    const currentPoint = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    //TODO: calculate new position and size in the extension\r\n    //aply the values with the position service\r\n    //don't switch from left positioning to right and so on...\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        const cx = parseFloat(circle.getAttribute('cx') ?? '0');\r\n        const cy = parseFloat(circle.getAttribute('cy') ?? '0');\r\n        this._offsetPoint = { x: cx - currentPoint.x, y: cy - currentPoint.y };\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        this._initialPoint = currentPoint;\r\n        this._initialHandleCanvasPoint = new DOMPoint(cx, cy);\r\n        this._initialLocalToCanvasMatrix = getElementLocalToCanvasMatrix(this.extendedItem);\r\n        this._initialBorderBoxSize = getElementSize(this.extendedItem.element);\r\n        this._initialFixedResizeAnchor = this._getFixedResizeAnchor(this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas, iframes: this.designerCanvas.iframes })[0], actionMode);\r\n        this._initialSizes = [];\r\n        this._actionModeStarted = actionMode;\r\n\r\n        this._initialSizes.push(this._getInitialSize(this.extendedItem.element));\r\n\r\n        if (this.resizeAllSelected) {\r\n          for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n            this._initialSizes.push(this._getInitialSize(designItem.element));\r\n          }\r\n        }\r\n        if (this.designerCanvas.alignOnSnap)\r\n          this.designerCanvas.snapLines.calculateSnaplines(this.designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n\r\n        this.prepareResize(this.extendedItem, this._actionModeStarted)\r\n        if (this.resizeAllSelected) {\r\n          for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n            if (designItem !== this.extendedItem) {\r\n              this.prepareResize(designItem, this._actionModeStarted);\r\n            }\r\n          }\r\n        }\r\n\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._initialPoint) {\r\n          if (!this._initialSizes || !this._initialHandleCanvasPoint || !this._initialLocalToCanvasMatrix || !this._initialBorderBoxSize) {\r\n            return;\r\n          }\r\n\r\n          const containerStyle = getComputedStyle(this.extendedItem.parent.element);\r\n          const containerService = this.designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this.extendedItem.parent, containerStyle))\r\n\r\n          const diff = containerService.placePoint(event, this.designerCanvas, this.extendedItem.parent, this._initialPoint, { x: 0, y: 0 }, currentPoint, this.designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n          const currentHandleCanvasPoint = new DOMPoint(diff.x + this._offsetPoint.x, diff.y + this._offsetPoint.y);\r\n          const localHandleDelta = this._getLocalHandleDelta(currentHandleCanvasPoint);\r\n          if (!localHandleDelta) {\r\n            return;\r\n          }\r\n\r\n          let deltaX = localHandleDelta.x;\r\n          let deltaY = localHandleDelta.y;\r\n\r\n          if (event.shiftKey) {\r\n            deltaX = deltaX < deltaY ? deltaX : deltaY;\r\n            deltaY = deltaX;\r\n          }\r\n\r\n          let i = 0;\r\n\r\n          let width = null;\r\n          let height = null;\r\n\r\n          switch (this._actionModeStarted) {\r\n            case 'e-resize':\r\n              width = (this._initialSizes[i].width + deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width + deltaX) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'se-resize':\r\n              width = (this._initialSizes[i].width + deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n              height = (this._initialSizes[i].height + deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width + deltaX) + 'px';\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height + deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 's-resize':\r\n              height = (this._initialSizes[i].height + deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height + deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'sw-resize':\r\n              width = (this._initialSizes[i].width - deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n              height = (this._initialSizes[i].height + deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width - deltaX) + 'px';\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height + deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'w-resize':\r\n              width = (this._initialSizes[i].width - deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width - deltaX) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'nw-resize':\r\n              width = (this._initialSizes[i].width - deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n              height = (this._initialSizes[i].height - deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width - deltaX) + 'px';\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height - deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'n-resize':\r\n              height = (this._initialSizes[i].height - deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height - deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n            case 'ne-resize':\r\n              width = (this._initialSizes[i].width + deltaX);\r\n              (<HTMLElement>this.extendedItem.element).style.width = roundValue(this.extendedItem, width) + 'px';\r\n              height = (this._initialSizes[i].height - deltaY);\r\n              (<HTMLElement>this.extendedItem.element).style.height = roundValue(this.extendedItem, height) + 'px';\r\n\r\n              if (this.resizeAllSelected) {\r\n                i++;\r\n                for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n                  if (designItem !== this.extendedItem) {\r\n                    (<HTMLElement>designItem.element).style.width = roundValue(this.extendedItem, this._initialSizes[i].width + deltaX) + 'px';\r\n                    (<HTMLElement>designItem.element).style.height = roundValue(this.extendedItem, this._initialSizes[i].height - deltaY) + 'px';\r\n                  }\r\n                }\r\n              }\r\n              break;\r\n          }\r\n\r\n          const currentBorderBoxSize = this._getCurrentBorderBoxSize(deltaX, deltaY);\r\n          if (!currentBorderBoxSize) {\r\n            return;\r\n          }\r\n\r\n          this._applyAnchorCorrection(currentBorderBoxSize);\r\n\r\n          const resizedElements = [this.extendedItem, this.extendedItem.parent];\r\n          if (this.resizeAllSelected)\r\n            resizedElements.push(...this.designerCanvas.instanceServiceContainer.selectionService.selectedElements)\r\n          this.extensionManager.refreshExtensions(resizedElements);\r\n          this.designerCanvas?.raiseDesignItemsChanged(resizedElements, 'resize', false);\r\n        }\r\n        break;\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        if (!this._initialPoint) {\r\n          return;\r\n        }\r\n\r\n        let cg = this.extendedItem.openGroup((this.resizeAllSelected && this.designerCanvas.instanceServiceContainer.selectionService.selectedElements.length > 1) ? \"Resize Elements\" : \"Resize &lt;\" + this.extendedItem.name + \"&gt;\");\r\n        try {\r\n          const element = <HTMLElement>this.extendedItem.element;\r\n          this.extendedItem.setStyle('width', (<HTMLElement>this.extendedItem.element).style.width);\r\n          this.extendedItem.setStyle('height', (<HTMLElement>this.extendedItem.element).style.height);\r\n\r\n          let left = parseFloat(normalizeToAbsolutePosition(element, 'left'));\r\n          let top = parseFloat(normalizeToAbsolutePosition(element, 'top'));\r\n          const anchorCorrection = this._getAnchorCorrectionInParent(getElementSize(this.extendedItem.element));\r\n          if (anchorCorrection) {\r\n            left += anchorCorrection.x;\r\n            top += anchorCorrection.y;\r\n            element.style.left = roundValue(this.extendedItem, left) + 'px';\r\n            element.style.top = roundValue(this.extendedItem, top) + 'px';\r\n          }\r\n\r\n          this.extendedItem.setStyle('left', roundValue(this.extendedItem, left) + 'px');\r\n          this.extendedItem.setStyle('top', roundValue(this.extendedItem, top) + 'px');\r\n\r\n          if (this.resizeAllSelected) {\r\n            for (const designItem of this.designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n              if (designItem !== this.extendedItem) {\r\n                designItem.setStyle('width', (<HTMLElement>designItem.element).style.width);\r\n                designItem.setStyle('height', (<HTMLElement>designItem.element).style.height);\r\n\r\n                designItem.setStyle('left', roundValue(this.extendedItem, parseFloat(normalizeToAbsolutePosition(<HTMLElement>designItem.element, 'left'))) + 'px');\r\n                designItem.setStyle('top', roundValue(this.extendedItem, parseFloat(normalizeToAbsolutePosition(<HTMLElement>designItem.element, 'top'))) + 'px');\r\n              }\r\n            }\r\n            this.designerCanvas?.raiseDesignItemsChanged(this.designerCanvas.instanceServiceContainer.selectionService.selectedElements, 'resize', true);\r\n          }\r\n          else {\r\n            this.designerCanvas?.raiseDesignItemsChanged([this.extendedItem], 'resize', true);\r\n          }\r\n          cg.commit();\r\n        }\r\n        catch (err) {\r\n          cg.abort();\r\n          console.error(err)\r\n        }\r\n        this._initialSizes = null;\r\n        this._initialPoint = null;\r\n        this._initialHandleCanvasPoint = null;\r\n        this._initialLocalToCanvasMatrix = null;\r\n        this._initialBorderBoxSize = null;\r\n        this._initialFixedResizeAnchor = null;\r\n        break;\r\n    }\r\n  }\r\n\r\n  private _getLocalHandleDelta(currentHandleCanvasPoint: DOMPoint): DOMPoint | null {\r\n    if (!this._initialHandleCanvasPoint || !this._initialLocalToCanvasMatrix) {\r\n      return null;\r\n    }\r\n\r\n    const localToCanvasMatrix = new DOMMatrix([\r\n      this._initialLocalToCanvasMatrix.a,\r\n      this._initialLocalToCanvasMatrix.b,\r\n      this._initialLocalToCanvasMatrix.c,\r\n      this._initialLocalToCanvasMatrix.d,\r\n      0,\r\n      0,\r\n    ]);\r\n\r\n    if (!localToCanvasMatrix.is2D) {\r\n      return null;\r\n    }\r\n\r\n    const canvasDelta = new DOMPoint(\r\n      currentHandleCanvasPoint.x - this._initialHandleCanvasPoint.x,\r\n      currentHandleCanvasPoint.y - this._initialHandleCanvasPoint.y,\r\n    );\r\n\r\n    return localToCanvasMatrix.inverse().transformPoint(canvasDelta);\r\n  }\r\n\r\n  private _getCurrentBorderBoxSize(deltaX: number, deltaY: number): ISize | null {\r\n    if (!this._initialBorderBoxSize) {\r\n      return null;\r\n    }\r\n\r\n    let width = this._initialBorderBoxSize.width;\r\n    let height = this._initialBorderBoxSize.height;\r\n\r\n    switch (this._actionModeStarted) {\r\n      case 'e-resize':\r\n      case 'se-resize':\r\n      case 'ne-resize':\r\n        width += deltaX;\r\n        break;\r\n      case 'w-resize':\r\n      case 'sw-resize':\r\n      case 'nw-resize':\r\n        width -= deltaX;\r\n        break;\r\n    }\r\n\r\n    switch (this._actionModeStarted) {\r\n      case 's-resize':\r\n      case 'se-resize':\r\n      case 'sw-resize':\r\n        height += deltaY;\r\n        break;\r\n      case 'n-resize':\r\n      case 'ne-resize':\r\n      case 'nw-resize':\r\n        height -= deltaY;\r\n        break;\r\n    }\r\n\r\n    return { width, height };\r\n  }\r\n\r\n  private _getInitialSize(element: Element): ISize {\r\n    const size = getElementSize(element);\r\n    let contentBoxOffset: IPoint = { x: 0, y: 0 };\r\n    if (getComputedStyle(<HTMLElement>element).boxSizing == 'content-box') {\r\n      contentBoxOffset = getContentBoxContentOffsets(<HTMLElement>element);\r\n    }\r\n    return { width: size.width - contentBoxOffset.x, height: size.height - contentBoxOffset.y };\r\n  }\r\n\r\n  private _getFixedResizeAnchor(quad: DOMQuad, mode: string): DOMPoint | null {\r\n    if (!quad) {\r\n      return null;\r\n    }\r\n\r\n    switch (mode) {\r\n      case 'e-resize':\r\n        return new DOMPoint((quad.p1.x + quad.p4.x) / 2, (quad.p1.y + quad.p4.y) / 2);\r\n      case 'se-resize':\r\n        return new DOMPoint(quad.p1.x, quad.p1.y);\r\n      case 's-resize':\r\n        return new DOMPoint((quad.p1.x + quad.p2.x) / 2, (quad.p1.y + quad.p2.y) / 2);\r\n      case 'sw-resize':\r\n        return new DOMPoint(quad.p2.x, quad.p2.y);\r\n      case 'w-resize':\r\n        return new DOMPoint((quad.p2.x + quad.p3.x) / 2, (quad.p2.y + quad.p3.y) / 2);\r\n      case 'nw-resize':\r\n        return new DOMPoint(quad.p3.x, quad.p3.y);\r\n      case 'n-resize':\r\n        return new DOMPoint((quad.p4.x + quad.p3.x) / 2, (quad.p4.y + quad.p3.y) / 2);\r\n      case 'ne-resize':\r\n        return new DOMPoint(quad.p4.x, quad.p4.y);\r\n      default:\r\n        return null;\r\n    }\r\n  }\r\n\r\n  private _getFixedResizeAnchorLocal(size: ISize, mode: string): DOMPoint | null {\r\n    switch (mode) {\r\n      case 'e-resize':\r\n        return new DOMPoint(0, size.height / 2);\r\n      case 'se-resize':\r\n        return new DOMPoint(0, 0);\r\n      case 's-resize':\r\n        return new DOMPoint(size.width / 2, 0);\r\n      case 'sw-resize':\r\n        return new DOMPoint(size.width, 0);\r\n      case 'w-resize':\r\n        return new DOMPoint(size.width, size.height / 2);\r\n      case 'nw-resize':\r\n        return new DOMPoint(size.width, size.height);\r\n      case 'n-resize':\r\n        return new DOMPoint(size.width / 2, size.height);\r\n      case 'ne-resize':\r\n        return new DOMPoint(0, size.height);\r\n      default:\r\n        return null;\r\n    }\r\n  }\r\n\r\n  private _getAnchorCorrectionInParent(currentBorderBoxSize: ISize): IPoint | null {\r\n    if (!this._initialFixedResizeAnchor) {\r\n      return null;\r\n    }\r\n\r\n    const currentAnchorLocal = this._getFixedResizeAnchorLocal(currentBorderBoxSize, this._actionModeStarted);\r\n    if (!currentAnchorLocal) {\r\n      return null;\r\n    }\r\n\r\n    const initialAnchorInParent = this.extendedItem.parent.element.convertPointFromNode(this._initialFixedResizeAnchor, this.designerCanvas.canvas, { iframes: this.designerCanvas.iframes });\r\n    const currentAnchor = this.designerCanvas.canvas.convertPointFromNode(currentAnchorLocal, this.extendedItem.element, { iframes: this.designerCanvas.iframes });\r\n    const currentAnchorInParent = this.extendedItem.parent.element.convertPointFromNode(currentAnchor, this.designerCanvas.canvas, { iframes: this.designerCanvas.iframes });\r\n\r\n    return {\r\n      x: initialAnchorInParent.x - currentAnchorInParent.x,\r\n      y: initialAnchorInParent.y - currentAnchorInParent.y\r\n    };\r\n  }\r\n\r\n  private _applyAnchorCorrection(currentBorderBoxSize: ISize) {\r\n    const anchorCorrection = this._getAnchorCorrectionInParent(currentBorderBoxSize);\r\n    if (!anchorCorrection) {\r\n      return;\r\n    }\r\n\r\n    const element = <HTMLElement>this.extendedItem.element;\r\n    const left = parseFloat(normalizeToAbsolutePosition(element, 'left')) + anchorCorrection.x;\r\n    const top = parseFloat(normalizeToAbsolutePosition(element, 'top')) + anchorCorrection.y;\r\n    element.style.left = roundValue(this.extendedItem, left) + 'px';\r\n    element.style.top = roundValue(this.extendedItem, top) + 'px';\r\n  }\r\n\r\n  private prepareResize(designItem: IDesignItem, mode: string) {\r\n    let top: string | null = null;\r\n    let bottom: string | null = null;\r\n    let left: string | null = null;\r\n    let right: string | null = null;\r\n\r\n    switch (this._actionModeStarted) {\r\n      case 'e-resize':\r\n        left = getComputedStyle(designItem.element).left;\r\n        (<HTMLElement>designItem.element).style.removeProperty('right');\r\n        (<HTMLElement>designItem.element).style.left = left;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = this._initialComputedTransformOrigins[i].x + 'px ' + this._initialComputedTransformOrigins[i].y + 'px';\r\n        break;\r\n      case 'se-resize':\r\n        top = getComputedStyle(designItem.element).top;\r\n        (<HTMLElement>designItem.element).style.removeProperty('bottom');\r\n        (<HTMLElement>designItem.element).style.top = top;\r\n        left = getComputedStyle(designItem.element).left;\r\n        (<HTMLElement>designItem.element).style.removeProperty('right');\r\n        (<HTMLElement>designItem.element).style.left = left;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = this._initialComputedTransformOrigins[i].x + 'px ' + this._initialComputedTransformOrigins[i].y + 'px';\r\n        break;\r\n      case 's-resize':\r\n        top = getComputedStyle(designItem.element).top;\r\n        (<HTMLElement>designItem.element).style.removeProperty('bottom');\r\n        (<HTMLElement>designItem.element).style.top = top;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = this._initialComputedTransformOrigins[i].x + 'px ' + this._initialComputedTransformOrigins[i].y + 'px';\r\n        break;\r\n      case 'sw-resize':\r\n        top = getComputedStyle(designItem.element).top;\r\n        (<HTMLElement>designItem.element).style.removeProperty('bottom');\r\n        (<HTMLElement>designItem.element).style.top = top;\r\n        right = getComputedStyle(designItem.element).right;\r\n        (<HTMLElement>designItem.element).style.removeProperty('left');\r\n        (<HTMLElement>designItem.element).style.right = right;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = 'calc(100% - ' + this._initialComputedTransformOrigins[i].x + 'px) ' + this._initialComputedTransformOrigins[i].y + 'px';\r\n        break;\r\n      case 'w-resize':\r\n        right = getComputedStyle(designItem.element).right;\r\n        (<HTMLElement>designItem.element).style.removeProperty('left');\r\n        (<HTMLElement>designItem.element).style.right = right;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = 'calc(100% - ' + this._initialComputedTransformOrigins[i].x + 'px) ' + this._initialComputedTransformOrigins[i].y + 'px';\r\n        break;\r\n      case 'nw-resize':\r\n        bottom = getComputedStyle(designItem.element).bottom;\r\n        (<HTMLElement>designItem.element).style.removeProperty('top');\r\n        (<HTMLElement>designItem.element).style.bottom = bottom;\r\n        right = getComputedStyle(designItem.element).right;\r\n        (<HTMLElement>designItem.element).style.removeProperty('left');\r\n        (<HTMLElement>designItem.element).style.right = right;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = 'calc(100% - ' + this._initialComputedTransformOrigins[i].x + 'px) ' + 'calc(100% - ' + this._initialComputedTransformOrigins[i].y + 'px)';\r\n        break;\r\n      case 'n-resize':\r\n        bottom = getComputedStyle(designItem.element).bottom;\r\n        (<HTMLElement>designItem.element).style.removeProperty('top');\r\n        (<HTMLElement>designItem.element).style.bottom = bottom;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = 'calc(100% - ' + this._initialComputedTransformOrigins[i].x + 'px) ' + 'calc(100% - ' + this._initialComputedTransformOrigins[i].y + 'px)';\r\n        break;\r\n      case 'ne-resize':\r\n        bottom = getComputedStyle(designItem.element).bottom;\r\n        (<HTMLElement>designItem.element).style.removeProperty('top');\r\n        (<HTMLElement>designItem.element).style.bottom = bottom;\r\n        left = getComputedStyle(designItem.element).left;\r\n        (<HTMLElement>designItem.element).style.removeProperty('right');\r\n        (<HTMLElement>designItem.element).style.left = left;\r\n        //(<HTMLElement>designItem.element).style.transformOrigin = this._initialComputedTransformOrigins[i].x + 'px ' + 'calc(100% - ' + this._initialComputedTransformOrigins[i].y + 'px)';\r\n        break;\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/ResizeExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { ResizeExtension } from './ResizeExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { NodeType } from \"../../../item/NodeType.js\";\r\n\r\nexport class ResizeExtensionProvider implements IDesignerExtensionProvider {\r\n  private resizeAllSelected: boolean;\r\n\r\n  constructor(resizeAllSelected: boolean = false) {\r\n    this.resizeAllSelected = resizeAllSelected;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designerCanvas.readOnly)\r\n      return false;\r\n    if (designItem.element instanceof SVGElement || designItem.element instanceof HTMLTemplateElement)\r\n      return false;\r\n    return !designItem.isRootItem && designItem.nodeType == NodeType.Element;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new ResizeExtension(extensionManager, designerCanvas, designItem, this.resizeAllSelected);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-primary-resizer { stroke: #3899ec; fill: white; pointer-events: auto; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/SelectionDefaultExtension.ts",
    "content": "import { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { AbstractExtension } from './AbstractExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\n\r\nexport class SelectionDefaultExtension extends AbstractExtension {\r\n  private _line1: SVGLineElement;\r\n  private _line2: SVGLineElement;\r\n  private _line3: SVGLineElement;\r\n  private _line4: SVGLineElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!transformedCornerPoints)\r\n      return;\r\n\r\n    if (isNaN(transformedCornerPoints.p1.x) || isNaN(transformedCornerPoints.p2.x)) {\r\n      this.remove();\r\n      return;\r\n    }\r\n\r\n    if (this._valuesHaveChanges(this.designerCanvas.zoomFactor, transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, transformedCornerPoints.p4.x, transformedCornerPoints.p4.y)) {\r\n      this._line1 = this._drawLine(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, 'svg-selection', this._line1);\r\n      this._line2 = this._drawLine(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p4.x, transformedCornerPoints.p4.y, 'svg-selection', this._line2);\r\n      this._line3 = this._drawLine(transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, 'svg-selection', this._line3);\r\n      this._line4 = this._drawLine(transformedCornerPoints.p4.x, transformedCornerPoints.p4.y, transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, 'svg-selection', this._line4);\r\n      this._line1.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\r\n      this._line2.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\r\n      this._line3.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\r\n      this._line4.style.strokeWidth = (2 / this.designerCanvas.zoomFactor).toString();\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/SelectionDefaultExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from './IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from './IDesignerExtension.js';\r\nimport { SelectionDefaultExtension } from './SelectionDefaultExtension.js';\r\nimport { IExtensionManager } from './IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { NodeType } from '../../../item/NodeType.js';\r\n\r\nexport class SelectionDefaultExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    return !designItem.isRootItem && designItem.nodeType != NodeType.Comment && !(designItem.element instanceof HTMLTemplateElement);\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new SelectionDefaultExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-selection { stroke: #3899ec; fill: transparent; stroke-width: 2; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/block/BlockToolbarExtension.ts",
    "content": "import { html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { BasicStackedToolbarExtension } from \"../BasicStackedToolbarExtension.js\";\r\nimport { IExtensionManager } from \"../IExtensionManger.js\";\r\n\r\nexport class BlockToolbarExtension extends BasicStackedToolbarExtension {\r\n\r\n  protected static template = html`\r\n      <div style=\"height: 100%; width: 100%;\">\r\n        ${BasicStackedToolbarExtension.basicTemplate}\r\n      </div>\r\n    `;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event: MouseEvent) {\r\n      super.extend(cache, event);\r\n      this.refresh(cache, event);\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/block/BlockToolbarExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { BlockToolbarExtension } from './BlockToolbarExtension.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { basicStackedToolbarExtensionOverlayOptionName } from '../BasicStackedToolbarExtension.js';\r\n\r\nexport class BlockToolbarExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType === NodeType.Element) {\r\n      const d = getComputedStyle(designItem.element).display;\r\n      if (d === 'block' || d === 'inline'|| d === 'inline-block')\r\n        return designerCanvas.instanceServiceContainer.designContext.extensionOptions[basicStackedToolbarExtensionOverlayOptionName] !== false;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new BlockToolbarExtension(extensionManager, designerView, designItem);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/AbstractDesignViewConfigButton.ts",
    "content": "import { ContextMenu } from \"../../../../helper/contextMenu/ContextMenu.js\";\r\nimport { IContextMenuItem } from \"../../../../helper/contextMenu/IContextMenuItem.js\";\r\nimport { DesignerView } from \"../../designerView.js\";\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignViewConfigButtonsProvider } from \"./IDesignViewConfigButtonsProvider.js\";\r\n\r\nexport class AbstractDesignViewConfigButton implements IDesignViewConfigButtonsProvider {\r\n\r\n  protected settingName;\r\n\r\n  private content: string | HTMLElement;\r\n  private tooltp: string;\r\n  private contextmenu: IContextMenuItem[];\r\n\r\n  constructor(settingName: string, content: string | HTMLElement, tooltip: string, contextmenu?: IContextMenuItem[]) {\r\n    this.settingName = settingName;\r\n    this.content = content;\r\n    this.tooltp = tooltip;\r\n    this.contextmenu = contextmenu;\r\n  }\r\n\r\n  provideButtons(designerView: DesignerView, designerCanvas: IDesignerCanvas): HTMLElement[] {\r\n\r\n    const btn = document.createElement('div');\r\n    if (typeof this.content == 'string')\r\n      btn.innerHTML = this.content;\r\n    else\r\n      btn.appendChild(this.content);\r\n    btn.title = this.tooltp;\r\n    btn.className = 'toolbar-control';\r\n    designerCanvas.instanceServiceContainer.designContext.extensionOptionsChanged.on(() => {\r\n      if (extensionOptions[this.settingName] !== false)\r\n        btn.classList.add('selected');\r\n      else\r\n        btn.classList.remove('selected');\r\n    })\r\n    const extensionOptions = designerCanvas.instanceServiceContainer.designContext.extensionOptions;\r\n    if (extensionOptions[this.settingName] !== false)\r\n      btn.classList.add('selected');\r\n    btn.onclick = () => {\r\n      const val = extensionOptions[this.settingName]\r\n      extensionOptions[this.settingName] = val === false ? true : false;\r\n      if (extensionOptions[this.settingName] !== false)\r\n        btn.classList.add('selected');\r\n      else\r\n        btn.classList.remove('selected');\r\n    }\r\n\r\n    btn.oncontextmenu = (e) => {\r\n      e.preventDefault();\r\n      if (this.contextmenu) {\r\n        ContextMenu.show(this.contextmenu, e);\r\n      }\r\n    }\r\n\r\n    return [btn];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/ButtonSeperatorProvider.ts",
    "content": "import { DesignerView } from \"../../designerView.js\";\r\nimport { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\nimport { IDesignViewConfigButtonsProvider } from \"./IDesignViewConfigButtonsProvider.js\";\r\n\r\nexport class ButtonSeperatorProvider implements IDesignViewConfigButtonsProvider {\r\n\r\n  _space: number\r\n  constructor(space: number) {\r\n    this._space = space;\r\n  }\r\n  provideButtons(designerView: DesignerView, designerCanvas: IDesignerCanvas): HTMLElement[] {\r\n    const div = document.createElement('div');\r\n    div.style.width = this._space + 'px';\r\n    div.oncontextmenu = (e) => { e.preventDefault(); };\r\n    return [div];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/FlexboxExtensionDesignViewConfigButtons.ts",
    "content": "import { flexboxExtensionShowOverlayOptionName } from \"../flex/FlexboxExtensionProvider.js\";\r\nimport { AbstractDesignViewConfigButton } from \"./AbstractDesignViewConfigButton.js\";\r\n\r\nexport class FlexboxExtensionDesignViewConfigButtons extends AbstractDesignViewConfigButton {\r\n  constructor() {\r\n    super(flexboxExtensionShowOverlayOptionName, \"F\", \"show flexbox overlay\")\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/GridExtensionDesignViewConfigButtons.ts",
    "content": "import { gridExtensionShowOverlayOptionName } from \"../grid/DisplayGridExtensionProvider.js\";\r\nimport { AbstractDesignViewConfigButton } from \"./AbstractDesignViewConfigButton.js\";\r\n\r\nexport class GridExtensionDesignViewConfigButtons extends AbstractDesignViewConfigButton {\r\n  constructor() {\r\n    super(gridExtensionShowOverlayOptionName, \"G\", \"show grid overlay\")\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/IDesignViewConfigButtonsProvider.ts",
    "content": "import { DesignerView } from \"../../designerView.js\";\r\nimport { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\n\r\nexport interface IDesignViewConfigButtonsProvider {\r\n  provideButtons(designerView: DesignerView, designerCanvas: IDesignerCanvas): HTMLElement[]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/InvisibleElementExtensionDesignViewConfigButtons.ts",
    "content": "import { invisibleElementExtensionShowOverlayOptionName } from \"../InvisibleElementExtensionProvider.js\";\r\nimport { AbstractDesignViewConfigButton } from \"./AbstractDesignViewConfigButton.js\";\r\n\r\nexport class InvisibleElementExtensionDesignViewConfigButtons extends AbstractDesignViewConfigButton {\r\n    constructor() {\r\n    super(invisibleElementExtensionShowOverlayOptionName, \"I\", \"show invisible div overlay\")\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/OptionsContextMenuButton.ts",
    "content": "import { ContextMenu } from \"../../../../helper/contextMenu/ContextMenu.js\";\r\nimport { DesignerView } from \"../../designerView.js\";\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignViewConfigButtonsProvider } from \"./IDesignViewConfigButtonsProvider.js\";\r\n\r\nexport class OptionsContextMenuButton implements IDesignViewConfigButtonsProvider {\r\n\r\n  constructor() {\r\n  }\r\n\r\n  provideButtons(designerView: DesignerView, designerCanvas: IDesignerCanvas): HTMLElement[] {\r\n    const btn = document.createElement('div');\r\n    btn.innerHTML = 'O';\r\n    btn.title = 'options';\r\n    btn.className = 'toolbar-control';\r\n    btn.onclick = (e) => {\r\n      this.showCtxMenu(e, designerCanvas);\r\n    }\r\n    btn.oncontextmenu = (e) => {\r\n      e.preventDefault();\r\n      this.showCtxMenu(e, designerCanvas);\r\n    }\r\n\r\n    return [btn];\r\n  }\r\n\r\n  protected prepareContextMenu(event: MouseEvent, designerCanvas: IDesignerCanvas) {\r\n    const ctxMenu = [\r\n      {\r\n        title: 'simulate hover on hover', checked: designerCanvas.instanceServiceContainer.designContext.extensionOptions.simulateHoverOnHover, action: () => {\r\n          designerCanvas.instanceServiceContainer.designContext.extensionOptions.simulateHoverOnHover = !designerCanvas.instanceServiceContainer.designContext.extensionOptions.simulateHoverOnHover;\r\n        }\r\n      },\r\n      {\r\n        title: 'select unhitable elements', checked: designerCanvas.instanceServiceContainer.designContext.extensionOptions.selectUnhitableElements, action: () => {\r\n          designerCanvas.instanceServiceContainer.designContext.extensionOptions.selectUnhitableElements = !designerCanvas.instanceServiceContainer.designContext.extensionOptions.selectUnhitableElements;\r\n        }\r\n      },\r\n      {\r\n        title: 'pause animations', checked: designerCanvas.pauseAnimations, action: () => {\r\n          designerCanvas.pauseAnimations = !designerCanvas.pauseAnimations;\r\n        }\r\n      },\r\n      {\r\n        title: 'hide overflowing content', checked: designerCanvas.instanceServiceContainer.designContext.extensionOptions.hideOverflowingContent, action: () => {\r\n          designerCanvas.instanceServiceContainer.designContext.extensionOptions.hideOverflowingContent = !designerCanvas.instanceServiceContainer.designContext.extensionOptions.hideOverflowingContent;\r\n          if (designerCanvas.instanceServiceContainer.designContext.extensionOptions.hideOverflowingContent) {\r\n            for (let c of designerCanvas.rootDesignItem.children(true)) {\r\n              if (c.element instanceof HTMLElement) {\r\n                c.element.style.overflow = 'hidden';\r\n                c.element.style.whiteSpace = 'nowrap';\r\n              }\r\n            }\r\n          } else {\r\n            for (let c of designerCanvas.rootDesignItem.children(true)) {\r\n              if (c.element instanceof HTMLElement) {\r\n                c.element.style.overflow = c.hasStyle('overflow') ? c.getStyle('overflow') : '';\r\n                c.element.style.whiteSpace = c.hasStyle('white-space') ? c.getStyle('white-space') : '';\r\n              }\r\n            }\r\n          }\r\n        }\r\n      },\r\n    ]\r\n    return ctxMenu;\r\n  }\r\n\r\n  showCtxMenu(event: MouseEvent, designerCanvas: IDesignerCanvas) {\r\n    ContextMenu.show(this.prepareContextMenu(event, designerCanvas), event);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/RoundPixelsDesignViewConfigButton.ts",
    "content": "import { DesignerView } from \"../../designerView.js\";\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignViewConfigButtonsProvider } from \"./IDesignViewConfigButtonsProvider.js\";\r\n\r\nexport class RoundPixelsDesignViewConfigButton implements IDesignViewConfigButtonsProvider {\r\n\r\n  constructor() { }\r\n\r\n  provideButtons(designerView: DesignerView, designerCanvas: IDesignerCanvas): HTMLElement[] {\r\n\r\n    const btn = document.createElement('div');\r\n    btn.className = 'toolbar-control';\r\n    btn.title = 'round pixels to';\r\n\r\n    const ip = document.createElement('input');\r\n    ip.type = 'number';\r\n    ip.step = '1';\r\n    ip.min = '-1';\r\n    ip.valueAsNumber = designerView.serviceContainer.options.roundPixelsToDecimalPlaces;\r\n\r\n    ip.onchange = () => designerView.serviceContainer.options.roundPixelsToDecimalPlaces = ip.valueAsNumber;\r\n\r\n    btn.appendChild(ip);\r\n\r\n    return [btn];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/StylesheetServiceDesignViewConfigButtons.ts",
    "content": "import { AbstractDesignViewConfigButton } from \"./AbstractDesignViewConfigButton.js\";\r\n\r\nexport const enableStylesheetService = 'enableStylesheetService';\r\n\r\nexport class StylesheetServiceDesignViewConfigButtons extends AbstractDesignViewConfigButton {\r\n\r\n  constructor() {\r\n    super(enableStylesheetService, \"ss\", \"modify Stylesheet\")\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/buttons/ToolbarExtensionsDesignViewConfigButtons.ts",
    "content": "import { basicStackedToolbarExtensionOverlayOptionName } from \"../BasicStackedToolbarExtension.js\";\r\nimport { AbstractDesignViewConfigButton } from \"./AbstractDesignViewConfigButton.js\";\r\n\r\nexport class ToolbarExtensionsDesignViewConfigButtons extends AbstractDesignViewConfigButton {\r\n  constructor() {\r\n    super(basicStackedToolbarExtensionOverlayOptionName, \"T\", \"show Toolbars\")\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/AlignItemsContextMenu.ts",
    "content": "import { CommandType } from '../../../../../commandHandling/CommandType.js';\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { NodeType } from '../../../../item/NodeType.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\nimport { assetsPath } from '../../../../../Constants.js';\n\nexport class AlignItemsContextMenu implements IContextMenuExtension {\n\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n    if (designerCanvas.readOnly)\n      return false;\n    if (designItem?.instanceServiceContainer.selectionService.selectedElements.length > 1) {\n      return !designItem?.isRootItem && designItem?.nodeType == NodeType.Element;\n    }\n    return false;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\n    return [\n      { title: 'align left', icon: `<img src=\"${assetsPath + 'icons/alignHorizontalLeft.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeLeft }); } },\n      { title: 'align center', icon: `<img src=\"${assetsPath + 'icons/alignHorizontalCenter.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeCenter }); } },\n      { title: 'align right', icon: `<img src=\"${assetsPath + 'icons/alignHorizontalRight.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeRight }); } },\n      { title: 'distribute horizontal', icon: `<img src=\"${assetsPath + 'icons/horizontalDistribute.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.distributeHorizontal }); } },\n      { title: 'align top', icon: `<img src=\"${assetsPath + 'icons/alignVerticalTop.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeTop }); } },\n      { title: 'align middle', icon: `<img src=\"${assetsPath + 'icons/alignVerticalCenter.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeMiddle }); } },\n      { title: 'align bottom', icon: `<img src=\"${assetsPath + 'icons/alignVerticalBottom.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.arrangeBottom }); } },\n      { title: 'distribute vertical', icon: `<img src=\"${assetsPath + 'icons/verticalDistribute.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.distributeVertical }); } },\n    ]\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/BasicContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\n\n\ntype BasicContextMenuItem = Omit<IContextMenuItem, 'action'> & { action?: (event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) => void };\nexport class BasicContextMenu implements IContextMenuExtension {\n    private _item: BasicContextMenuItem;\n    private _shouldProvide: (event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) => boolean;\n\n    constructor(item: BasicContextMenuItem, shouldProvide?: (event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) => boolean) {\n        this._item = item;\n        this._shouldProvide = shouldProvide || (() => true);\n    }\n    public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n        return this._shouldProvide(event, designerCanvas, designItem, initiator);\n    }\n\n    public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator): IContextMenuItem[] {\n        return [{ ...this._item, action: (e) => this._item.action?.(e, designerCanvas, designItem, initiator)}];\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ChildContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\nimport { SeperatorContextMenu } from './SeperatorContextMenu.js';\n\nexport class ChildContextMenu implements IContextMenuExtension {\n    private _title: string;\n    private _contextMenus: IContextMenuExtension[];\n\n    constructor(title: string, ...contextMenus: IContextMenuExtension[]) {\n        this._title = title;\n        this._contextMenus = contextMenus;\n    }\n    public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n        return this._contextMenus.some(x => !(x instanceof SeperatorContextMenu) && x.shouldProvideContextmenu(event, designerView, designItem, initiator));\n    }\n\n    public provideContextMenuItems(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator): IContextMenuItem[] {\n        return [{ title: this._title, children: this._contextMenus.map(x => x.shouldProvideContextmenu(event, designerView, designItem, initiator) ? x.provideContextMenuItems(event, designerView, designItem, initiator) : []).flat() }];\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ChildrenContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { DesignItem } from '../../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class ChildrenContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return initiator == 'designer';\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    if (designItem) {\r\n      const lstItems = [...designItem.element.children];\r\n      if (lstItems.length > 0) {\r\n        return [{ title: 'children', children: [...lstItems.map(x => ({ title: 'select: ' + x.localName + (x.id ? ' (#' + x.id + ')' : ''), action: () => this._select(designerCanvas, x) }))] }];\r\n      }\r\n    }\r\n    return [];\r\n  }\r\n  private _select(designerView: IDesignerCanvas, element: Element) {\r\n    const item = DesignItem.GetOrCreateDesignItem(element, element, designerView.serviceContainer, designerView.instanceServiceContainer);\r\n    designerView.instanceServiceContainer.selectionService.setSelectedElements([item]);\r\n  }\r\n\r\n\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/CopyPasteContextMenu.ts",
    "content": "import { CommandType } from '../../../../../commandHandling/CommandType.js';\r\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\nimport { assetsPath } from '../../../../../Constants.js';\r\n\r\nexport class CopyPasteContextMenu implements IContextMenuExtension {\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return true;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    if (designerCanvas.readOnly)\r\n      return [\r\n        { title: 'copy', icon: `<img src=\"${assetsPath + 'icons/copy.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.copy }); }, shortCut: 'Ctrl + C', disabled: designItem === null }\r\n      ]\r\n\r\n    return [\r\n      { title: 'copy', icon: `<img src=\"${assetsPath + 'icons/copy.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.copy }); }, shortCut: 'Ctrl + C', disabled: designItem === null },\r\n      { title: 'cut', icon: `<img src=\"${assetsPath + 'icons/cut.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.cut }); }, shortCut: 'Ctrl + X', disabled: designItem === null },\r\n      { title: 'paste', icon: `<img src=\"${assetsPath + 'icons/paste.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.paste }); }, shortCut: 'Ctrl + V' },\r\n      { title: 'delete', icon: `<img src=\"${assetsPath + 'icons/delete.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.delete }); }, shortCut: 'Del', disabled: designItem === null },\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ForceCssContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class ForceCssContextMenu implements IContextMenuExtension {\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return designItem != null && designItem.nodeType == NodeType.Element;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      { title: ':hover', action: () => { designItem.cssForceHover = !designItem.cssForceHover }, checked: designItem.cssForceHover },\r\n      { title: ':active', action: () => { designItem.cssForceActive = !designItem.cssForceActive }, checked: designItem.cssForceActive },\r\n      { title: ':visited', action: () => { designItem.cssForceVisited = !designItem.cssForceVisited }, checked: designItem.cssForceVisited },\r\n      { title: ':focus', action: () => { designItem.cssForceFocus = !designItem.cssForceFocus }, checked: designItem.cssForceFocus },\r\n      { title: ':focus-within', action: () => { designItem.cssForceFocusWithin = !designItem.cssForceFocusWithin }, checked: designItem.cssForceFocusWithin },\r\n      { title: ':focus-visible', action: () => { designItem.cssForceFocusVisible = !designItem.cssForceFocusVisible }, checked: designItem.cssForceFocusVisible },\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/IContextMenuExtension.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\n\r\nexport type ContextmenuInitiator =  'designer' | 'treeView' | 'other';\r\n\r\nexport interface IContextMenuExtension {\r\n  shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator);\r\n  provideContextMenuItems(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator, provider?: any): IContextMenuItem[];\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ItemsBelowContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { DesignItem } from '../../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class ItemsBelowContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return initiator == 'designer';\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    const lstItems = designerCanvas.elementsFromPoint(event.x, event.y);\r\n    if (lstItems.length > 0) {\r\n      return [{ title: 'items below', children: [...lstItems.map(x => ({ title: 'select: ' + x.localName + (x.id ? ' (#' + x.id + ')' : ''), action: () => this._select(designerCanvas, x) }))] }];\r\n    }\r\n    return [];\r\n  }\r\n  private _select(designerView: IDesignerCanvas, element: Element) {\r\n    const item = DesignItem.GetOrCreateDesignItem(element, element, designerView.serviceContainer, designerView.instanceServiceContainer);\r\n    designerView.instanceServiceContainer.selectionService.setSelectedElements([item]);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/JumpToElementContextMenu.ts",
    "content": "import { assetsPath } from '../../../../../Constants.js';\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\n\nexport class JumpToElementContextMenu implements IContextMenuExtension {\n\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n    return designItem !== null;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\n    return [\n      {\n        title: 'jump to', icon: `<img src=\"${assetsPath + 'icons/jump.svg'}\">`, action: () => {\n          const coord = designerCanvas.getNormalizedElementCoordinates(designItem.element);\n\n          designerCanvas.zoomPoint({ x: coord.x + coord.width / 2, y: coord.y + coord.height / 2 }, designerCanvas.zoomFactor);\n        }\n      },\n    ]\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/MultipleItemsSelectedContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { switchContainer } from '../../../../helper/SwitchContainerHelper.js';\r\nimport { DesignItem } from '../../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class MultipleItemsSelectedContextMenu implements IContextMenuExtension {\r\n\r\n  public orderIndex: number = 60;\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    if (designItem?.instanceServiceContainer.selectionService.selectedElements.length > 1) {\r\n      return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      {\r\n        title: 'wrap in',\r\n        children: [\r\n          {\r\n            title: 'div',\r\n            action: () => {\r\n              const grp = designItem.openGroup(\"wrap in Div\");\r\n              let elements = designItem.instanceServiceContainer.selectionService.selectedElements;\r\n              let newContainer = document.createElement('div');\r\n              const newContainerDesignItem = DesignItem.createDesignItemFromInstance(newContainer, designItem.serviceContainer, designItem.instanceServiceContainer);\r\n\r\n              elements[0].insertAdjacentElement(newContainerDesignItem, 'beforebegin');\r\n              switchContainer(elements, newContainerDesignItem, true, 10);\r\n\r\n              grp.commit();\r\n              designItem.instanceServiceContainer.selectionService.setSelectedElements([newContainerDesignItem]);\r\n            }\r\n          }\r\n        ]\r\n      }\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/PasteFormatContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { NodeType } from '../../../../item/NodeType.js';\nimport { createPasteFormatSnapshotFromEntries, getPasteFormatEntries, PasteFormatKind } from '../../../../services/copyPasteService/PasteFormatSnapshot.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\n\nexport class PasteFormatContextMenu implements IContextMenuExtension {\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n    return !designerCanvas.readOnly\n      && this._getTargetDesignItems(designerCanvas).length > 0;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\n    return [{\n      title: 'paste format',\n      children: [\n        this._createPasteFormatItem(designerCanvas, 'all'),\n        { title: '-' },\n        this._createPasteFormatItem(designerCanvas, 'border'),\n        this._createPasteFormatItem(designerCanvas, 'background'),\n        this._createPasteFormatItem(designerCanvas, 'transform'),\n        this._createPasteFormatItem(designerCanvas, 'text')\n      ]\n    }];\n  }\n\n  private _createPasteFormatItem(designerCanvas: IDesignerCanvas, kind: PasteFormatKind): IContextMenuItem {\n    return {\n      title: kind,\n      action: () => {\n        void this._pasteFormat(designerCanvas, kind);\n      }\n    };\n  }\n\n  private _getTargetDesignItems(designerCanvas: IDesignerCanvas): IDesignItem[] {\n    return designerCanvas.instanceServiceContainer.selectionService.selectedElements?.filter(x => x?.nodeType === NodeType.Element && !x.isRootItem) ?? [];\n  }\n\n  private async _pasteFormat(designerCanvas: IDesignerCanvas, kind: PasteFormatKind): Promise<void> {\n    const [pasteDesignItems] = await designerCanvas.serviceContainer.copyPasteService.getPasteItems(designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\n    const pasteSource = pasteDesignItems?.find(x => x?.nodeType === NodeType.Element && x.hasStyles && !x.isRootItem);\n    const snapshot = pasteSource ? createPasteFormatSnapshotFromEntries(pasteSource.styles()) : null;\n    if (!snapshot) {\n      return;\n    }\n\n    const entries = getPasteFormatEntries(snapshot, kind);\n    const targets = this._getTargetDesignItems(designerCanvas);\n    if (!entries.length || !targets.length) {\n      return;\n    }\n\n    const group = targets[0].openGroup(`paste format: ${kind}`);\n    let changed = false;\n\n    for (const target of targets) {\n      for (const entry of entries) {\n        if (target.getStyle(entry.name) === entry.value) {\n          continue;\n        }\n\n        target.setStyle(entry.name, entry.value);\n        changed = true;\n      }\n    }\n\n    if (changed) {\n      group.commit();\n    } else {\n      group.abort();\n    }\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/PathContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { createPathD } from '../../../../helper/PathDataPolyfill.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class PathContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    if (designItem?.element instanceof SVGPathElement)\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    const pathdata = (<SVGGraphicsElement>designItem.node).getPathData({ normalize: true });\r\n    const items: IContextMenuItem[] = [];\r\n    const lastType = pathdata[pathdata.length - 1].type;\r\n    items.push({ title: '-' });\r\n    if (lastType == 'z' || lastType == 'Z') {\r\n      items.push({\r\n        title: 'open path ', action: () => {\r\n          pathdata.splice(pathdata.length - 1, 1);\r\n          designItem.setAttribute('d', createPathD(pathdata));\r\n        }\r\n      });\r\n    }\r\n    else {\r\n      items.push({\r\n        title: 'close path ', action: () => {\r\n          pathdata.push(<any>{ type: 'Z' });\r\n          designItem.setAttribute('d', createPathD(pathdata));\r\n        }\r\n      });\r\n    }\r\n\r\n    return items;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/RectContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { DesignItem } from '../../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { InsertAction } from '../../../../services/undoService/transactionItems/InsertAction.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class RectContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    if (designItem?.element instanceof SVGRectElement)\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      {\r\n        title: 'convert to path', action: () => {\r\n          let rect = <SVGRectElement>designItem.element;\r\n          let pathD: string = \"\";\r\n          pathD += \"M\" + rect.x.baseVal.value + \" \" + rect.y.baseVal.value +\r\n            \"L\" + (rect.x.baseVal.value + rect.width.baseVal.value) + \" \" + rect.y.baseVal.value +\r\n            \"L\" + (rect.x.baseVal.value + rect.width.baseVal.value) + \" \" + (rect.y.baseVal.value + rect.height.baseVal.value) +\r\n            \"L\" + rect.x.baseVal.value + \" \" + (rect.y.baseVal.value + rect.height.baseVal.value) +\r\n            \"Z\";\r\n          const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n          path.setAttribute(\"d\", pathD);\r\n          path.setAttribute(\"stroke\", rect.getAttribute(\"stroke\"));\r\n          path.setAttribute(\"fill\", rect.getAttribute(\"fill\"));\r\n          path.setAttribute(\"stroke-width\", rect.getAttribute(\"stroke-width\"));\r\n          const di = DesignItem.createDesignItemFromInstance(path, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n          designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designItem.parent, designItem.childCount, di));\r\n          designerCanvas.serviceContainer.deletionService.removeItems([designItem]);\r\n        }\r\n      }\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/RotateLeftAndRightContextMenu.ts",
    "content": "import { CommandType } from '../../../../../commandHandling/CommandType.js';\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { NodeType } from '../../../../item/NodeType.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\nimport { assetsPath } from '../../../../../Constants.js';\n\nexport class RotateLeftAndRight implements IContextMenuExtension {\n\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n     if (designerCanvas.readOnly)\n      return false;\n    return !designItem?.isRootItem && designItem?.nodeType == NodeType.Element;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\n    return [\n      { title: 'rotate right', icon: `<img src=\"${assetsPath + 'icons/rotateRight.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.rotateClockwise }); }, shortCut: 'Ctrl + R' },\n      { title: 'rotate left', icon: `<img src=\"${assetsPath + 'icons/rotateLeft.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.rotateCounterClockwise }); }, shortCut: 'Ctrl + Shift + R' }\n    ]\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/SelectAllChildrenContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { NodeType } from \"../../../../item/NodeType.js\";\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class SelectAllChildrenContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return designItem?.hasChildren;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [{\r\n      title: 'select all Children', action: () => {\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(Array.from(designItem.children()).filter(x => x.nodeType == NodeType.Element));\r\n      }\r\n    }];\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/SeperatorContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nexport class SeperatorContextMenu implements IContextMenuExtension {\r\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return true;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      { title: '-' }\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ToolWindowsContextMenu.ts",
    "content": "import { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\nimport { DraggableToolWindow } from '../../tools/toolBar/popups/DraggableToolWindow.js';\nimport { TransformToolPopup } from '../../tools/toolBar/popups/TransformToolPopup.js';\nimport { BoxShadowEditorWindow } from '../../tools/toolBar/popups/BoxShadowEditorWindow.js';\nimport { TextShadowEditorWindow } from '../../tools/toolBar/popups/TextShadowEditorWindow.js';\nimport { GradientEditorWindow } from '../../tools/toolBar/popups/GradientEditorWindow.js';\nimport { BorderRadiusEditorWindow } from '../../tools/toolBar/popups/BorderRadiusEditorWindow.js';\n\nexport class ToolWindowsContextMenu implements IContextMenuExtension {\n  shouldProvideContextmenu(_event: MouseEvent, _designerCanvas: IDesignerCanvas, _designItem: IDesignItem, _initiator: ContextmenuInitiator): boolean {\n    return true;\n  }\n\n  provideContextMenuItems(_event: MouseEvent, designerCanvas: IDesignerCanvas, _designItem: IDesignItem, _initiator: ContextmenuInitiator): IContextMenuItem[] {\n    return [{\n      title: 'tool windows',\n      children: [\n        {\n          title: 'transform',\n          action: () => {\n            const win = new TransformToolPopup(designerCanvas);\n            DraggableToolWindow.showWindow(win);\n          }\n        },\n        {\n          title: 'box shadow editor',\n          action: () => {\n            const win = new BoxShadowEditorWindow(designerCanvas);\n            DraggableToolWindow.showWindow(win);\n          }\n        },\n        {\n          title: 'text shadow editor',\n          action: () => {\n            const win = new TextShadowEditorWindow(designerCanvas);\n            DraggableToolWindow.showWindow(win);\n          }\n        },\n        {\n          title: 'gradient editor',\n          action: () => {\n            const win = new GradientEditorWindow(designerCanvas);\n            DraggableToolWindow.showWindow(win);\n          }\n        },\n        {\n          title: 'border radius editor',\n          action: () => {\n            const win = new BorderRadiusEditorWindow(designerCanvas);\n            DraggableToolWindow.showWindow(win);\n          }\n        },\n      ]\n    }];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ZMoveContextMenu.ts",
    "content": "import { CommandType } from '../../../../../commandHandling/CommandType.js';\r\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\nimport { assetsPath } from '../../../../../Constants.js';\r\n\r\nexport class ZMoveContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n     if (designerCanvas.readOnly)\r\n      return false;\r\n    return !designItem?.isRootItem && designItem?.nodeType == NodeType.Element;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      { title: 'to front', icon: `<img style=\"rotate: 90deg\" src=\"${assetsPath + 'icons/moveFirst.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.moveToFront }); } },\r\n      { title: 'move forward', icon: `<img style=\"rotate: 90deg\" src=\"${assetsPath + 'icons/moveLeft.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.moveForward }); } },\r\n      { title: 'move backward', icon: `<img style=\"rotate: 270deg\" src=\"${assetsPath + 'icons/moveLeft.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.moveBackward }); } },\r\n      { title: 'to back', icon: `<img style=\"rotate: 270deg\" src=\"${assetsPath + 'icons/moveFirst.svg'}\">`, action: () => { designerCanvas.executeCommand({ type: CommandType.moveToBack }); } },\r\n    ]\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/contextMenu/ZoomToElementContextMenu.ts",
    "content": "import { assetsPath } from \"../../../../../Constants.js\";\r\nimport { IRect } from \"../../../../../interfaces/IRect.js\";\r\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { ContextmenuInitiator, IContextMenuExtension } from './IContextMenuExtension.js';\r\n\r\nconst offset = 10;\r\n\r\nexport class ZoomToElementContextMenu implements IContextMenuExtension {\r\n\r\n  public shouldProvideContextmenu(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\r\n    return designItem !== null;\r\n  }\r\n\r\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\r\n    return [\r\n      {\r\n        title: 'zoom to', icon: `<img src=\"${assetsPath + 'icons/zoomIn.svg'}\">`, action: () => {\r\n          const coord = designerCanvas.getNormalizedElementCoordinates(designItem.element);\r\n          const startPoint = { x: coord.x - offset, y: coord.y - offset };\r\n          const endPoint = { x: coord.x + coord.width + offset, y: coord.y + coord.height + offset };\r\n\r\n          let rect: IRect = {\r\n            x: startPoint.x < endPoint.x ? startPoint.x : endPoint.x,\r\n            y: startPoint.y < endPoint.y ? startPoint.y : endPoint.y,\r\n            width: Math.abs(startPoint.x - endPoint.x),\r\n            height: Math.abs(startPoint.y - endPoint.y),\r\n          }\r\n\r\n          let zFactorWidth = designerCanvas.outerRect.width / rect.width;\r\n          let zFactorHeight = designerCanvas.outerRect.height / rect.height;\r\n\r\n          let zoomFactor = zFactorWidth >= zFactorHeight ? zFactorHeight : zFactorWidth;\r\n\r\n          designerCanvas.zoomPoint({ x: coord.x + coord.width / 2, y: coord.y + coord.height / 2 }, zoomFactor);\r\n        }\r\n      },\r\n    ]\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/flex/FlexToolbarExtension.ts",
    "content": "import { html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { assetsPath } from \"../../../../../Constants.js\";\r\nimport { BasicStackedToolbarExtension } from \"../BasicStackedToolbarExtension.js\";\r\nimport { ImageButtonListSelector } from \"../../../../controls/ImageButtonListSelector.js\";\r\n\r\nexport class FlexToolbarExtension extends BasicStackedToolbarExtension {\r\n\r\n  protected static template = html`\r\n    <div style=\"height: 100%; width: 100%;\">\r\n      ${BasicStackedToolbarExtension.basicTemplate}\r\n      <node-projects-image-button-list-selector property=\"direction\" no-value-in-header id=\"flex-direction\">\r\n        <img title=\"row\" data-value=\"row\" src=\"${assetsPath}images/chromeDevtools/flex-direction-row-icon.svg\">\r\n        <img title=\"column\" data-value=\"column\" src=\"${assetsPath}images/chromeDevtools/flex-direction-column-icon.svg\">\r\n        <img title=\"row-reverse\" data-value=\"row-reverse\" style=\"transform: scaleX(-1);\" src=\"${assetsPath}images/chromeDevtools/flex-direction-row-icon.svg\">\r\n        <img title=\"column-reverse\" data-value=\"column-reverse\" style=\"transform: scaleY(-1);\" src=\"${assetsPath}images/chromeDevtools/flex-direction-column-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"wrap\" no-value-in-header id=\"flex-wrap\">\r\n        <img title=\"nowrap\" data-value=\"nowrap\" src=\"${assetsPath}images/chromeDevtools/flex-wrap-nowrap-icon.svg\">\r\n        <img title=\"wrap\" data-value=\"wrap\" src=\"${assetsPath}images/chromeDevtools/flex-wrap-wrap-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"align-content\" no-value-in-header id=\"align-content\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/align-content-flex-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/align-content-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/align-content-flex-end-icon.svg\">\r\n        <img title=\"space-around\" data-value=\"space-around\" src=\"${assetsPath}images/chromeDevtools/align-content-space-around-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/align-content-space-evenly-icon.svg\">\r\n        <img title=\"space-between\" data-value=\"space-between\" src=\"${assetsPath}images/chromeDevtools/align-content-space-between-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/align-content-stretch-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"justify-content\" no-value-in-header id=\"justify-content\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/justify-content-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/justify-content-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/justify-content-end-icon.svg\">\r\n        <img title=\"space-around\" data-value=\"space-around\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-around-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-evenly-icon.svg\">\r\n        <img title=\"space-between\" data-value=\"space-between\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-between-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"align-items\" no-value-in-header id=\"align-items\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/align-items-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/align-items-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/align-items-end-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/align-items-stretch-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/align-items-baseline-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n    </div>\r\n  `;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._size.width = 625;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event: MouseEvent) {\r\n    super.extend(cache, event);\r\n    this._addFlexDirectionButton();\r\n    this._addStyleButton('flex-wrap');\r\n    this._addStyleButton('align-content');\r\n    this._addStyleButton('justify-content');\r\n    this._addStyleButton('align-items');\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  protected _addFlexDirectionButton() {\r\n    const cs = getComputedStyle(this.extendedItem.element);\r\n    const ctl = this._toolbar.getById<ImageButtonListSelector>('flex-direction')\r\n    ctl.addEventListener('value-changed', async () => {\r\n      await this.extendedItem.updateStyleInSheetOrLocalAsync('flex-direction', ctl.value);\r\n      this.rotateImagesAcordingFlexDirection(ctl.value);\r\n    });\r\n    ctl.value = cs['flex-direction'];\r\n    this.rotateImagesAcordingFlexDirection(ctl.value);\r\n  }\r\n\r\n  rotateImagesAcordingFlexDirection(direction: string) {\r\n    let angle = 0\r\n    if (direction == 'column' || direction == 'column-reverse')\r\n      angle = -90;\r\n    this._toolbar.getById('flex-wrap').querySelectorAll('img').forEach(x => x.style.rotate = angle + 'deg');\r\n    this._toolbar.getById('align-content').querySelectorAll('img').forEach(x => x.style.rotate = angle + 'deg');\r\n    this._toolbar.getById('justify-content').querySelectorAll('img').forEach(x => x.style.rotate = angle + 'deg');\r\n    //@ts-ignore\r\n    this._toolbar.getById('align-items').querySelectorAll('img:nth-child(-n+4)').forEach(x => x.style.rotate = angle + 'deg');\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/flex/FlexToolbarExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { FlexToolbarExtension } from './FlexToolbarExtension.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { basicStackedToolbarExtensionOverlayOptionName } from '../BasicStackedToolbarExtension.js';\r\n\r\nexport class FlexToolbarExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType === NodeType.Element) {\r\n      const d = getComputedStyle(designItem.element).display;\r\n      if (d === 'flex' || d === 'inline-flex')\r\n        return designerCanvas.instanceServiceContainer.designContext.extensionOptions[basicStackedToolbarExtensionOverlayOptionName] !== false;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new FlexToolbarExtension(extensionManager, designerView, designItem);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/flex/FlexboxExtension.ts",
    "content": "import { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension } from '../AbstractExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { OverlayLayer } from '../OverlayLayer.js';\r\n\r\nexport class FlexboxExtension extends AbstractExtension {\r\n  private _path: SVGPathElement;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend() {\r\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\r\n    this._path = this._drawTransformedRect(transformedCornerPoints, 'svg-flexbox', this._path, OverlayLayer.Background);\r\n  }\r\n\r\n  override refresh() {\r\n    this._removeAllOverlays();\r\n    this.extend();\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/flex/FlexboxExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { FlexboxExtension } from './FlexboxExtension.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\n\r\nexport const flexboxExtensionShowOverlayOptionName = 'flexboxExtensionShowOverlay';\r\n\r\nexport class FlexboxExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType != NodeType.Element)\r\n          return false;\r\n    const display = getComputedStyle((<HTMLElement>designItem.element)).display;\r\n    if (display == 'flex' || display == 'inline-flex')\r\n      return designerCanvas.instanceServiceContainer.designContext.extensionOptions[flexboxExtensionShowOverlayOptionName] !== false;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new FlexboxExtension(extensionManager, designerCanvas, designItem);\r\n  }\r\n\r\n  readonly style = css`\r\n    .svg-flexbox { stroke: orange; fill: #9a47ff22; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/DisplayGridExtension.ts",
    "content": "import { calculateGridInformation, getElementLocalToCanvasMatrix, getGridCellFromPoint } from \"../../../../helper/GridHelper.js\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension } from '../AbstractExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { OverlayLayer } from \"../OverlayLayer.js\";\r\n\r\nexport class DisplayGridExtension extends AbstractExtension {\r\n\r\n  private _cells: SVGRectElement[][];\r\n  private _texts: SVGTextElement[][];\r\n  private _gaps: SVGRectElement[];\r\n  private _group: SVGGElement;\r\n\r\n  private gridInformation: ReturnType<typeof calculateGridInformation>\r\n  private gridInformationString: string;\r\n  private _lastEvent: Event;\r\n  private gridColor: string;\r\n  private gridFillColor: string;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem, gridColor: string, gridFillColor: string) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this.gridColor = gridColor;\r\n    this.gridFillColor = gridFillColor;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this._initSVGArrays();\r\n    this.refresh(event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.gridInformation = calculateGridInformation(this.extendedItem);\r\n    const gridInformationString = JSON.stringify(this.gridInformation);\r\n\r\n    if (gridInformationString !== this.gridInformationString || (event != null && this._lastEvent !== event)) {\r\n      if (event)\r\n        this._lastEvent = event;\r\n\r\n      this.gridInformationString = gridInformationString;\r\n      let cells = this.gridInformation.cells;\r\n      const hoveredCell = this._lastEvent && this._lastEvent instanceof MouseEvent\r\n        ? getGridCellFromPoint(this.extendedItem, this.designerCanvas.getNormalizedEventCoordinates(this._lastEvent), this.gridInformation)\r\n        : null;\r\n\r\n      if (cells[0][0] && !isNaN(cells[0][0].height) && !isNaN(cells[0][0].width)) {\r\n        if (this.gridInformation.cells.length != this._cells.length || this.gridInformation.cells[0].length != this._cells[0].length)\r\n          this._initSVGArrays();\r\n\r\n        if (!this._group) {\r\n          this._group = this._drawGroup(null, this._group, OverlayLayer.Background);\r\n          this._group.style.transformOrigin = '0 0';\r\n          this._group.style.setProperty(\"--svg-grid-stroke-color\", this.gridColor)\r\n          this._group.style.setProperty(\"--svg-grid-fill-color\", this.gridFillColor)\r\n        }\r\n        this._group.style.transform = getElementLocalToCanvasMatrix(this.extendedItem).toString();\r\n\r\n        //draw gaps\r\n        this.gridInformation.gaps.forEach((gap, i) => {\r\n          this._gaps[i] = this._drawRect(gap.localX, gap.localY, gap.width, gap.height, 'svg-grid-gap', this._gaps[i], OverlayLayer.Background);\r\n          this._group.appendChild(this._gaps[i]);\r\n        });\r\n\r\n        //draw cells\r\n        cells.forEach((row, i) => {\r\n          row.forEach((cell, j) => {\r\n            this._cells[i][j] = this._drawRect(cell.localX, cell.localY, cell.width, cell.height, 'svg-grid', this._cells[i][j], OverlayLayer.Background);\r\n            this._group.appendChild(this._cells[i][j]);\r\n            if (cell.name) {\r\n              this._texts[i][j] = this._drawText(cell.name, cell.localX, cell.localY, 'svg-grid-area', this._texts[i][j], OverlayLayer.Background);\r\n              this._texts[i][j].setAttribute(\"dominant-baseline\", \"hanging\");\r\n            }\r\n            if (hoveredCell?.row === i && hoveredCell?.column === j) {\r\n              this._cells[i][j].setAttribute(\"class\", \"svg-grid-current-cell\");\r\n            }\r\n          })\r\n        });\r\n      }\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n\r\n  _initSVGArrays() {\r\n    this._removeAllOverlays();\r\n    this._group = null;\r\n    this.gridInformation = calculateGridInformation(this.extendedItem);\r\n    this._cells = new Array(this.gridInformation.cells.length);\r\n    this.gridInformation.cells.forEach((row, i) => this._cells[i] = new Array(row.length));\r\n    this._texts = new Array(this.gridInformation.cells.length);\r\n    this.gridInformation.cells.forEach((row, i) => this._texts[i] = new Array(row.length));\r\n    this._gaps = new Array(this.gridInformation.gaps.length);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/DisplayGridExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { DisplayGridExtension } from './DisplayGridExtension.js';\r\nimport { svgAsString } from '../../../../helper/SvgHelper.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\n\r\nexport const gridExtensionShowOverlayOptionName = 'gridExtensionShowOverlay';\r\n\r\nexport class DisplayGridExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  gridColor: string;\r\n  gridFillColor: string;\r\n\r\n  constructor(gridColor: string = 'orange', gridFillColor = '#ff944722') {\r\n    this.gridColor = gridColor;\r\n    this.gridFillColor = gridFillColor;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType != NodeType.Element)\r\n      return false;\r\n    const display = getComputedStyle((<HTMLElement>designItem.element)).display;\r\n    if (display == 'grid' || display == 'inline-grid')\r\n      return designerCanvas.instanceServiceContainer.designContext.extensionOptions[gridExtensionShowOverlayOptionName] !== false;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new DisplayGridExtension(extensionManager, designerCanvas, designItem, this.gridColor, this.gridFillColor);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-grid { stroke: var(--svg-grid-stroke-color); stroke-dasharray: 5; fill: var(--svg-grid-fill-color); }\r\n    .svg-grid-current-cell { stroke: var(--svg-grid-stroke-color); stroke-dasharray: 5; fill: #e3ff4722; }\r\n    .svg-grid-area { font-size: 8px; }\r\n    .svg-grid-gap { stroke: transparent; fill: var(--svg-grid-stroke-color); opacity: 0.3; mask: url(#mask-stripe-grid) }\r\n    .svg-grid-header { fill: var(--svg-grid-fill-color); stroke: var(--svg-grid-stroke-color); }\r\n    .svg-grid-plus-sign { stroke: black; }\r\n  `;\r\n\r\n  static readonly svgDefs = svgAsString`\r\n    <pattern id=\"pattern-stripe-grid\" \r\n      width=\"4\" height=\"4\" \r\n      patternUnits=\"userSpaceOnUse\"\r\n      patternTransform=\"rotate(45)\">\r\n      <rect width=\"1\" height=\"4\" transform=\"translate(0,0)\" fill=\"white\"></rect>\r\n    </pattern>\r\n    <mask id=\"mask-stripe-grid\">\r\n      <rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" fill=\"url(#pattern-stripe-grid)\" />\r\n    </mask>`;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/EditGridColumnRowSizesExtension.ts",
    "content": "import { EventNames } from \"../../../../../enums/EventNames.js\";\r\nimport { convertCssUnit, convertCssUnitToPixel, getCssUnit, getExpandedCssGridColumnSizes } from \"../../../../helper/CssUnitConverter.js\";\r\nimport { getBoundingClientRectAlsoForDisplayContents } from \"../../../../helper/ElementHelper.js\";\r\nimport { calculateGridInformation, getElementLocalToCanvasMatrix, getGridLocalPoint } from \"../../../../helper/GridHelper.js\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension } from '../AbstractExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { OverlayLayer } from \"../OverlayLayer.js\";\r\n\r\nexport class EditGridColumnRowSizesExtension extends AbstractExtension {\r\n\r\n  gridInformation: ReturnType<typeof calculateGridInformation>;\r\n\r\n  private _resizers: SVGRectElement[] = [];\r\n  private _initalPos: number;\r\n  private _initialSizes: string;\r\n  private _group: SVGGElement;\r\n  private _hasChanged: boolean;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this._group = this._drawGroup(null, this._group, OverlayLayer.Background);\r\n    this._group.style.transformOrigin = '0 0';\r\n\r\n    this.refresh(event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.gridInformation = calculateGridInformation(this.extendedItem);\r\n    this._group.style.transform = getElementLocalToCanvasMatrix(this.extendedItem).toString();\r\n    this.gridInformation.gaps.forEach((gap, i) => {\r\n      if (gap.width < 3) { gap.width = 3; gap.x--; gap.localX--; }\r\n      if (gap.height < 3) { gap.height = 3; gap.y--; gap.localY--; }\r\n      let rect = this._drawRect(gap.localX, gap.localY, gap.width, gap.height, 'svg-grid-resizer-' + gap.type, this._resizers[i], OverlayLayer.Normal);\r\n      if (!this._resizers[i]) {\r\n        this._resizers[i] = rect;\r\n        rect.addEventListener(EventNames.PointerDown, event => this._pointerActionTypeResize(event, rect, gap));\r\n        rect.addEventListener(EventNames.PointerMove, event => this._pointerActionTypeResize(event, rect, gap));\r\n        rect.addEventListener(EventNames.PointerUp, event => this._pointerActionTypeResize(event, rect, gap));\r\n        this._group.appendChild(rect);\r\n      }\r\n    });\r\n  }\r\n\r\n  private _pointerActionTypeResize(event: PointerEvent, rect: SVGRectElement, gap: ReturnType<typeof calculateGridInformation>['gaps'][0]) {\r\n    event.stopPropagation();\r\n\r\n    const templatePropertyName = gap.type == 'h' ? 'gridTemplateRows' : 'gridTemplateColumns';\r\n    const index = (gap.type == 'h' ? gap.row : gap.column) - 1;\r\n    const sizeType = gap.type == 'h' ? 'height' : 'width';\r\n    const pos = this._getAxisLocalPosition(event, gap.type);\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        rect.setPointerCapture(event.pointerId);\r\n        this._initalPos = pos;\r\n        this._initialSizes = getComputedStyle(this.extendedItem.element)[templatePropertyName];\r\n        break;\r\n      case EventNames.PointerMove:\r\n        if (this._initialSizes) {\r\n          const diff = pos - this._initalPos;\r\n          if (Math.abs(diff) > 5 || this._hasChanged) {\r\n            this._hasChanged = true;\r\n            let parts = this._initialSizes.split(' ');\r\n            parts[index] = (parseFloat(parts[index]) + diff) + 'px';\r\n            parts[index + 1] = (parseFloat(parts[index + 1]) - diff) + 'px';\r\n            (<HTMLElement>this.extendedItem.element).style[templatePropertyName] = parts.join(' ');\r\n            this.extensionManager.refreshExtensions([this.extendedItem], null, event);\r\n          }\r\n        }\r\n        break;\r\n      case EventNames.PointerUp:\r\n        rect.releasePointerCapture(event.pointerId);\r\n        const diff = pos - this._initalPos;\r\n        if (this._hasChanged) {\r\n          this._hasChanged = false;\r\n          const realStyle = this.extendedItem.getStyleFromSheetOrLocalOrComputed(templatePropertyName);\r\n          const initialParts = this._initialSizes.split(' ');\r\n          let units = getExpandedCssGridColumnSizes(realStyle);\r\n          if (units.length != initialParts.length) {\r\n            units = initialParts.map(x => getCssUnit(x));\r\n          }\r\n          (<HTMLElement>this.extendedItem.element).style[templatePropertyName] = '';\r\n\r\n          const targetPixelSizes = initialParts.map(x => parseFloat(x));\r\n          targetPixelSizes[index] += diff;\r\n          targetPixelSizes[index + 1] -= diff;\r\n          const newSizes = this._convertCssUnits(targetPixelSizes, units, <HTMLElement>this.extendedItem.element, sizeType);\r\n\r\n          this.extendedItem.updateStyleInSheetOrLocal(templatePropertyName, newSizes.join(' '), null, true);\r\n        }\r\n        this._initalPos = null;\r\n        this._initialSizes = null;\r\n        this.extensionManager.refreshExtensions([this.extendedItem]);\r\n        break;\r\n    }\r\n  }\r\n\r\n  private _getAxisLocalPosition(event: PointerEvent, gapType: 'h' | 'v'): number {\r\n    const localPoint = getGridLocalPoint(this.extendedItem, this.designerCanvas.getNormalizedEventCoordinates(event));\r\n    return gapType == 'h' ? localPoint.y : localPoint.x;\r\n  }\r\n\r\n  private _convertCssUnits(pixelSizes: number[], targetUnits: string[], target: HTMLElement, percentTarget: 'width' | 'height'): string[] {\r\n    let cp = getComputedStyle(target);\r\n    let bounding = getBoundingClientRectAlsoForDisplayContents(target);\r\n    let containerSize = bounding[percentTarget];\r\n    let amountGaps = percentTarget == \"height\" ? this.gridInformation.cells.length - 1 : this.gridInformation.cells[0].length - 1;\r\n    let gapValue = percentTarget == \"width\" ? cp.columnGap : cp.rowGap;\r\n    if (gapValue == \"normal\")\r\n      gapValue = '0px';\r\n    let gapSize = convertCssUnitToPixel(gapValue, target, percentTarget) ?? 0;\r\n    let containerSizeWithoutGaps = containerSize - gapSize * amountGaps;\r\n    let sizeForFrs = containerSizeWithoutGaps;\r\n\r\n    for (let i = 0; i < pixelSizes.length; i++) {\r\n      if (targetUnits[i] != 'fr')\r\n        sizeForFrs -= pixelSizes[i];\r\n    }\r\n\r\n    let result: string[] = [];\r\n    for (let i = 0; i < pixelSizes.length; i++) {\r\n      if (targetUnits[i] != 'fr') {\r\n        result.push(convertCssUnit(pixelSizes[i], target, percentTarget, targetUnits[i]));\r\n      } else {\r\n        result.push(((pixelSizes[i] / sizeForFrs) * 10).toFixed(2) + 'fr');\r\n      }\r\n    }\r\n    return result;\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/EditGridColumnRowSizesExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { EditGridColumnRowSizesExtension } from './EditGridColumnRowSizesExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { gridExtensionShowOverlayOptionName } from './DisplayGridExtensionProvider.js';\r\n\r\nexport class EditGridColumnRowSizesExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    const display = getComputedStyle((<HTMLElement>designItem.element)).display;\r\n    if (display == 'grid' || display == 'inline-grid')\r\n      return designerCanvas.instanceServiceContainer.designContext.extensionOptions[gridExtensionShowOverlayOptionName] !== false;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new EditGridColumnRowSizesExtension(extensionManager, designerCanvas, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-grid-resizer-v { fill: transparent; cursor: ew-resize; pointer-events: auto; }\r\n    .svg-grid-resizer-v:hover { fill: #ff7f5052; }\r\n    .svg-grid-resizer-h { fill: transparent; cursor: ns-resize; pointer-events: auto; }\r\n    .svg-grid-resizer-h:hover { fill: #ff7f5052; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridChildResizeExtension.ts",
    "content": "import { EventNames } from \"../../../../../enums/EventNames.js\";\r\nimport { IPoint } from \"../../../../../interfaces/IPoint.js\";\r\nimport { calculateGridInformation, getGridColumnIndexFromLocalX, getGridColumnStartLineFromLocalX, getGridLocalPoint, getGridRowIndexFromLocalY, getGridRowStartLineFromLocalY } from \"../../../../helper/GridHelper.js\";\r\nimport { IDesignItem } from \"../../../../item/IDesignItem.js\";\r\nimport { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\nimport { AbstractExtension } from \"../AbstractExtension.js\";\r\nimport { IExtensionManager } from \"../IExtensionManger.js\";\r\n\r\nexport class GridChildResizeExtension extends AbstractExtension {\r\n  private _actionModeStarted: string;\r\n  private _initialPoint: IPoint;\r\n  private _circle1: SVGCircleElement;\r\n  private _circle2: SVGCircleElement;\r\n  private _circle3: SVGCircleElement;\r\n  private _circle4: SVGCircleElement;\r\n  private _circle5: SVGCircleElement;\r\n  private _circle6: SVGCircleElement;\r\n  private _circle7: SVGCircleElement;\r\n  private _circle8: SVGCircleElement;\r\n  private _initialComputedTransformOrigins: DOMPoint[];\r\n  private _initialTransformOrigins: string[];\r\n  private _styleBackup: string;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerCanvas, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    //#region Resizer circles\r\n    let transformedCornerPoints = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n\r\n    if (isNaN(transformedCornerPoints.p1.x) || isNaN(transformedCornerPoints.p2.x)) {\r\n      this.remove();\r\n      return;\r\n    }\r\n    if (this._valuesHaveChanges(this.designerCanvas.zoomFactor, transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, transformedCornerPoints.p4.x, transformedCornerPoints.p4.y, transformedCornerPoints.p3.x, transformedCornerPoints.p3.y)) {\r\n      this._circle1 = this._drawResizerOverlay(transformedCornerPoints.p1.x, transformedCornerPoints.p1.y, 'nw-resize', this._circle1);\r\n      this._circle2 = this._drawResizerOverlay((transformedCornerPoints.p1.x + (transformedCornerPoints.p2.x - transformedCornerPoints.p1.x) / 2), (transformedCornerPoints.p1.y + (transformedCornerPoints.p2.y - transformedCornerPoints.p1.y) / 2), 'n-resize', this._circle2);\r\n      this._circle3 = this._drawResizerOverlay(transformedCornerPoints.p2.x, transformedCornerPoints.p2.y, 'ne-resize', this._circle3);\r\n\r\n      this._circle4 = this._drawResizerOverlay((transformedCornerPoints.p1.x + (transformedCornerPoints.p4.x - transformedCornerPoints.p1.x) / 2), (transformedCornerPoints.p1.y + (transformedCornerPoints.p4.y - transformedCornerPoints.p1.y) / 2), 'w-resize', this._circle4);\r\n      this._circle5 = this._drawResizerOverlay(transformedCornerPoints.p4.x, transformedCornerPoints.p4.y, 'sw-resize', this._circle5);\r\n\r\n      this._circle6 = this._drawResizerOverlay((transformedCornerPoints.p4.x + (transformedCornerPoints.p3.x - transformedCornerPoints.p4.x) / 2), (transformedCornerPoints.p4.y + (transformedCornerPoints.p3.y - transformedCornerPoints.p4.y) / 2), 's-resize', this._circle6);\r\n      this._circle8 = this._drawResizerOverlay((transformedCornerPoints.p2.x + (transformedCornerPoints.p3.x - transformedCornerPoints.p2.x) / 2), (transformedCornerPoints.p2.y + (transformedCornerPoints.p3.y - transformedCornerPoints.p2.y) / 2), 'e-resize', this._circle8);\r\n\r\n      this._circle7 = this._drawResizerOverlay(transformedCornerPoints.p3.x, transformedCornerPoints.p3.y, 'se-resize', this._circle7);\r\n    }\r\n    //#endregion Circles\r\n  }\r\n\r\n  _drawResizerOverlay(x: number, y: number, cursor: string, oldCircle?: SVGCircleElement): SVGCircleElement {\r\n    let circle = this._drawCircle(x, y, this.designerCanvas.serviceContainer.options.resizerPixelSize / this.designerCanvas.zoomFactor, 'svg-grid-resizer', oldCircle);\r\n    circle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n    if (!oldCircle) {\r\n      circle.addEventListener(EventNames.PointerDown, event => this._pointerActionTypeResize(circle, event, cursor));\r\n      circle.addEventListener(EventNames.PointerMove, event => this._pointerActionTypeResize(circle, event, cursor));\r\n      circle.addEventListener(EventNames.PointerUp, event => this._pointerActionTypeResize(circle, event, cursor));\r\n    }\r\n    circle.style.cursor = cursor;\r\n    return circle;\r\n  }\r\n\r\n  _pointerActionTypeResize(circle: SVGCircleElement, event: PointerEvent, actionMode: string) {\r\n    event.stopPropagation();\r\n    const currentPoint = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        this._styleBackup = this.extendedItem.element.getAttribute('style');\r\n\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        this._initialPoint = currentPoint;\r\n        this._actionModeStarted = actionMode;\r\n        this._initialComputedTransformOrigins = [];\r\n        this._initialTransformOrigins = [];\r\n\r\n        const toArr = getComputedStyle(this.extendedItem.element).transformOrigin.split(' ').map(x => parseFloat(x.replace('px', '')));\r\n        const transformOrigin: DOMPoint = new DOMPoint(toArr[0], toArr[1]);\r\n        this._initialComputedTransformOrigins.push(transformOrigin);\r\n        this._initialTransformOrigins.push((<HTMLElement>this.extendedItem.element).style.transformOrigin);\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._initialPoint) {\r\n          let cellX = 0;\r\n          let cellY = 0;\r\n\r\n          const gridInformation = calculateGridInformation(this.extendedItem.parent);\r\n          const localPoint = getGridLocalPoint(this.extendedItem.parent, this.designerCanvas.getNormalizedEventCoordinates(event));\r\n          const cs = getComputedStyle(this.extendedItem.element);\r\n          (<HTMLElement>this.extendedItem.element).style.gridColumnStart = cs.gridColumnStart;\r\n          (<HTMLElement>this.extendedItem.element).style.gridColumnEnd = cs.gridColumnEnd === 'auto' ? '' + (parseInt(cs.gridColumnStart) + 1) : cs.gridColumnEnd;\r\n          (<HTMLElement>this.extendedItem.element).style.gridRowStart = cs.gridRowStart;\r\n          (<HTMLElement>this.extendedItem.element).style.gridRowEnd = cs.gridRowEnd === 'auto' ? '' + (parseInt(cs.gridRowStart) + 1) : cs.gridRowEnd;\r\n\r\n          if (this._actionModeStarted == 'nw-resize' || this._actionModeStarted == 'w-resize' || this._actionModeStarted == 'sw-resize') {\r\n            (<HTMLElement>this.extendedItem.element).style.gridColumnStart = '' + getGridColumnStartLineFromLocalX(gridInformation, localPoint.x);\r\n          }\r\n          if (this._actionModeStarted == 'nw-resize' || this._actionModeStarted == 'n-resize' || this._actionModeStarted == 'ne-resize') {\r\n            (<HTMLElement>this.extendedItem.element).style.gridRowStart = '' + getGridRowStartLineFromLocalY(gridInformation, localPoint.y);\r\n          }\r\n          if (this._actionModeStarted == 'se-resize' || this._actionModeStarted == 'e-resize' || this._actionModeStarted == 'ne-resize') {\r\n            cellX = getGridColumnIndexFromLocalX(gridInformation, localPoint.x);\r\n            (<HTMLElement>this.extendedItem.element).style.gridColumnEnd = '' + (cellX + 2);\r\n          }\r\n          if (this._actionModeStarted == 'sw-resize' || this._actionModeStarted == 's-resize' || this._actionModeStarted == 'se-resize') {\r\n            cellY = getGridRowIndexFromLocalY(gridInformation, localPoint.y);\r\n            (<HTMLElement>this.extendedItem.element).style.gridRowEnd = '' + (cellY + 2);\r\n          }\r\n\r\n          const resizedElements = [this.extendedItem, this.extendedItem.parent];\r\n          this.extensionManager.refreshExtensions(resizedElements);\r\n        }\r\n        break;\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n\r\n        const cs = getComputedStyle(this.extendedItem.element);\r\n        const gridColumnStart = cs.gridColumnStart;\r\n        const gridColumnEnd = cs.gridColumnEnd;\r\n        const gridRowStart = cs.gridRowStart;\r\n        const gridRowEnd = cs.gridRowEnd;\r\n        if (this._styleBackup)\r\n          this.extendedItem.element.setAttribute('style', this._styleBackup);\r\n        else\r\n          this.extendedItem.element.removeAttribute('style');\r\n\r\n        let cg = this.extendedItem.openGroup(\"Resize &lt;\" + this.extendedItem.name + \"&gt;\");\r\n        this.extendedItem.setStyle(\"gridColumnStart\", gridColumnStart);\r\n        this.extendedItem.setStyle(\"gridColumnEnd\", gridColumnEnd);\r\n        this.extendedItem.setStyle(\"gridRowStart\", gridRowStart);\r\n        this.extendedItem.setStyle(\"gridRowEnd\", gridRowEnd);\r\n        cg.commit();\r\n        this._initialPoint = null;\r\n        break;\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridChildResizeExtensionProvider.ts",
    "content": "import { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from \"../../../../item/IDesignItem.js\";\r\nimport { NodeType } from \"../../../../item/NodeType.js\";\r\nimport { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\nimport { IDesignerExtension } from \"../IDesignerExtension.js\";\r\nimport { IDesignerExtensionProvider } from \"../IDesignerExtensionProvider.js\";\r\nimport { IExtensionManager } from \"../IExtensionManger.js\";\r\nimport { GridChildResizeExtension } from \"./GridChildResizeExtension.js\";\r\n\r\nexport class GridChildResizeExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType === NodeType.Element && designItem.parent?.nodeType === NodeType.Element && getComputedStyle(designItem.parent.element).display === 'grid')\r\n      return true;\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new GridChildResizeExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-grid-resizer { stroke: #3899ec; fill: white; pointer-events: auto; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridChildToolbarExtension.ts",
    "content": "import { html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { assetsPath } from \"../../../../../Constants.js\";\r\nimport { BasicStackedToolbarExtension } from \"../BasicStackedToolbarExtension.js\";\r\n\r\nexport class GridChildToolbarExtension extends BasicStackedToolbarExtension {\r\n\r\n  protected static template = html`\r\n    <div style=\"height: 100%; width: 100%;\">\r\n      <div style=\"display: flex; flex-direction: column;\">\r\n        <span style=\"font-size: 10px; color: #00aff0;\">column:</span>\r\n        <input type=\"text\" title=\"column\" id=\"gridColumn\" style=\"pointer-events: auto; height: 12px; width: 45px; padding: 0; margin-right: 5px\">\r\n      </div>\r\n       <div style=\"display: flex; flex-direction: column;\">\r\n        <span style=\"font-size: 10px; color: #00aff0;\">row:</span>\r\n        <input type=\"text\" title=\"column\" id=\"gridRow\" style=\"pointer-events: auto; height: 12px; width: 45px; padding: 0; margin-right: 10px\">\r\n      </div>\r\n      <node-projects-image-button-list-selector property=\"align-self\" no-value-in-header id=\"align-self\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/align-items-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/align-items-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/align-items-end-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/align-items-stretch-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"justify-self\" no-value-in-header id=\"justify-self\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/justify-items-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/justify-items-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/justify-items-end-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/justify-items-stretch-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n    </div>`;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._size.width = 250;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event: MouseEvent) {\r\n    super.extend(cache, event);\r\n\r\n    const cs = getComputedStyle(this.extendedItem.element);\r\n    const gridColumnEl = this._toolbar.getById<HTMLSelectElement>('gridColumn');\r\n    if (gridColumnEl) {\r\n      gridColumnEl.value = cs.gridColumn;\r\n      gridColumnEl.onkeyup = async (e) => {\r\n        if (e.key === 'Enter')\r\n          await this.extendedItem.updateStyleInSheetOrLocalAsync('gridColumn', gridColumnEl.value);\r\n      }\r\n    }\r\n    const gridRowEl = this._toolbar.getById<HTMLSelectElement>('gridRow');\r\n    if (gridRowEl) {\r\n      gridRowEl.value = cs.gridColumn;\r\n      gridRowEl.onkeyup = async (e) => {\r\n        if (e.key === 'Enter')\r\n          await this.extendedItem.updateStyleInSheetOrLocalAsync('gridRow', gridRowEl.value);\r\n      }\r\n    }\r\n\r\n    this._addStyleButton('align-self');\r\n    this._addStyleButton('justify-self');\r\n\r\n    this.refresh(cache, event);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridChildToolbarExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { GridChildToolbarExtension } from './GridChildToolbarExtension.js';\r\nimport { basicStackedToolbarExtensionOverlayOptionName } from '../BasicStackedToolbarExtension.js';\r\n\r\nexport class GridChildToolbarExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType === NodeType.Element && designItem.parent) {\r\n      const cs = designItem.parent?.getComputedStyle();\r\n      if (cs != null && (cs.display === 'grid' || cs.display === 'inline-grid'))\r\n        return designerCanvas.instanceServiceContainer.designContext.extensionOptions[basicStackedToolbarExtensionOverlayOptionName] !== false;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new GridChildToolbarExtension(extensionManager, designerView, designItem);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridToolbarExtension.ts",
    "content": "import { html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { BasicStackedToolbarExtension } from \"../BasicStackedToolbarExtension.js\";\r\nimport { assetsPath } from \"../../../../../Constants.js\";\r\n\r\nexport class GridToolbarExtension extends BasicStackedToolbarExtension {\r\n\r\n  protected static template = html`\r\n    <div style=\"height: 100%; width: 100%;\">\r\n      ${BasicStackedToolbarExtension.basicTemplate}\r\n      <select title=\"display\" id=\"gridType\" style=\"pointer-events: auto; height: 24px; width: 60px; padding: 0; margin-right: 10px\">\r\n        <option>1x1</option>\r\n        <option>1x16</option>\r\n        <option>2x8</option>\r\n        <option>4x4</option>\r\n        <option>8x2</option>\r\n        <option>16x1</option>\r\n        <option>custom</option>\r\n      </select>\r\n      <node-projects-image-button-list-selector property=\"align-content\" no-value-in-header id=\"align-content\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/align-content-center-icon.svg\">\r\n        <img title=\"space-around\" data-value=\"space-around\" src=\"${assetsPath}images/chromeDevtools/align-content-space-around-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/align-content-space-evenly-icon.svg\">\r\n        <img title=\"space-between\" data-value=\"space-between\" src=\"${assetsPath}images/chromeDevtools/align-content-space-between-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/align-content-stretch-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"justify-content\" no-value-in-header id=\"justify-content\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/justify-content-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/justify-content-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/justify-content-end-icon.svg\">\r\n        <img title=\"space-around\" data-value=\"space-around\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-around-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-evenly-icon.svg\">\r\n        <img title=\"space-between\" data-value=\"space-between\" src=\"${assetsPath}images/chromeDevtools/justify-content-space-between-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"align-items\" no-value-in-header id=\"align-items\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/align-items-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/align-items-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/align-items-end-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/align-items-stretch-icon.svg\">\r\n        <img title=\"space-evenly\" data-value=\"space-evenly\" src=\"${assetsPath}images/chromeDevtools/align-items-baseline-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n      <node-projects-image-button-list-selector property=\"justify-items\" no-value-in-header id=\"justify-items\">\r\n        <img title=\"start\" data-value=\"start\" src=\"${assetsPath}images/chromeDevtools/justify-items-start-icon.svg\">\r\n        <img title=\"center\" data-value=\"center\" src=\"${assetsPath}images/chromeDevtools/justify-items-center-icon.svg\">\r\n        <img title=\"end\" data-value=\"end\" src=\"${assetsPath}images/chromeDevtools/justify-items-end-icon.svg\">\r\n        <img title=\"stretch\" data-value=\"stretch\" src=\"${assetsPath}images/chromeDevtools/justify-items-stretch-icon.svg\">\r\n      </node-projects-image-button-list-selector>\r\n    </div>\r\n  `;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n    this._size.width = 624;\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event: MouseEvent) {\r\n    super.extend(cache, event);\r\n\r\n    const style = getComputedStyle(this.extendedItem.element);\r\n\r\n    const gridTypeEl = this._toolbar.getById<HTMLSelectElement>('gridType');\r\n    let op = document.createElement('option');\r\n    op.innerText = style.gridTemplateColumns.split(' ').length + 'x' + style.gridTemplateRows.split(' ').length;\r\n    gridTypeEl.insertAdjacentElement('afterbegin', op);\r\n    gridTypeEl.selectedIndex = 0;\r\n    gridTypeEl.onchange = async () => {\r\n      if (gridTypeEl.value == 'custom') {\r\n        const columns = prompt(\"Number of columns?\", '4');\r\n        if (!columns) return;\r\n        const rows = prompt(\"Number of rows?\", '4');\r\n        if (!rows) return;\r\n        const cg = this.extendedItem.openGroup('change grid type');\r\n        await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-columns', 'repeat(' + columns + ', 1fr)');\r\n        await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-rows', 'repeat(' + rows + ', 1fr)');\r\n        cg.commit();\r\n      } else {\r\n        const parts = gridTypeEl.value.split('x');\r\n        const cg = this.extendedItem.openGroup('change grid type');\r\n        await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-columns', 'repeat(' + parts[0] + ', 1fr)');\r\n        await this.extendedItem.updateStyleInSheetOrLocalAsync('grid-template-rows', 'repeat(' + parts[1] + ', 1fr)');\r\n        cg.commit();\r\n      }\r\n    }\r\n\r\n    this._addStyleButton('align-content');\r\n    this._addStyleButton('justify-content');\r\n    this._addStyleButton('align-items');\r\n    this._addStyleButton('justify-items');\r\n\r\n    this.refresh(cache, event);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/grid/GridToolbarExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { GridToolbarExtension } from './GridToolbarExtension.js';\r\nimport { NodeType } from '../../../../item/NodeType.js';\r\nimport { basicStackedToolbarExtensionOverlayOptionName } from '../BasicStackedToolbarExtension.js';\r\n\r\nexport class GridToolbarExtensionProvider implements IDesignerExtensionProvider {\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designItem.nodeType === NodeType.Element) {\r\n      const d = getComputedStyle(designItem.element).display;\r\n      if (d === 'grid' || d === 'inline-grid')\r\n        return designerCanvas.instanceServiceContainer.designContext.extensionOptions[basicStackedToolbarExtensionOverlayOptionName] !== false;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new GridToolbarExtension(extensionManager, designerView, designItem);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/logic/ApplyFirstMachingExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\n\r\nexport class ApplyFirstMachingExtensionProvider implements IDesignerExtensionProvider {\r\n\r\n  private extensions: IDesignerExtensionProvider[]\r\n  private extIndex: number;\r\n  public style: CSSStyleSheet[];\r\n  public svgDefs: string[];\r\n\r\n  constructor(...extensions: IDesignerExtensionProvider[]) {\r\n    this.extensions = extensions;\r\n    for (let e of extensions) {\r\n      if (e.style) {\r\n        if (!this.style)\r\n          this.style = [];\r\n        if (Array.isArray(e.style))\r\n          this.style.push(...e.style);\r\n        else\r\n          this.style.push(e.style);\r\n      }\r\n      if (e.constructor.style) {\r\n        if (!this.style)\r\n          this.style = [];\r\n        if (Array.isArray(e.constructor.style))\r\n          this.style.push(...e.constructor.style);\r\n        else\r\n          this.style.push(e.constructor.style);\r\n      }\r\n      if (e.svgDefs) {\r\n        if (!this.svgDefs)\r\n          this.svgDefs = [];\r\n        if (Array.isArray(e.svgDefs))\r\n          this.svgDefs.push(...e.svgDefs);\r\n        else\r\n          this.svgDefs.push(e.svgDefs);\r\n      }\r\n      if (e.constructor.svgDefs) {\r\n        if (!this.svgDefs)\r\n          this.svgDefs = [];\r\n        if (Array.isArray(e.constructor.svgDefs))\r\n          this.svgDefs.push(...e.svgDefs);\r\n        else\r\n          this.svgDefs.push(e.constructor.svgDefs);\r\n      }\r\n    }\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    for (this.extIndex = 0; this.extIndex < this.extensions.length; this.extIndex++) {\r\n      if (this.extensions[this.extIndex].shouldExtend(extensionManager, designerCanvas, designItem))\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return this.extensions[this.extIndex].getExtension(extensionManager, designerCanvas, designItem);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/logic/ConditionExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { IDesignerExtension } from '../IDesignerExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\n\nexport class ConditionExtensionProvider implements IDesignerExtensionProvider {\n    constructor(extensionProvider: IDesignerExtensionProvider, condition: (designItem: IDesignItem, designerCanvas: IDesignerCanvas) => boolean, recheckOnRefresh: boolean = false) {\n        this.extensionProvider = extensionProvider;\n        this.condition = condition;\n        this.style = <any>extensionProvider.style ?? extensionProvider.constructor.style;\n        this.svgDefs = <any>extensionProvider.svgDefs ?? extensionProvider.constructor.svgDefs;\n    }\n\n    extensionProvider: IDesignerExtensionProvider;\n    condition: (designItem: IDesignItem, designerCanvas: IDesignerCanvas) => boolean;\n    style: CSSStyleSheet;\n    svgDefs: string;\n\n    shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\n        if (!this.condition(designItem, designerCanvas))\n            return false;\n        return this.extensionProvider.shouldExtend(extensionManager, designerCanvas, designItem);\n    }\n\n    getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n        return this.extensionProvider.getExtension(extensionManager, designerCanvas, designItem);\n    }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/AbstractDesignerPointerExtension.ts",
    "content": "\r\nimport { IDesignItem } from \"../../../../item/IDesignItem.js\";\r\nimport { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\nimport { IExtensionManager } from \"../IExtensionManger.js\";\r\nimport { IDesignerPointerExtension } from \"./IDesignerPointerExtension.js\";\r\nimport { AbstractExtensionBase } from '../AbstractExtensionBase.js';\r\n\r\n//TODO: move draw functions to overlay layer, implement designerpointerextension, create ruler\r\nexport abstract class AbstractDesignerPointerExtension extends AbstractExtensionBase implements IDesignerPointerExtension {\r\n  protected extendedItem: IDesignItem;\r\n\r\n  constructor(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas) {\r\n    super(extensionManager, designerCanvas);\r\n  }\r\n\r\n  abstract refresh(event: PointerEvent);\r\n  abstract dispose();\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/CursorLinePointerExtension.ts",
    "content": "import { OverlayLayer } from '../OverlayLayer.js';\r\nimport { AbstractDesignerPointerExtension } from './AbstractDesignerPointerExtension.js';\r\n\r\nexport class CursorLinePointerExtension extends AbstractDesignerPointerExtension {\r\n  private _lineOffset = 5;\r\n  private _lineLength = 10;\r\n\r\n  private _circle: SVGCircleElement;\r\n  private _line1: SVGLineElement;\r\n  private _line2: SVGLineElement;\r\n  private _line3: SVGLineElement;\r\n  private _line4: SVGLineElement;\r\n\r\n  refresh(event: PointerEvent) {\r\n    let mp = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n    this._circle = this._drawCircle(mp.x, mp.y, 1, 'svg-cursor-line', this._circle, OverlayLayer.Foreground);\r\n    this._line1 = this._drawLine(mp.x, mp.y - this._lineOffset, mp.x, mp.y - this._lineOffset - this._lineLength, 'svg-cursor-line', this._line1, OverlayLayer.Foreground);\r\n    this._line2 = this._drawLine(mp.x, mp.y + this._lineOffset, mp.x, mp.y + this._lineOffset + this._lineLength, 'svg-cursor-line', this._line2, OverlayLayer.Foreground);\r\n    this._line3 = this._drawLine(mp.x - this._lineOffset, mp.y, mp.x - this._lineOffset - this._lineLength, mp.y, 'svg-cursor-line', this._line3, OverlayLayer.Foreground);\r\n    this._line4 = this._drawLine(mp.x + this._lineOffset, mp.y, mp.x + this._lineOffset + this._lineLength, mp.y, 'svg-cursor-line', this._line4, OverlayLayer.Foreground);\r\n  }\r\n\r\n  dispose() {\r\n    super._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/CursorLinePointerExtensionProvider.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { CursorLinePointerExtension } from './CursorLinePointerExtension.js';\r\nimport { IDesignerPointerExtension } from './IDesignerPointerExtension.js';\r\nimport { IDesignerPointerExtensionProvider } from './IDesignerPointerExtensionProvider.js';\r\n\r\nexport class CursorLinePointerExtensionProvider implements IDesignerPointerExtensionProvider { \r\n  getExtension(designerCanvas: IDesignerCanvas): IDesignerPointerExtension {\r\n    return new CursorLinePointerExtension(designerCanvas.extensionManager, designerCanvas)\r\n  }\r\n  \r\n  style = css`\r\n    .svg-cursor-line { stroke: black; pointer-events: none }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/IDesignerPointerExtension.ts",
    "content": "import { IDisposable } from \"../../../../../interfaces/IDisposable.js\";\r\n\r\nexport interface IDesignerPointerExtension extends IDisposable {\r\n  refresh(event: PointerEvent);\r\n  style? : CSSStyleSheet;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/IDesignerPointerExtensionProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\nimport { IDesignerPointerExtension } from \"./IDesignerPointerExtension.js\";\r\n\r\nexport interface IDesignerPointerExtensionProvider {\r\n  getExtension(designerView: IDesignerCanvas) : IDesignerPointerExtension;\r\n  style? : CSSStyleSheet;\r\n  svgDefs?: string;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/LinePointerExtension.ts",
    "content": "import { OverlayLayer } from '../OverlayLayer.js';\r\nimport { AbstractDesignerPointerExtension } from './AbstractDesignerPointerExtension.js';\r\n\r\nexport class LinePointerExtension extends AbstractDesignerPointerExtension {\r\n  private _circle: SVGCircleElement;\r\n  private _line1: SVGLineElement;\r\n  private _line2: SVGLineElement;\r\n  private _line3: SVGLineElement;\r\n  private _line4: SVGLineElement;\r\n\r\n  refresh(event: PointerEvent) {\r\n    let mp = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n    this._circle = this._drawCircle(mp.x, mp.y, 1, 'svg-cursor-line-dashed', this._circle, OverlayLayer.Foreground);\r\n    this._line1 = this._drawLine(mp.x, mp.y, mp.x, 0, 'svg-cursor-line-dashed', this._line1, OverlayLayer.Foreground);\r\n    this._line2 = this._drawLine(mp.x, mp.y, mp.x, this.designerCanvas.outerRect.height, 'svg-cursor-line-dashed', this._line2, OverlayLayer.Foreground);\r\n    this._line3 = this._drawLine(mp.x, mp.y, 0, mp.y, 'svg-cursor-line-dashed', this._line3, OverlayLayer.Foreground);\r\n    this._line4 = this._drawLine(mp.x, mp.y, this.designerCanvas.outerRect.width, mp.y, 'svg-cursor-line-dashed', this._line4, OverlayLayer.Foreground);\r\n  }\r\n\r\n  dispose() {\r\n    super._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/pointerExtensions/LinePointerExtensionProvider.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerPointerExtension } from './IDesignerPointerExtension.js';\r\nimport { IDesignerPointerExtensionProvider } from './IDesignerPointerExtensionProvider.js';\r\nimport { LinePointerExtension } from './LinePointerExtension.js';\r\n\r\nexport class LinePointerExtensionProvider implements IDesignerPointerExtensionProvider {\r\n  getExtension(designerCanvas: IDesignerCanvas): IDesignerPointerExtension {\r\n    return new LinePointerExtension(designerCanvas.extensionManager, designerCanvas)\r\n  }\r\n  \r\n  style = css`\r\n    .svg-cursor-line-dashed { stroke: black; pointer-events: none; stroke-dasharray: 4 4 }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/UnifiedGeometryExtension.ts",
    "content": "import { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { AbstractExtension } from '../AbstractExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { IPoint } from '../../../../../interfaces/IPoint.js';\nimport { EventNames } from '../../../../../enums/EventNames.js';\nimport { ContextMenu } from '../../../../helper/contextMenu/ContextMenu.js';\nimport { IContextMenuItem } from '../../../../helper/contextMenu/IContextMenuItem.js';\nimport { OverlayLayer } from '../OverlayLayer.js';\nimport { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './geometry/IGeometry.js';\nimport { getGeometryReader } from './geometry/GeometryReaderFactory.js';\nimport { applyGeometryWritesToDesignItem, applyGeometryWritesToElement } from './geometry/GeometryWriteHelper.js';\nimport { fromOverlayPointToSvgUserSpace, toOverlayPointFromSvgUserSpace } from '../../../../helper/SvgHelper.js';\nimport { ISourcePart, ISvgPathHandleSourcePartData } from '../../../../services/sourceMapService/ISourcePart.js';\nimport { createSvgPathHandleSourcePartKey } from '../../../../services/sourceMapService/SvgPathSourceMapProvider.js';\n\ninterface DragState {\n  startCursorPos: IPoint;\n  segmentIndex: number;\n  handleType: 'anchor' | 'cp1' | 'cp2';\n  originalPoint: IPoint;\n  originalSegments: { point: IPoint; cp1?: IPoint; cp2?: IPoint }[];\n  startScrollOffset: IPoint;\n  geometryChanged: boolean;\n}\n\nexport interface UnifiedGeometryExtensionOptions {\n  allowShapeDrag?: boolean;\n}\n\nexport class UnifiedGeometryExtension extends AbstractExtension {\n\n  private _geometry: IGeometry | null = null;\n  private _reader: IGeometryReader | undefined;\n  private _dragState: DragState | null = null;\n  private _options: UnifiedGeometryExtensionOptions;\n  private _overlayStructureSignature: string | null = null;\n  private _eventCleanups: (() => void)[] = [];\n  /** Map from 'anchor-{index}', 'cp1-{index}', 'cp2-{index}' to overlay circle */\n  private _overlayCircles: Map<string, SVGCircleElement> = new Map();\n  /** Lines for control point connections */\n  private _overlayLines: SVGLineElement[] = [];\n\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem, reader?: IGeometryReader, options?: UnifiedGeometryExtensionOptions) {\n    super(extensionManager, designerView, extendedItem);\n    this._reader = reader ?? getGeometryReader(extendedItem.element);\n    this._options = options ?? {};\n  }\n\n  override extend() {\n    this.refresh();\n  }\n\n  override refresh() {\n    if (!this._reader) {\n      this._cleanupAll();\n      return;\n    }\n\n    this._geometry = this._reader.read(this.extendedItem.element);\n    if (!this._geometry) {\n      this._cleanupAll();\n      return;\n    }\n\n    const structureSignature = this._getOverlayStructureSignature(this._geometry);\n    const shouldRebuild = this._overlayStructureSignature !== structureSignature || this._hasOverlayStructureMismatch(this._geometry);\n\n    if (shouldRebuild) {\n      this._cleanupAll();\n      this._overlayStructureSignature = structureSignature;\n      this._drawOverlays();\n      return;\n    }\n\n    if (!this._valuesHaveChanges(...this._getRefreshValues(this._geometry))) {\n      return;\n    }\n\n    this._updateOverlayAppearance();\n    this._updateOverlayPositions();\n  }\n\n  override dispose() {\n    this._cleanupAll();\n    this._geometry = null;\n    this._dragState = null;\n  }\n\n  private _cleanupAll() {\n    for (const cleanup of this._eventCleanups) {\n      try { cleanup(); } catch { /* ignore */ }\n    }\n    this._eventCleanups = [];\n    this._overlayStructureSignature = null;\n    this._overlayCircles.clear();\n    this._overlayLines = [];\n    this._removeAllOverlays();\n  }\n\n  private _getOverlayStructureSignature(geometry: IGeometry): string {\n    return geometry.segments.map(segment => `${segment.type}:${segment.cp1 ? 1 : 0}:${segment.cp2 ? 1 : 0}`).join('|');\n  }\n\n  private _hasOverlayStructureMismatch(geometry: IGeometry): boolean {\n    let expectedLines = 0;\n    let expectedCircles = 0;\n\n    for (let i = 0; i < geometry.segments.length; i++) {\n      const segment = geometry.segments[i];\n      if (segment.type === SegmentType.Close) {\n        continue;\n      }\n\n      if (this._shouldRenderAnchorHandle(i, segment, geometry)) {\n        expectedCircles++;\n      }\n      if (segment.cp1) {\n        expectedCircles++;\n        expectedLines++;\n        if (segment.type === SegmentType.QuadraticBezier) {\n          expectedLines++;\n        }\n      }\n      if (segment.cp2) {\n        expectedCircles++;\n        expectedLines++;\n      }\n    }\n\n    return this._overlayCircles.size !== expectedCircles || this._overlayLines.length !== expectedLines;\n  }\n\n  private _getRefreshValues(geometry: IGeometry): (number | string)[] {\n    const transformedBoxQuad = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.canvas })[0];\n    const values: (number | string)[] = [\n      this.designerCanvas.zoomFactor,\n      this.designerCanvas.scaleFactor,\n      this._overlayStructureSignature ?? '',\n      this._getSelectedHandleRefreshKey()\n    ];\n\n    if (transformedBoxQuad) {\n      values.push(\n        transformedBoxQuad.p1.x,\n        transformedBoxQuad.p1.y,\n        transformedBoxQuad.p2.x,\n        transformedBoxQuad.p2.y,\n        transformedBoxQuad.p3.x,\n        transformedBoxQuad.p3.y,\n        transformedBoxQuad.p4.x,\n        transformedBoxQuad.p4.y\n      );\n    }\n\n    for (const segment of geometry.segments) {\n      values.push(segment.type, segment.point.x, segment.point.y);\n      if (segment.cp1) {\n        values.push(segment.cp1.x, segment.cp1.y);\n      } else {\n        values.push('cp1:none');\n      }\n      if (segment.cp2) {\n        values.push(segment.cp2.x, segment.cp2.y);\n      } else {\n        values.push('cp2:none');\n      }\n    }\n\n    return values;\n  }\n\n  private _getSelectedHandleRefreshKey(): string {\n    const selectedPart = this.extendedItem.instanceServiceContainer.selectionService.selectedPart;\n    const data = selectedPart?.data as ISvgPathHandleSourcePartData;\n    if (selectedPart?.designItem !== this.extendedItem || selectedPart.kind !== 'svg-path-handle') {\n      return 'selected:none';\n    }\n    return `selected:${data?.segmentIndex}:${data?.handleType}`;\n  }\n\n  /** Convert an SVG user-space coordinate to overlay (canvas) coordinates. */\n  private _toOverlay(p: IPoint): IPoint {\n    return toOverlayPointFromSvgUserSpace(this.extendedItem.element, this.designerCanvas, p);\n  }\n\n  /** Convert overlay (canvas) coordinates back to SVG user-space. */\n  private _fromOverlay(p: IPoint): IPoint {\n    return fromOverlayPointToSvgUserSpace(this.extendedItem.element, this.designerCanvas, p);\n  }\n\n  private _drawOverlays() {\n    if (!this._geometry) return;\n\n    let prevAnchor: IPoint | null = null;\n\n    for (let i = 0; i < this._geometry.segments.length; i++) {\n      const seg = this._geometry.segments[i];\n\n      if (seg.type === SegmentType.Close) {\n        prevAnchor = this._geometry.segments[0]?.point ?? null;\n        continue;\n      }\n\n      // Draw control point handles and dashed lines for bezier segments\n      if (seg.cp1) {\n        const cp1Overlay = this._toOverlay(seg.cp1);\n        if (prevAnchor) {\n          const prevOverlay = this._toOverlay(prevAnchor);\n          const line = this._drawLine(prevOverlay.x, prevOverlay.y, cp1Overlay.x, cp1Overlay.y, 'svg-control-line');\n          this._overlayLines.push(line);\n        }\n        if (seg.type === SegmentType.QuadraticBezier) {\n          const anchorOverlay = this._toOverlay(seg.point);\n          const line = this._drawLine(anchorOverlay.x, anchorOverlay.y, cp1Overlay.x, cp1Overlay.y, 'svg-control-line');\n          this._overlayLines.push(line);\n        }\n        this._drawControlCircle(cp1Overlay, i, 'cp1');\n      }\n\n      if (seg.cp2) {\n        const cp2Overlay = this._toOverlay(seg.cp2);\n        const anchorOverlay = this._toOverlay(seg.point);\n        const line = this._drawLine(anchorOverlay.x, anchorOverlay.y, cp2Overlay.x, cp2Overlay.y, 'svg-control-line');\n        this._overlayLines.push(line);\n        this._drawControlCircle(cp2Overlay, i, 'cp2');\n      }\n\n      // Draw the anchor point\n      if (this._shouldRenderAnchorHandle(i, seg, this._geometry)) {\n        const overlayPt = this._toOverlay(seg.point);\n        this._drawAnchorCircle(overlayPt, i, seg);\n      }\n\n      prevAnchor = seg.point;\n    }\n\n    this._updateOverlayAppearance();\n  }\n\n  private _updateOverlayAppearance() {\n    const anchorRadius = 5 / this.designerCanvas.scaleFactor;\n    const controlRadius = 4 / this.designerCanvas.scaleFactor;\n    const strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n    const dashArray = (4 / this.designerCanvas.scaleFactor).toString();\n\n    for (const [key, circle] of this._overlayCircles) {\n      circle.setAttribute('r', (key.startsWith('anchor-') ? anchorRadius : controlRadius).toString());\n      circle.style.strokeWidth = strokeWidth;\n      circle.classList.toggle('svg-path-selected-part', this._isSelectedHandle(key));\n    }\n\n    for (const line of this._overlayLines) {\n      line.style.strokeDasharray = dashArray;\n      line.style.strokeWidth = strokeWidth;\n    }\n  }\n\n  private _addTrackedListener(el: Element, event: string, handler: (e: any) => void) {\n    el.addEventListener(event, handler);\n    this._eventCleanups.push(() => el.removeEventListener(event, handler));\n  }\n\n  private _drawAnchorCircle(overlayPt: IPoint, segIndex: number, seg: IGeometrySegment) {\n    const r = 5 / this.designerCanvas.scaleFactor;\n    const circle = this._drawCircle(overlayPt.x, overlayPt.y, r, 'svg-path', undefined, OverlayLayer.Foreground);\n    circle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n    circle.style.cursor = 'move';\n    this._overlayCircles.set(`anchor-${segIndex}`, circle);\n\n    const onDown = (e: PointerEvent) => this._onPointerDown(e, circle, segIndex, 'anchor');\n    const onMove = (e: PointerEvent) => this._onPointerMove(e, circle, segIndex, 'anchor');\n    const onUp = (e: PointerEvent) => this._onPointerUp(e, segIndex);\n    const onCtx = (e: MouseEvent) => {\n      e.preventDefault();\n      this._showContextMenu(e, segIndex, seg);\n    };\n\n    this._addTrackedListener(circle, EventNames.PointerDown, onDown);\n    this._addTrackedListener(circle, EventNames.PointerMove, onMove);\n    this._addTrackedListener(circle, EventNames.PointerUp, onUp);\n    this._addTrackedListener(circle, EventNames.ContextMenu, onCtx);\n  }\n\n  private _drawControlCircle(overlayPt: IPoint, segIndex: number, handleType: 'cp1' | 'cp2') {\n    const r = 4 / this.designerCanvas.scaleFactor;\n    const circle = this._drawCircle(overlayPt.x, overlayPt.y, r, 'svg-control-point', undefined, OverlayLayer.Foreground);\n    circle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n    circle.style.cursor = 'move';\n    this._overlayCircles.set(`${handleType}-${segIndex}`, circle);\n\n    const onDown = (e: PointerEvent) => this._onPointerDown(e, circle, segIndex, handleType);\n    const onMove = (e: PointerEvent) => this._onPointerMove(e, circle, segIndex, handleType);\n    const onUp = (e: PointerEvent) => this._onPointerUp(e, segIndex);\n\n    this._addTrackedListener(circle, EventNames.PointerDown, onDown);\n    this._addTrackedListener(circle, EventNames.PointerMove, onMove);\n    this._addTrackedListener(circle, EventNames.PointerUp, onUp);\n  }\n\n  // -- Pointer event handlers --\n\n  private _onPointerDown(event: PointerEvent, circle: SVGCircleElement, segIndex: number, handleType: 'anchor' | 'cp1' | 'cp2') {\n    event.stopPropagation();\n    event.preventDefault();\n    this._selectSourcePart(segIndex, handleType, event);\n    (event.target as Element).setPointerCapture(event.pointerId);\n    const cursorPos = this.designerCanvas.getNormalizedEventCoordinates(event);\n\n    if (!this._geometry) return;\n\n    const seg = this._geometry.segments[segIndex];\n    let originalPoint: IPoint;\n\n    if (handleType === 'anchor') {\n      originalPoint = { ...seg.point };\n    } else if (handleType === 'cp1') {\n      originalPoint = { ...seg.cp1! };\n    } else {\n      originalPoint = { ...seg.cp2! };\n    }\n\n    const originalSegments = this._geometry.segments.map(s => ({\n      point: { ...s.point },\n      cp1: s.cp1 ? { ...s.cp1 } : undefined,\n      cp2: s.cp2 ? { ...s.cp2 } : undefined,\n    }));\n\n    this._dragState = {\n      startCursorPos: cursorPos,\n      segmentIndex: segIndex,\n      handleType,\n      originalPoint,\n      originalSegments,\n      startScrollOffset: this.designerCanvas.canvasOffset,\n      geometryChanged: false,\n    };\n  }\n\n  private _selectSourcePart(segIndex: number, handleType: 'anchor' | 'cp1' | 'cp2', event: Event) {\n    const selectionService = this.extendedItem.instanceServiceContainer.selectionService;\n    const sourcePart = this._getSourcePart(segIndex, handleType);\n    if (selectionService.primarySelection !== this.extendedItem)\n      selectionService.setSelectedElements([this.extendedItem], event, sourcePart);\n\n    else if (sourcePart)\n      selectionService.setSelectedPart(sourcePart, event);\n  }\n\n  private _getSourcePart(segIndex: number, handleType: 'anchor' | 'cp1' | 'cp2'): ISourcePart<ISvgPathHandleSourcePartData> {\n    const key = createSvgPathHandleSourcePartKey(segIndex, handleType);\n    return this.extendedItem.instanceServiceContainer.designItemDocumentPositionService\n      ?.getSourceParts(this.extendedItem)\n      .find(x => x.key === key) as ISourcePart<ISvgPathHandleSourcePartData>;\n  }\n\n  private _isSelectedHandle(circleKey: string) {\n    const [handleType, segmentIndexText] = circleKey.split('-') as ['anchor' | 'cp1' | 'cp2', string];\n    const selectedPart = this.extendedItem.instanceServiceContainer.selectionService.selectedPart;\n    const data = selectedPart?.data as ISvgPathHandleSourcePartData;\n    return selectedPart?.designItem === this.extendedItem\n      && selectedPart.kind === 'svg-path-handle'\n      && data?.segmentIndex === Number(segmentIndexText)\n      && data?.handleType === handleType;\n  }\n\n  private _onPointerMove(event: PointerEvent, circle: SVGCircleElement, segIndex: number, handleType: 'anchor' | 'cp1' | 'cp2') {\n    event.stopPropagation();\n    event.preventDefault();\n    if (!this._dragState || !this._geometry || event.buttons === 0) return;\n    if (this._dragState.segmentIndex !== segIndex || this._dragState.handleType !== handleType) return;\n\n    const cursorPos = this.designerCanvas.getNormalizedEventCoordinates(event);\n    let dx = cursorPos.x - this._dragState.startCursorPos.x;\n    let dy = cursorPos.y - this._dragState.startCursorPos.y;\n    if (dx === 0 && dy === 0) return;\n\n    // Shift key: constrain to axis\n    if (event.shiftKey) {\n      if (Math.abs(dx) >= Math.abs(dy)) {\n        dy = 0;\n      } else {\n        dx = 0;\n      }\n    }\n    if (dx === 0 && dy === 0) return;\n\n    // Alt key + anchor drag: move entire shape\n    if (event.altKey && handleType === 'anchor' && this._options.allowShapeDrag !== false) {\n      this._dragWholeShape(dx, dy);\n    } else {\n      this._dragSingleHandle(segIndex, handleType, dx, dy);\n    }\n    if (handleType === 'anchor') {\n      this._normalizeAxisLineSegments();\n    }\n    this._dragState.geometryChanged = true;\n\n    // Update geometry model\n    this._applyGeometryToElement();\n    // Update overlay positions in-place (no recreate to preserve pointer capture)\n    this._updateOverlayPositions();\n    this.designerCanvas.extensionManager.refreshAllExtensions([this.extendedItem], this);\n  }\n\n  private _dragSingleHandle(segIndex: number, handleType: string, dx: number, dy: number) {\n    const origOverlay = this._toOverlay(this._dragState!.originalPoint);\n    const newOverlay: IPoint = { x: origOverlay.x + dx, y: origOverlay.y + dy };\n    const newLocal = this._fromOverlay(newOverlay);\n\n    const seg = this._geometry!.segments[segIndex];\n\n    if (handleType === 'anchor') {\n      if (this.extendedItem.element instanceof SVGRectElement && this._dragRectCorner(segIndex, newLocal)) {\n        return;\n      }\n      if (this.extendedItem.element instanceof SVGCircleElement && this._dragCircleCardinal(segIndex, newLocal)) {\n        return;\n      }\n      if (this.extendedItem.element instanceof SVGEllipseElement && this._dragEllipseCardinal(segIndex, newLocal)) {\n        return;\n      }\n      if (seg.type === SegmentType.HorizontalLine) {\n        const prevPt = this._findPreviousAnchor(segIndex);\n        seg.point.x = newLocal.x;\n        seg.point.y = prevPt?.y ?? seg.point.y;\n      } else if (seg.type === SegmentType.VerticalLine) {\n        const prevPt = this._findPreviousAnchor(segIndex);\n        seg.point.x = prevPt?.x ?? seg.point.x;\n        seg.point.y = newLocal.y;\n      } else {\n        seg.point.x = newLocal.x;\n        seg.point.y = newLocal.y;\n      }\n    } else if (handleType === 'cp1') {\n      seg.cp1!.x = newLocal.x;\n      seg.cp1!.y = newLocal.y;\n    } else {\n      seg.cp2!.x = newLocal.x;\n      seg.cp2!.y = newLocal.y;\n    }\n  }\n\n  private _normalizeAxisLineSegments() {\n    if (!this._geometry) return;\n\n    let prevAnchor: IPoint | null = null;\n    const firstAnchor = this._geometry.segments.find(s => s.type !== SegmentType.Close)?.point;\n\n    for (const seg of this._geometry.segments) {\n      if (seg.type === SegmentType.Close) {\n        if (firstAnchor) {\n          seg.point = { ...firstAnchor };\n        }\n        continue;\n      }\n\n      if (prevAnchor) {\n        if (seg.type === SegmentType.HorizontalLine) {\n          seg.point.y = prevAnchor.y;\n        } else if (seg.type === SegmentType.VerticalLine) {\n          seg.point.x = prevAnchor.x;\n        }\n      }\n\n      prevAnchor = seg.point;\n    }\n  }\n\n  private _dragRectCorner(segIndex: number, newPoint: IPoint): boolean {\n    if (!this._geometry || !this._dragState?.originalSegments) return false;\n    if (this._geometry.segments.length < 4 || segIndex < 0 || segIndex > 3) return false;\n\n    const originalSegments = this._dragState.originalSegments;\n\n    switch (segIndex) {\n      case 0:\n        this._setRectCornerPoints(\n          newPoint,\n          { x: originalSegments[2].point.x, y: newPoint.y },\n          { ...originalSegments[2].point },\n          { x: newPoint.x, y: originalSegments[2].point.y }\n        );\n        break;\n      case 1:\n        this._setRectCornerPoints(\n          { x: originalSegments[3].point.x, y: newPoint.y },\n          newPoint,\n          { x: newPoint.x, y: originalSegments[3].point.y },\n          { ...originalSegments[3].point }\n        );\n        break;\n      case 2:\n        this._setRectCornerPoints(\n          { ...originalSegments[0].point },\n          { x: newPoint.x, y: originalSegments[0].point.y },\n          newPoint,\n          { x: originalSegments[0].point.x, y: newPoint.y }\n        );\n        break;\n      case 3:\n        this._setRectCornerPoints(\n          { x: newPoint.x, y: originalSegments[1].point.y },\n          { ...originalSegments[1].point },\n          { x: originalSegments[1].point.x, y: newPoint.y },\n          newPoint\n        );\n        break;\n      default:\n        return false;\n    }\n\n    const closeSegment = this._geometry.segments[4];\n    if (closeSegment?.type === SegmentType.Close) {\n      closeSegment.point = { ...this._geometry.segments[0].point };\n    }\n\n    return true;\n  }\n\n  private _setRectCornerPoints(topLeft: IPoint, topRight: IPoint, bottomRight: IPoint, bottomLeft: IPoint) {\n    this._geometry!.segments[0].point = { ...topLeft };\n    this._geometry!.segments[1].point = { ...topRight };\n    this._geometry!.segments[2].point = { ...bottomRight };\n    this._geometry!.segments[3].point = { ...bottomLeft };\n  }\n\n  private _dragCircleCardinal(segIndex: number, newPoint: IPoint): boolean {\n    if (!this._geometry || !this._dragState?.originalSegments) return false;\n\n    const cardinalIndex = this._normalizeCardinalIndex(segIndex);\n    if (cardinalIndex === null) return false;\n\n    const original = this._dragState.originalSegments;\n    const centerX = (original[1].point.x + original[3].point.x) / 2;\n    const centerY = (original[0].point.y + original[2].point.y) / 2;\n\n    let cx = centerX;\n    let cy = centerY;\n    let radius = Math.abs(original[1].point.x - original[3].point.x) / 2;\n\n    switch (cardinalIndex) {\n      case 0: {\n        const oppositeY = original[2].point.y;\n        cy = (newPoint.y + oppositeY) / 2;\n        radius = Math.abs(oppositeY - newPoint.y) / 2;\n        break;\n      }\n      case 1: {\n        const oppositeX = original[3].point.x;\n        cx = (newPoint.x + oppositeX) / 2;\n        radius = Math.abs(newPoint.x - oppositeX) / 2;\n        break;\n      }\n      case 2: {\n        const oppositeY = original[0].point.y;\n        cy = (original[0].point.y + newPoint.y) / 2;\n        radius = Math.abs(newPoint.y - oppositeY) / 2;\n        break;\n      }\n      case 3: {\n        const oppositeX = original[1].point.x;\n        cx = (oppositeX + newPoint.x) / 2;\n        radius = Math.abs(oppositeX - newPoint.x) / 2;\n        break;\n      }\n      default:\n        return false;\n    }\n\n    this._setCardinalArcPoints(cx, cy, radius, radius);\n    return true;\n  }\n\n  private _dragEllipseCardinal(segIndex: number, newPoint: IPoint): boolean {\n    if (!this._geometry || !this._dragState?.originalSegments) return false;\n\n    const cardinalIndex = this._normalizeCardinalIndex(segIndex);\n    if (cardinalIndex === null) return false;\n\n    const original = this._dragState.originalSegments;\n    let cx = (original[1].point.x + original[3].point.x) / 2;\n    let cy = (original[0].point.y + original[2].point.y) / 2;\n    let rx = Math.abs(original[1].point.x - original[3].point.x) / 2;\n    let ry = Math.abs(original[2].point.y - original[0].point.y) / 2;\n\n    switch (cardinalIndex) {\n      case 0: {\n        const oppositeY = original[2].point.y;\n        cy = (newPoint.y + oppositeY) / 2;\n        ry = Math.abs(oppositeY - newPoint.y) / 2;\n        break;\n      }\n      case 1: {\n        const oppositeX = original[3].point.x;\n        cx = (newPoint.x + oppositeX) / 2;\n        rx = Math.abs(newPoint.x - oppositeX) / 2;\n        break;\n      }\n      case 2: {\n        const oppositeY = original[0].point.y;\n        cy = (oppositeY + newPoint.y) / 2;\n        ry = Math.abs(newPoint.y - oppositeY) / 2;\n        break;\n      }\n      case 3: {\n        const oppositeX = original[1].point.x;\n        cx = (oppositeX + newPoint.x) / 2;\n        rx = Math.abs(oppositeX - newPoint.x) / 2;\n        break;\n      }\n      default:\n        return false;\n    }\n\n    this._setCardinalArcPoints(cx, cy, rx, ry);\n    return true;\n  }\n\n  private _normalizeCardinalIndex(segIndex: number): 0 | 1 | 2 | 3 | null {\n    if (segIndex >= 0 && segIndex <= 3) {\n      return segIndex as 0 | 1 | 2 | 3;\n    }\n    if (segIndex === 4) {\n      return 0;\n    }\n    return null;\n  }\n\n  private _setCardinalArcPoints(cx: number, cy: number, rx: number, ry: number) {\n    if (!this._geometry) return;\n\n    this._geometry.segments[0].point = { x: cx, y: cy - ry };\n    this._geometry.segments[1].point = { x: cx + rx, y: cy };\n    this._geometry.segments[2].point = { x: cx, y: cy + ry };\n    this._geometry.segments[3].point = { x: cx - rx, y: cy };\n\n    if (this._geometry.segments[1].arc) {\n      this._geometry.segments[1].arc.rx = rx;\n      this._geometry.segments[1].arc.ry = ry;\n    }\n    if (this._geometry.segments[2].arc) {\n      this._geometry.segments[2].arc.rx = rx;\n      this._geometry.segments[2].arc.ry = ry;\n    }\n    if (this._geometry.segments[3].arc) {\n      this._geometry.segments[3].arc.rx = rx;\n      this._geometry.segments[3].arc.ry = ry;\n    }\n    if (this._geometry.segments[4]?.arc) {\n      this._geometry.segments[4].point = { x: cx, y: cy - ry };\n      this._geometry.segments[4].arc.rx = rx;\n      this._geometry.segments[4].arc.ry = ry;\n    }\n    if (this._geometry.segments[5]?.type === SegmentType.Close) {\n      this._geometry.segments[5].point = { x: cx, y: cy - ry };\n    }\n  }\n\n  private _shouldRenderAnchorHandle(segIndex: number, seg: IGeometrySegment, geometry: IGeometry): boolean {\n    if (!(this.extendedItem.element instanceof SVGCircleElement) && !(this.extendedItem.element instanceof SVGEllipseElement)) {\n      return true;\n    }\n\n    if (segIndex !== 4) {\n      return true;\n    }\n\n    const firstSegment = geometry.segments[0];\n    return seg.point.x !== firstSegment?.point.x || seg.point.y !== firstSegment?.point.y;\n  }\n\n  private _dragWholeShape(dx: number, dy: number) {\n    if (!this._dragState?.originalSegments || !this._geometry) return;\n\n    for (let i = 0; i < this._geometry.segments.length; i++) {\n      const orig = this._dragState.originalSegments[i];\n      const seg = this._geometry.segments[i];\n\n      const origOverlay = this._toOverlay(orig.point);\n      const newOverlay: IPoint = { x: origOverlay.x + dx, y: origOverlay.y + dy };\n      const newLocal = this._fromOverlay(newOverlay);\n      seg.point.x = newLocal.x;\n      seg.point.y = newLocal.y;\n\n      if (seg.cp1 && orig.cp1) {\n        const origCp1Overlay = this._toOverlay(orig.cp1);\n        const newCp1Overlay: IPoint = { x: origCp1Overlay.x + dx, y: origCp1Overlay.y + dy };\n        const newCp1Local = this._fromOverlay(newCp1Overlay);\n        seg.cp1.x = newCp1Local.x;\n        seg.cp1.y = newCp1Local.y;\n      }\n      if (seg.cp2 && orig.cp2) {\n        const origCp2Overlay = this._toOverlay(orig.cp2);\n        const newCp2Overlay: IPoint = { x: origCp2Overlay.x + dx, y: origCp2Overlay.y + dy };\n        const newCp2Local = this._fromOverlay(newCp2Overlay);\n        seg.cp2.x = newCp2Local.x;\n        seg.cp2.y = newCp2Local.y;\n      }\n    }\n  }\n\n  /** Update existing overlay circle/line positions without removing/recreating them.\n   *  This preserves pointer capture during drag. */\n  private _updateOverlayPositions() {\n    if (!this._geometry) return;\n\n    let prevAnchor: IPoint | null = null;\n    let lineIdx = 0;\n\n    for (let i = 0; i < this._geometry.segments.length; i++) {\n      const seg = this._geometry.segments[i];\n      if (seg.type === SegmentType.Close) {\n        prevAnchor = this._geometry.segments[0]?.point ?? null;\n        continue;\n      }\n\n      if (seg.cp1) {\n        const cp1Overlay = this._toOverlay(seg.cp1);\n        if (prevAnchor && lineIdx < this._overlayLines.length) {\n          const prevOverlay = this._toOverlay(prevAnchor);\n          const line = this._overlayLines[lineIdx++];\n          line.setAttribute('x1', prevOverlay.x.toString());\n          line.setAttribute('y1', prevOverlay.y.toString());\n          line.setAttribute('x2', cp1Overlay.x.toString());\n          line.setAttribute('y2', cp1Overlay.y.toString());\n        }\n        if (seg.type === SegmentType.QuadraticBezier && lineIdx < this._overlayLines.length) {\n          const anchorOverlay = this._toOverlay(seg.point);\n          const line = this._overlayLines[lineIdx++];\n          line.setAttribute('x1', anchorOverlay.x.toString());\n          line.setAttribute('y1', anchorOverlay.y.toString());\n          line.setAttribute('x2', cp1Overlay.x.toString());\n          line.setAttribute('y2', cp1Overlay.y.toString());\n        }\n        const cp1Circle = this._overlayCircles.get(`cp1-${i}`);\n        if (cp1Circle) {\n          cp1Circle.setAttribute('cx', cp1Overlay.x.toString());\n          cp1Circle.setAttribute('cy', cp1Overlay.y.toString());\n        }\n      }\n      if (seg.cp2) {\n        const cp2Overlay = this._toOverlay(seg.cp2);\n        const anchorOverlay = this._toOverlay(seg.point);\n        if (lineIdx < this._overlayLines.length) {\n          const line = this._overlayLines[lineIdx++];\n          line.setAttribute('x1', anchorOverlay.x.toString());\n          line.setAttribute('y1', anchorOverlay.y.toString());\n          line.setAttribute('x2', cp2Overlay.x.toString());\n          line.setAttribute('y2', cp2Overlay.y.toString());\n        }\n        const cp2Circle = this._overlayCircles.get(`cp2-${i}`);\n        if (cp2Circle) {\n          cp2Circle.setAttribute('cx', cp2Overlay.x.toString());\n          cp2Circle.setAttribute('cy', cp2Overlay.y.toString());\n        }\n      }\n\n      if (this._shouldRenderAnchorHandle(i, seg, this._geometry)) {\n        const overlayPt = this._toOverlay(seg.point);\n        const anchorCircle = this._overlayCircles.get(`anchor-${i}`);\n        if (anchorCircle) {\n          anchorCircle.setAttribute('cx', overlayPt.x.toString());\n          anchorCircle.setAttribute('cy', overlayPt.y.toString());\n        }\n      }\n\n      prevAnchor = seg.point;\n    }\n  }\n\n  private _onPointerUp(event: PointerEvent, segIndex: number) {\n    event.stopPropagation();\n    (event.target as Element).releasePointerCapture(event.pointerId);\n\n    if (this._dragState?.geometryChanged && this._geometry) {\n      // Commit via designItem for undo support (follows PathExtension pattern)\n      this._commitGeometryChange();\n    }\n    this._dragState = null;\n  }\n\n  // -- Geometry manipulation --\n\n  /** Set attributes directly on the DOM element for live visual feedback during drag */\n  private _applyGeometryToElement() {\n    if (!this._reader || !this._geometry) return;\n    const attrs = this._reader.serialize(this._geometry);\n    applyGeometryWritesToElement(this.extendedItem.element, attrs, this.extendedItem.serviceContainer.options.roundPixelsToDecimalPlaces);\n  }\n\n  /** Commit geometry change through the designItem undo system.\n   *  group.commit() will trigger refreshAllExtensions which redraws overlays. */\n  private _commitGeometryChange() {\n    if (!this._reader || !this._geometry) return;\n    const attrs = this._reader.serialize(this._geometry);\n    const group = this.extendedItem.openGroup('editGeometry');\n    applyGeometryWritesToDesignItem(this.extendedItem, attrs);\n    group.commit();\n  }\n\n  // -- Context menu --\n\n  private _showContextMenu(event: MouseEvent, segIndex: number, seg: IGeometrySegment) {\n    if (!this._geometry) return;\n    const items: IContextMenuItem[] = [];\n    const isPath = this.extendedItem.element instanceof SVGPathElement;\n    const nonCloseSegments = this._geometry.segments.filter(s => s.type !== SegmentType.Close);\n\n    // Delete point (only if not the only point)\n    if (nonCloseSegments.length > 2) {\n      items.push({\n        title: 'Delete point', action: () => {\n          if (!this._geometry) return;\n          this._geometry.segments.splice(segIndex, 1);\n          if (segIndex === 0 && this._geometry.segments.length > 0 && this._geometry.segments[0].type !== SegmentType.Close) {\n            this._geometry.segments[0].type = SegmentType.Move;\n          }\n          this._applyGeometryToElement();\n          this._commitGeometryChange();\n        }\n      });\n    }\n\n    // Insert point after\n    if (isPath) {\n      const nextSeg = this._findNextNonCloseSegment(segIndex);\n      if (nextSeg !== null) {\n        items.push({\n          title: 'Insert point after', action: () => {\n            this._insertPointAfter(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n    }\n\n    // Close / Open path\n    if (isPath && this._geometry.segments.length > 0) {\n      const hasClose = this._geometry.segments.some(s => s.type === SegmentType.Close);\n      const lastNonCloseIdx = this._findLastNonCloseIndex();\n      if (segIndex === lastNonCloseIdx || (hasClose && seg.type !== SegmentType.Close)) {\n        items.push({ title: '---' });\n        if (hasClose) {\n          items.push({\n            title: 'Open path', action: () => {\n              if (!this._geometry) return;\n              this._geometry.segments = this._geometry.segments.filter(s => s.type !== SegmentType.Close);\n              this._geometry.closed = false;\n              this._applyGeometryToElement();\n              this._commitGeometryChange();\n            }\n          });\n        } else {\n          items.push({\n            title: 'Close path', action: () => {\n              if (!this._geometry) return;\n              this._geometry.segments.push({ type: SegmentType.Close, relative: false, point: this._geometry.segments[0].point });\n              this._geometry.closed = true;\n              this._applyGeometryToElement();\n              this._commitGeometryChange();\n            }\n          });\n        }\n      }\n    }\n\n    // Segment type conversion (only for path elements)\n    if (isPath && segIndex > 0 && seg.type !== SegmentType.Close) {\n      items.push({ title: '---' });\n      const conversionItems: IContextMenuItem[] = [];\n\n      if (seg.type !== SegmentType.Move) {\n        conversionItems.push({\n          title: 'moveto', action: () => {\n            this._convertToMove(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.Line && seg.type !== SegmentType.HorizontalLine && seg.type !== SegmentType.VerticalLine) {\n        conversionItems.push({\n          title: 'line', action: () => {\n            this._convertToLine(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.HorizontalLine) {\n        conversionItems.push({\n          title: 'horizontal line', action: () => {\n            this._convertToHorizontalLine(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.VerticalLine) {\n        conversionItems.push({\n          title: 'vertical line', action: () => {\n            this._convertToVerticalLine(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.QuadraticBezier) {\n        conversionItems.push({\n          title: 'quadratic bézier', action: () => {\n            this._convertToQuadratic(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.CubicBezier) {\n        conversionItems.push({\n          title: 'cubic bézier', action: () => {\n            this._convertToCubic(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (seg.type !== SegmentType.Arc) {\n        conversionItems.push({\n          title: 'arc', action: () => {\n            this._convertToArc(segIndex, false);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n        conversionItems.push({\n          title: 'inverted arc', action: () => {\n            this._convertToArc(segIndex, true);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n\n      if (conversionItems.length > 0) {\n        items.push({ title: 'Convert to', children: conversionItems });\n      }\n\n      if (seg.type === SegmentType.Arc) {\n        items.push({\n          title: 'Invert arc', action: () => {\n            this._invertArc(segIndex);\n            this._applyGeometryToElement();\n            this._commitGeometryChange();\n          }\n        });\n      }\n    }\n\n    if (items.length > 0) {\n      ContextMenu.show(items, event);\n    }\n  }\n\n  private _findLastNonCloseIndex(): number {\n    if (!this._geometry) return -1;\n    for (let i = this._geometry.segments.length - 1; i >= 0; i--) {\n      if (this._geometry.segments[i].type !== SegmentType.Close) return i;\n    }\n    return -1;\n  }\n\n  private _findNextNonCloseSegment(segIndex: number): number | null {\n    if (!this._geometry) return null;\n    for (let i = segIndex + 1; i < this._geometry.segments.length; i++) {\n      if (this._geometry.segments[i].type !== SegmentType.Close) return i;\n    }\n    return null;\n  }\n\n  private _findPreviousAnchor(segIndex: number): IPoint | null {\n    if (!this._geometry) return null;\n    for (let i = segIndex - 1; i >= 0; i--) {\n      const s = this._geometry.segments[i];\n      if (s.type !== SegmentType.Close) return s.point;\n    }\n    return null;\n  }\n\n  private _insertPointAfter(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const nextIdx = this._findNextNonCloseSegment(segIndex);\n    if (nextIdx === null) return;\n\n    const nextSeg = this._geometry.segments[nextIdx];\n    let newPoint: IPoint;\n\n    switch (nextSeg.type) {\n      case SegmentType.CubicBezier: {\n        // Evaluate cubic bezier at t=0.5 using De Casteljau\n        const p0 = seg.point;\n        const p1 = <IPoint>nextSeg.cp1;\n        const p2 = <IPoint>nextSeg.cp2;\n        const p3 = nextSeg.point;\n        newPoint = this._evalCubicBezier(p0, p1, p2, p3, 0.5);\n        break;\n      }\n      case SegmentType.QuadraticBezier: {\n        // Evaluate quadratic bezier at t=0.5\n        const p0 = seg.point;\n        const p1 = <IPoint>nextSeg.cp1;\n        const p2 = nextSeg.point;\n        newPoint = this._evalQuadraticBezier(p0, p1, p2, 0.5);\n        break;\n      }\n      default: {\n        // Linear midpoint\n        newPoint = {\n          x: (seg.point.x + nextSeg.point.x) / 2,\n          y: (seg.point.y + nextSeg.point.y) / 2,\n        };\n        break;\n      }\n    }\n\n    const newSeg: IGeometrySegment = {\n      type: SegmentType.Line,\n      relative: seg.relative,\n      point: newPoint,\n    };\n    this._geometry.segments.splice(segIndex + 1, 0, newSeg);\n  }\n\n  private _convertToMove(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    seg.type = SegmentType.Move;\n    delete seg.cp1;\n    delete seg.cp2;\n    delete seg.arc;\n  }\n\n  private _convertToLine(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    seg.type = SegmentType.Line;\n    delete seg.cp1;\n    delete seg.cp2;\n    delete seg.arc;\n  }\n\n  private _convertToHorizontalLine(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const prevPt = this._findPreviousAnchor(segIndex);\n    if (!prevPt) return;\n\n    seg.type = SegmentType.HorizontalLine;\n    seg.point.y = prevPt.y;\n    delete seg.cp1;\n    delete seg.cp2;\n    delete seg.arc;\n  }\n\n  private _convertToVerticalLine(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const prevPt = this._findPreviousAnchor(segIndex);\n    if (!prevPt) return;\n\n    seg.type = SegmentType.VerticalLine;\n    seg.point.x = prevPt.x;\n    delete seg.cp1;\n    delete seg.cp2;\n    delete seg.arc;\n  }\n\n  private _convertToQuadratic(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const prevPt = this._findPreviousAnchor(segIndex);\n    if (!prevPt) return;\n\n    const endPt = seg.point;\n    // Generate control point perpendicular to the midpoint\n    const mpx = (prevPt.x + endPt.x) / 2;\n    const mpy = (prevPt.y + endPt.y) / 2;\n    const dist = Math.sqrt(Math.pow(endPt.x - prevPt.x, 2) + Math.pow(endPt.y - prevPt.y, 2));\n    const offset = Math.max(20, dist * 0.25);\n    const theta = Math.atan2(endPt.y - prevPt.y, endPt.x - prevPt.x) - Math.PI / 2;\n\n    seg.type = SegmentType.QuadraticBezier;\n    seg.cp1 = {\n      x: mpx + offset * Math.cos(theta),\n      y: mpy + offset * Math.sin(theta),\n    };\n    delete seg.cp2;\n    delete seg.arc;\n  }\n\n  private _convertToCubic(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const prevPt = this._findPreviousAnchor(segIndex);\n    if (!prevPt) return;\n\n    const endPt = seg.point;\n\n    if (seg.type === SegmentType.QuadraticBezier && seg.cp1) {\n      // Elevate quadratic to cubic: C_cp1 = P0 + 2/3*(Q_cp1 - P0), C_cp2 = P3 + 2/3*(Q_cp1 - P3)\n      seg.type = SegmentType.CubicBezier;\n      const qcp = seg.cp1;\n      seg.cp1 = {\n        x: prevPt.x + (2 / 3) * (qcp.x - prevPt.x),\n        y: prevPt.y + (2 / 3) * (qcp.y - prevPt.y),\n      };\n      seg.cp2 = {\n        x: endPt.x + (2 / 3) * (qcp.x - endPt.x),\n        y: endPt.y + (2 / 3) * (qcp.y - endPt.y),\n      };\n    } else {\n      // Generate default control points at 1/3 and 2/3 along the line\n      seg.type = SegmentType.CubicBezier;\n      seg.cp1 = {\n        x: prevPt.x + (endPt.x - prevPt.x) / 3,\n        y: prevPt.y + (endPt.y - prevPt.y) / 3,\n      };\n      seg.cp2 = {\n        x: prevPt.x + 2 * (endPt.x - prevPt.x) / 3,\n        y: prevPt.y + 2 * (endPt.y - prevPt.y) / 3,\n      };\n    }\n    delete seg.arc;\n  }\n\n  private _convertToArc(segIndex: number, inverted: boolean = false) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    const prevPt = this._findPreviousAnchor(segIndex);\n    if (!prevPt) return;\n\n    const endPt = seg.point;\n    const dx = endPt.x - prevPt.x;\n    const dy = endPt.y - prevPt.y;\n    const dist = Math.sqrt(dx * dx + dy * dy);\n    const radius = Math.max(1, dist / 2);\n\n    seg.type = SegmentType.Arc;\n    seg.arc = {\n      rx: radius,\n      ry: radius,\n      rotation: 0,\n      largeArc: false,\n      sweep: !inverted,\n    };\n    delete seg.cp1;\n    delete seg.cp2;\n  }\n\n  private _invertArc(segIndex: number) {\n    if (!this._geometry) return;\n    const seg = this._geometry.segments[segIndex];\n    if (seg.type !== SegmentType.Arc || !seg.arc) return;\n\n    seg.arc.sweep = !seg.arc.sweep;\n  }\n\n  // -- Bezier evaluation --\n\n  private _evalCubicBezier(p0: IPoint, p1: IPoint, p2: IPoint, p3: IPoint, t: number): IPoint {\n    const mt = 1 - t;\n    return {\n      x: mt * mt * mt * p0.x + 3 * mt * mt * t * p1.x + 3 * mt * t * t * p2.x + t * t * t * p3.x,\n      y: mt * mt * mt * p0.y + 3 * mt * mt * t * p1.y + 3 * mt * t * t * p2.y + t * t * t * p3.y,\n    };\n  }\n\n  private _evalQuadraticBezier(p0: IPoint, p1: IPoint, p2: IPoint, t: number): IPoint {\n    const mt = 1 - t;\n    return {\n      x: mt * mt * p0.x + 2 * mt * t * p1.x + t * t * p2.x,\n      y: mt * mt * p0.y + 2 * mt * t * p1.y + t * t * p2.y,\n    };\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/UnifiedGeometryExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { IDesignerExtension } from '../IDesignerExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { UnifiedGeometryExtension } from './UnifiedGeometryExtension.js';\nimport { isVisualSvgElement } from '../../../../helper/SvgHelper.js';\nimport { IGeometryReader } from './geometry/IGeometry.js';\nimport { css } from '@node-projects/base-custom-webcomponent';\n\nexport class UnifiedGeometryExtensionProvider implements IDesignerExtensionProvider {\n\n  private _customReader?: IGeometryReader;\n\n  constructor(customReader?: IGeometryReader) {\n    this._customReader = customReader;\n  }\n\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\n    if (this._customReader) return true;\n\n    const node = designItem.node;\n    if (node instanceof SVGPathElement ||\n      node instanceof SVGRectElement ||\n      node instanceof SVGLineElement ||\n      node instanceof SVGEllipseElement ||\n      node instanceof SVGCircleElement ||\n      node instanceof SVGPolygonElement ||\n      node instanceof SVGPolylineElement) {\n      return isVisualSvgElement(node);\n    }\n    return false;\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new UnifiedGeometryExtension(extensionManager, designerView, designItem, this._customReader);\n  }\n\n  readonly style = css`\n    .svg-control-line {\n      stroke: #3899ec;\n      fill: none;\n      stroke-dasharray: 4;\n      pointer-events: none;\n      opacity: 0.9;\n    }\n\n    .svg-control-point {\n      stroke: #3899ec;\n      fill: white;\n      pointer-events: auto;\n    }\n\n    .svg-path-selected-part {\n      stroke: black;\n      fill: lime;\n    }\n  `;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/CssClipPathGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\n/**\n * Reads and writes CSS `clip-path` property geometry.\n * Supports: polygon(), circle(), ellipse(), path()\n */\nexport class CssClipPathGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const style = getComputedStyle(element);\n    const raw = style.clipPath || element.getAttribute('style')?.match(/clip-path\\s*:\\s*([^;]+)/)?.[1] || '';\n    return this._parse(raw);\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const value = this._serializeGeometry(geometry);\n    return [{ attribute: 'style:clipPath', value }];\n  }\n\n  private _parse(raw: string): IGeometry {\n    const trimmed = raw.trim();\n\n    if (trimmed.startsWith('polygon(')) {\n      return this._parsePolygon(trimmed);\n    }\n    if (trimmed.startsWith('circle(')) {\n      return this._parseCircle(trimmed);\n    }\n    if (trimmed.startsWith('ellipse(')) {\n      return this._parseEllipse(trimmed);\n    }\n    if (trimmed.startsWith('path(')) {\n      return this._parsePath(trimmed);\n    }\n\n    return { segments: [], closed: false };\n  }\n\n  private _parsePolygon(raw: string): IGeometry {\n    // polygon(x1 y1, x2 y2, ...)\n    const inner = raw.slice(8, -1).trim();\n    const segments: IGeometrySegment[] = [];\n    const pairs = inner.split(',');\n    for (let i = 0; i < pairs.length; i++) {\n      const parts = pairs[i].trim().split(/\\s+/);\n      if (parts.length >= 2) {\n        const x = parseFloat(parts[0]);\n        const y = parseFloat(parts[1]);\n        if (!isNaN(x) && !isNaN(y)) {\n          segments.push({\n            type: i === 0 ? SegmentType.Move : SegmentType.Line,\n            relative: false,\n            point: { x, y },\n          });\n        }\n      }\n    }\n    if (segments.length > 0) {\n      segments.push({ type: SegmentType.Close, relative: false, point: { ...segments[0].point } });\n    }\n    return { segments, closed: true };\n  }\n\n  private _parseCircle(raw: string): IGeometry {\n    // circle(r at cx cy)\n    const inner = raw.slice(7, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let r = 50, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      r = parseFloat(inner.substring(0, atIdx));\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    } else {\n      r = parseFloat(inner) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - r } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - r } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _parseEllipse(raw: string): IGeometry {\n    // ellipse(rx ry at cx cy)\n    const inner = raw.slice(8, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let rx = 50, ry = 25, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      const radii = inner.substring(0, atIdx).trim().split(/\\s+/);\n      rx = parseFloat(radii[0]) || 50;\n      ry = parseFloat(radii[1]) || 25;\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - ry } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - ry } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _parsePath(raw: string): IGeometry {\n    // path(\"d string\") — delegate to an internal SVG path parser\n    // Extract d string between quotes\n    const match = raw.match(/path\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/);\n    if (!match) return { segments: [], closed: false };\n    // Create a temporary SVG path element to parse\n    const tempNs = 'http://www.w3.org/2000/svg';\n    const tempSvg = document.createElementNS(tempNs, 'svg');\n    const tempPath = document.createElementNS(tempNs, 'path');\n    tempPath.setAttribute('d', match[1]);\n    tempSvg.appendChild(tempPath);\n    // Temporarily attach for getPathData to work\n    tempSvg.style.position = 'absolute';\n    tempSvg.style.width = '0';\n    tempSvg.style.height = '0';\n    tempSvg.style.overflow = 'hidden';\n    document.body.appendChild(tempSvg);\n    try {\n      const { SvgPathGeometryReader } = require('./SvgPathGeometryReader.js');\n      const reader = new SvgPathGeometryReader();\n      return reader.read(tempPath);\n    } catch {\n      return { segments: [], closed: false };\n    } finally {\n      document.body.removeChild(tempSvg);\n    }\n  }\n\n  private _serializeGeometry(geometry: IGeometry): string {\n    // Detect the shape type from the segment structure\n    const nonClose = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (nonClose.length === 0) return 'none';\n\n    // If all non-move/close segments are arcs with equal rx/ry => circle or ellipse\n    const arcs = nonClose.filter(s => s.type === SegmentType.Arc);\n    if (arcs.length >= 3 && arcs[0].arc) {\n      // Compute center from cardinal points\n      const pts = nonClose;\n      const top = pts[0].point;\n      const right = pts[1].point;\n      const bottom = pts[2].point;\n      const left = pts[3].point;\n      const cx = (right.x + left.x) / 2;\n      const cy = (top.y + bottom.y) / 2;\n      const finalRx = Math.abs(right.x - left.x) / 2;\n      const finalRy = Math.abs(bottom.y - top.y) / 2;\n\n      if (Math.abs(finalRx - finalRy) < 0.01) {\n        return `circle(${finalRx}px at ${cx}px ${cy}px)`;\n      }\n      return `ellipse(${finalRx}px ${finalRy}px at ${cx}px ${cy}px)`;\n    }\n\n    // Otherwise serialize as polygon\n    const points = nonClose.map(s => `${s.point.x}px ${s.point.y}px`).join(', ');\n    return `polygon(${points})`;\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/CssOffsetPathGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\n/**\n * Reads and writes CSS `offset-path` (formerly `motion-path`) property geometry.\n * Supports: path(), polygon(), circle(), ellipse()\n */\nexport class CssOffsetPathGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const style = getComputedStyle(element);\n    const raw = style.offsetPath || element.getAttribute('style')?.match(/offset-path\\s*:\\s*([^;]+)/)?.[1] || '';\n    return this._parse(raw);\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const value = this._serializeGeometry(geometry);\n    return [{ attribute: 'style:offsetPath', value }];\n  }\n\n  private _parse(raw: string): IGeometry {\n    const trimmed = raw.trim();\n\n    if (trimmed.startsWith('polygon(')) {\n      return this._parsePolygon(trimmed);\n    }\n    if (trimmed.startsWith('circle(')) {\n      return this._parseCircle(trimmed);\n    }\n    if (trimmed.startsWith('ellipse(')) {\n      return this._parseEllipse(trimmed);\n    }\n    if (trimmed.startsWith('path(')) {\n      return this._parsePath(trimmed);\n    }\n\n    return { segments: [], closed: false };\n  }\n\n  private _parsePolygon(raw: string): IGeometry {\n    const inner = raw.slice(8, -1).trim();\n    const segments: IGeometrySegment[] = [];\n    const pairs = inner.split(',');\n    for (let i = 0; i < pairs.length; i++) {\n      const parts = pairs[i].trim().split(/\\s+/);\n      if (parts.length >= 2) {\n        const x = parseFloat(parts[0]);\n        const y = parseFloat(parts[1]);\n        if (!isNaN(x) && !isNaN(y)) {\n          segments.push({\n            type: i === 0 ? SegmentType.Move : SegmentType.Line,\n            relative: false,\n            point: { x, y },\n          });\n        }\n      }\n    }\n    if (segments.length > 0) {\n      segments.push({ type: SegmentType.Close, relative: false, point: { ...segments[0].point } });\n    }\n    return { segments, closed: true };\n  }\n\n  private _parseCircle(raw: string): IGeometry {\n    const inner = raw.slice(7, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let r = 50, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      r = parseFloat(inner.substring(0, atIdx));\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    } else {\n      r = parseFloat(inner) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - r } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - r } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _parseEllipse(raw: string): IGeometry {\n    const inner = raw.slice(8, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let rx = 50, ry = 25, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      const radii = inner.substring(0, atIdx).trim().split(/\\s+/);\n      rx = parseFloat(radii[0]) || 50;\n      ry = parseFloat(radii[1]) || 25;\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - ry } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - ry } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _parsePath(raw: string): IGeometry {\n    const match = raw.match(/path\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/);\n    if (!match) return { segments: [], closed: false };\n    const tempNs = 'http://www.w3.org/2000/svg';\n    const tempSvg = document.createElementNS(tempNs, 'svg');\n    const tempPath = document.createElementNS(tempNs, 'path');\n    tempPath.setAttribute('d', match[1]);\n    tempSvg.appendChild(tempPath);\n    tempSvg.style.position = 'absolute';\n    tempSvg.style.width = '0';\n    tempSvg.style.height = '0';\n    tempSvg.style.overflow = 'hidden';\n    document.body.appendChild(tempSvg);\n    try {\n      const { SvgPathGeometryReader } = require('./SvgPathGeometryReader.js');\n      const reader = new SvgPathGeometryReader();\n      return reader.read(tempPath);\n    } catch {\n      return { segments: [], closed: false };\n    } finally {\n      document.body.removeChild(tempSvg);\n    }\n  }\n\n  private _serializeGeometry(geometry: IGeometry): string {\n    const nonClose = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (nonClose.length === 0) return 'none';\n\n    const arcs = nonClose.filter(s => s.type === SegmentType.Arc);\n    if (arcs.length >= 3 && arcs[0].arc) {\n      const pts = nonClose;\n      const top = pts[0].point;\n      const right = pts[1].point;\n      const bottom = pts[2].point;\n      const left = pts[3].point;\n      const cx = (right.x + left.x) / 2;\n      const cy = (top.y + bottom.y) / 2;\n      const finalRx = Math.abs(right.x - left.x) / 2;\n      const finalRy = Math.abs(bottom.y - top.y) / 2;\n\n      if (Math.abs(finalRx - finalRy) < 0.01) {\n        return `circle(${finalRx}px at ${cx}px ${cy}px)`;\n      }\n      return `ellipse(${finalRx}px ${finalRy}px at ${cx}px ${cy}px)`;\n    }\n\n    const points = nonClose.map(s => `${s.point.x}px ${s.point.y}px`).join(', ');\n    return `polygon(${points})`;\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/CssShapeOutsideGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\n/**\n * Reads and writes CSS `shape-outside` property geometry.\n * Supports: polygon(), circle(), ellipse()\n */\nexport class CssShapeOutsideGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const style = getComputedStyle(element);\n    const raw = style.shapeOutside || element.getAttribute('style')?.match(/shape-outside\\s*:\\s*([^;]+)/)?.[1] || '';\n    return this._parse(raw);\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const value = this._serializeGeometry(geometry);\n    return [{ attribute: 'style:shapeOutside', value }];\n  }\n\n  private _parse(raw: string): IGeometry {\n    const trimmed = raw.trim();\n\n    if (trimmed.startsWith('polygon(')) {\n      return this._parsePolygon(trimmed);\n    }\n    if (trimmed.startsWith('circle(')) {\n      return this._parseCircle(trimmed);\n    }\n    if (trimmed.startsWith('ellipse(')) {\n      return this._parseEllipse(trimmed);\n    }\n\n    return { segments: [], closed: false };\n  }\n\n  private _parsePolygon(raw: string): IGeometry {\n    const inner = raw.slice(8, -1).trim();\n    const segments: IGeometrySegment[] = [];\n    const pairs = inner.split(',');\n    for (let i = 0; i < pairs.length; i++) {\n      const parts = pairs[i].trim().split(/\\s+/);\n      if (parts.length >= 2) {\n        const x = parseFloat(parts[0]);\n        const y = parseFloat(parts[1]);\n        if (!isNaN(x) && !isNaN(y)) {\n          segments.push({\n            type: i === 0 ? SegmentType.Move : SegmentType.Line,\n            relative: false,\n            point: { x, y },\n          });\n        }\n      }\n    }\n    if (segments.length > 0) {\n      segments.push({ type: SegmentType.Close, relative: false, point: { ...segments[0].point } });\n    }\n    return { segments, closed: true };\n  }\n\n  private _parseCircle(raw: string): IGeometry {\n    const inner = raw.slice(7, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let r = 50, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      r = parseFloat(inner.substring(0, atIdx));\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    } else {\n      r = parseFloat(inner) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - r } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - r } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _parseEllipse(raw: string): IGeometry {\n    const inner = raw.slice(8, -1).trim();\n    const atIdx = inner.indexOf(' at ');\n    let rx = 50, ry = 25, cx = 50, cy = 50;\n    if (atIdx >= 0) {\n      const radii = inner.substring(0, atIdx).trim().split(/\\s+/);\n      rx = parseFloat(radii[0]) || 50;\n      ry = parseFloat(radii[1]) || 25;\n      const pos = inner.substring(atIdx + 4).trim().split(/\\s+/);\n      cx = parseFloat(pos[0]) || 50;\n      cy = parseFloat(pos[1]) || 50;\n    }\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - ry } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - ry } },\n    ];\n    return { segments, closed: true };\n  }\n\n  private _serializeGeometry(geometry: IGeometry): string {\n    const nonClose = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (nonClose.length === 0) return 'none';\n\n    const arcs = nonClose.filter(s => s.type === SegmentType.Arc);\n    if (arcs.length >= 3 && arcs[0].arc) {\n      const pts = nonClose;\n      const top = pts[0].point;\n      const right = pts[1].point;\n      const bottom = pts[2].point;\n      const left = pts[3].point;\n      const cx = (right.x + left.x) / 2;\n      const cy = (top.y + bottom.y) / 2;\n      const finalRx = Math.abs(right.x - left.x) / 2;\n      const finalRy = Math.abs(bottom.y - top.y) / 2;\n\n      if (Math.abs(finalRx - finalRy) < 0.01) {\n        return `circle(${finalRx}px at ${cx}px ${cy}px)`;\n      }\n      return `ellipse(${finalRx}px ${finalRy}px at ${cx}px ${cy}px)`;\n    }\n\n    const points = nonClose.map(s => `${s.point.x}px ${s.point.y}px`).join(', ');\n    return `polygon(${points})`;\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/GeometryReaderFactory.ts",
    "content": "import { IGeometryReader } from './IGeometry.js';\nimport { SvgPathGeometryReader } from './SvgPathGeometryReader.js';\nimport { SvgRectGeometryReader } from './SvgRectGeometryReader.js';\nimport { SvgLineGeometryReader } from './SvgLineGeometryReader.js';\nimport { SvgEllipseGeometryReader } from './SvgEllipseGeometryReader.js';\nimport { SvgCircleGeometryReader } from './SvgCircleGeometryReader.js';\nimport { SvgPolygonGeometryReader } from './SvgPolygonGeometryReader.js';\nimport { SvgPolylineGeometryReader } from './SvgPolylineGeometryReader.js';\n\nconst readerMap = new Map<Function, IGeometryReader>();\n\nexport function getGeometryReader(element: Element): IGeometryReader | undefined {\n  if (element instanceof SVGPathElement) {\n    if (!readerMap.has(SVGPathElement)) readerMap.set(SVGPathElement, new SvgPathGeometryReader());\n    return readerMap.get(SVGPathElement);\n  }\n  if (element instanceof SVGRectElement) {\n    if (!readerMap.has(SVGRectElement)) readerMap.set(SVGRectElement, new SvgRectGeometryReader());\n    return readerMap.get(SVGRectElement);\n  }\n  if (element instanceof SVGLineElement) {\n    if (!readerMap.has(SVGLineElement)) readerMap.set(SVGLineElement, new SvgLineGeometryReader());\n    return readerMap.get(SVGLineElement);\n  }\n  if (element instanceof SVGEllipseElement) {\n    if (!readerMap.has(SVGEllipseElement)) readerMap.set(SVGEllipseElement, new SvgEllipseGeometryReader());\n    return readerMap.get(SVGEllipseElement);\n  }\n  if (element instanceof SVGCircleElement) {\n    if (!readerMap.has(SVGCircleElement)) readerMap.set(SVGCircleElement, new SvgCircleGeometryReader());\n    return readerMap.get(SVGCircleElement);\n  }\n  if (element instanceof SVGPolygonElement) {\n    if (!readerMap.has(SVGPolygonElement)) readerMap.set(SVGPolygonElement, new SvgPolygonGeometryReader());\n    return readerMap.get(SVGPolygonElement);\n  }\n  if (element instanceof SVGPolylineElement) {\n    if (!readerMap.has(SVGPolylineElement)) readerMap.set(SVGPolylineElement, new SvgPolylineGeometryReader());\n    return readerMap.get(SVGPolylineElement);\n  }\n  return undefined;\n}\n\nconst customReaders = new Map<string, IGeometryReader>();\n\nexport function registerGeometryReader(key: string, reader: IGeometryReader) {\n  customReaders.set(key, reader);\n}\n\nexport function getCustomGeometryReader(key: string): IGeometryReader | null {\n  return customReaders.get(key) ?? null;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/GeometryWriteHelper.ts",
    "content": "import { IDesignItem } from '../../../../../item/IDesignItem.js';\nimport { IGeometryWrite } from './IGeometry.js';\n\nconst numberPattern = /-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?/gi;\n\nfunction hasStyleTarget(element: Element): element is Element & { style: CSSStyleDeclaration } {\n  return 'style' in element;\n}\n\nexport function roundValueToDecimalPlaces(value: number, decimalPlaces: number): string {\n  if (decimalPlaces >= 0) {\n    return value.toFixed(decimalPlaces);\n  }\n  return value.toString();\n}\n\nexport function roundNumericParts(value: string, decimalPlaces: number): string {\n  if (decimalPlaces < 0) {\n    return value;\n  }\n\n  return value.replace(numberPattern, numberText => roundValueToDecimalPlaces(Number(numberText), decimalPlaces));\n}\n\nexport function roundGeometryWrites(writes: IGeometryWrite[], decimalPlaces: number): IGeometryWrite[] {\n  if (decimalPlaces < 0) {\n    return writes;\n  }\n\n  return writes.map(write => ({\n    ...write,\n    value: roundNumericParts(write.value, decimalPlaces)\n  }));\n}\n\nexport function applyGeometryWritesToElement(element: Element, writes: IGeometryWrite[], decimalPlaces = -1) {\n  for (const write of roundGeometryWrites(writes, decimalPlaces)) {\n    if (write.target === 'style' && hasStyleTarget(element)) {\n      element.style.setProperty(write.attribute, write.value);\n    } else {\n      element.setAttribute(write.attribute, write.value);\n    }\n  }\n}\n\nexport function applyGeometryWritesToDesignItem(designItem: IDesignItem, writes: IGeometryWrite[]) {\n  const roundedWrites = roundGeometryWrites(writes, designItem.serviceContainer.options.roundPixelsToDecimalPlaces);\n  for (const write of roundedWrites) {\n    if (write.target === 'style') {\n      designItem.setStyle(write.attribute, write.value);\n    } else {\n      designItem.setAttribute(write.attribute, write.value);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/IGeometry.ts",
    "content": "import { IPoint } from '../../../../../../interfaces/IPoint.js';\n\n/**\n * Segment types supported by the geometry model.\n */\nexport enum SegmentType {\n  Move = 'M',\n  Line = 'L',\n  CubicBezier = 'C',\n  QuadraticBezier = 'Q',\n  Arc = 'A',\n  Close = 'Z',\n  HorizontalLine = 'H',\n  VerticalLine = 'V',\n  SmoothCubic = 'S',\n  SmoothQuadratic = 'T',\n}\n\n/**\n * A single segment in the geometry. All coordinates are stored as absolute values for internal editing.\n * The `relative` flag preserves whether the original command was relative.\n */\nexport interface IGeometrySegment {\n  /** The segment type */\n  type: SegmentType;\n  /** Whether the original command was relative (lowercase letter in SVG path) */\n  relative: boolean;\n  /** The endpoint of this segment (absolute coordinates) */\n  point: IPoint;\n  /** For cubic bezier: first control point (absolute) */\n  cp1?: IPoint;\n  /** For cubic bezier: second control point (absolute). For quadratic bezier: the single control point. */\n  cp2?: IPoint;\n  /** For arc segments */\n  arc?: {\n    rx: number;\n    ry: number;\n    rotation: number;\n    largeArc: boolean;\n    sweep: boolean;\n  };\n}\n\nexport type GeometryWriteTarget = 'attribute' | 'style';\n\nexport interface IGeometryWrite {\n  attribute: string;\n  value: string;\n  target?: GeometryWriteTarget;\n}\n\nexport interface IGeometrySerializationHint {\n  target: GeometryWriteTarget;\n  unit?: string;\n}\n\n/**\n * Abstraction over any editable geometry (SVG shapes, CSS shapes, etc.)\n */\nexport interface IGeometry {\n  /** The segments describing this geometry */\n  segments: IGeometrySegment[];\n  /** Whether this geometry represents a closed shape */\n  closed: boolean;\n  /** Optional write-back hints for readers that preserve style-vs-attribute storage */\n  serializationHints?: Record<string, IGeometrySerializationHint>;\n}\n\n/**\n * Reads element attributes/properties into an IGeometry\n */\nexport interface IGeometryReader {\n  /** Read the element's current geometry */\n  read(element: Element): IGeometry;\n  /** Write the geometry back to the element as attribute string */\n  serialize(geometry: IGeometry): IGeometryWrite[];\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgCircleGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\nexport class SvgCircleGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const el = element as SVGCircleElement;\n    const cx = el.cx.baseVal.value;\n    const cy = el.cy.baseVal.value;\n    const r = el.r.baseVal.value;\n\n    // Same as ellipse but rx == ry == r\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - r } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - r, y: cy }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - r }, arc: { rx: r, ry: r, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - r } },\n    ];\n\n    return { segments, closed: true };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (pts.length < 4) return [];\n\n    const top = pts[0].point;\n    const right = pts[1].point;\n    const bottom = pts[2].point;\n    const left = pts[3].point;\n\n    const cx = (right.x + left.x) / 2;\n    const cy = (top.y + bottom.y) / 2;\n    const r = Math.max(Math.abs(right.x - left.x) / 2, Math.abs(bottom.y - top.y) / 2);\n\n    return [\n      { attribute: 'cx', value: cx.toString() },\n      { attribute: 'cy', value: cy.toString() },\n      { attribute: 'r', value: r.toString() },\n    ];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgEllipseGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\nexport class SvgEllipseGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const el = element as SVGEllipseElement;\n    const cx = el.cx.baseVal.value;\n    const cy = el.cy.baseVal.value;\n    const rx = el.rx.baseVal.value;\n    const ry = el.ry.baseVal.value;\n\n    // Represent ellipse as 4 cardinal points (top, right, bottom, left)\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: cx, y: cy - ry } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx + rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy + ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx - rx, y: cy }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Arc, relative: false, point: { x: cx, y: cy - ry }, arc: { rx, ry, rotation: 0, largeArc: false, sweep: true } },\n      { type: SegmentType.Close, relative: false, point: { x: cx, y: cy - ry } },\n    ];\n\n    return { segments, closed: true };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    // Reconstruct cx, cy, rx, ry from the cardinal points\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (pts.length < 4) return [];\n\n    // top=0, right=1, bottom=2, left=3\n    const top = pts[0].point;\n    const right = pts[1].point;\n    const bottom = pts[2].point;\n    const left = pts[3].point;\n\n    const cx = (right.x + left.x) / 2;\n    const cy = (top.y + bottom.y) / 2;\n    const rx = Math.abs(right.x - left.x) / 2;\n    const ry = Math.abs(bottom.y - top.y) / 2;\n\n    return [\n      { attribute: 'cx', value: cx.toString() },\n      { attribute: 'cy', value: cy.toString() },\n      { attribute: 'rx', value: rx.toString() },\n      { attribute: 'ry', value: ry.toString() },\n    ];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgLineGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\nexport class SvgLineGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const line = element as SVGLineElement;\n    const x1 = line.x1.baseVal.value;\n    const y1 = line.y1.baseVal.value;\n    const x2 = line.x2.baseVal.value;\n    const y2 = line.y2.baseVal.value;\n\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x: x1, y: y1 } },\n      { type: SegmentType.Line, relative: false, point: { x: x2, y: y2 } },\n    ];\n\n    return { segments, closed: false };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (pts.length < 2) return [];\n    return [\n      { attribute: 'x1', value: pts[0].point.x.toString() },\n      { attribute: 'y1', value: pts[0].point.y.toString() },\n      { attribute: 'x2', value: pts[1].point.x.toString() },\n      { attribute: 'y2', value: pts[1].point.y.toString() },\n    ];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgPathGeometryReader.ts",
    "content": "import { IPoint } from '../../../../../../interfaces/IPoint.js';\nimport { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\nimport \"../../../../../helper/PathDataPolyfill.js\";\nimport { PathData, createPathD } from '../../../../../helper/PathDataPolyfill.js';\n\nexport class SvgPathGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const pathEl = element as SVGPathElement;\n    const pathData: PathData[] = pathEl.getPathData({ normalize: false });\n    const segments: IGeometrySegment[] = [];\n    let lastPos: IPoint = { x: 0, y: 0 };\n    let firstPos: IPoint = { x: 0, y: 0 };\n    let closed = false;\n\n    for (const cmd of pathData) {\n      const rel = cmd.type === cmd.type.toLowerCase();\n      const t = cmd.type.toUpperCase();\n\n      switch (t) {\n        case 'M': {\n          const x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          segments.push({ type: SegmentType.Move, relative: rel, point: { x, y } });\n          lastPos = { x, y };\n          firstPos = { x, y };\n          break;\n        }\n        case 'L': {\n          const x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          segments.push({ type: SegmentType.Line, relative: rel, point: { x, y } });\n          lastPos = { x, y };\n          break;\n        }\n        case 'H': {\n          const x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          segments.push({ type: SegmentType.HorizontalLine, relative: rel, point: { x, y: lastPos.y } });\n          lastPos = { x, y: lastPos.y };\n          break;\n        }\n        case 'V': {\n          const y = rel ? lastPos.y + cmd.values[0] : cmd.values[0];\n          segments.push({ type: SegmentType.VerticalLine, relative: rel, point: { x: lastPos.x, y } });\n          lastPos = { x: lastPos.x, y };\n          break;\n        }\n        case 'C': {\n          const cp1x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const cp1y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          const cp2x = rel ? lastPos.x + cmd.values[2] : cmd.values[2];\n          const cp2y = rel ? lastPos.y + cmd.values[3] : cmd.values[3];\n          const x = rel ? lastPos.x + cmd.values[4] : cmd.values[4];\n          const y = rel ? lastPos.y + cmd.values[5] : cmd.values[5];\n          segments.push({\n            type: SegmentType.CubicBezier, relative: rel,\n            point: { x, y }, cp1: { x: cp1x, y: cp1y }, cp2: { x: cp2x, y: cp2y }\n          });\n          lastPos = { x, y };\n          break;\n        }\n        case 'S': {\n          const cp2x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const cp2y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          const x = rel ? lastPos.x + cmd.values[2] : cmd.values[2];\n          const y = rel ? lastPos.y + cmd.values[3] : cmd.values[3];\n          segments.push({\n            type: SegmentType.SmoothCubic, relative: rel,\n            point: { x, y }, cp2: { x: cp2x, y: cp2y }\n          });\n          lastPos = { x, y };\n          break;\n        }\n        case 'Q': {\n          const cp1x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const cp1y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          const x = rel ? lastPos.x + cmd.values[2] : cmd.values[2];\n          const y = rel ? lastPos.y + cmd.values[3] : cmd.values[3];\n          segments.push({\n            type: SegmentType.QuadraticBezier, relative: rel,\n            point: { x, y }, cp1: { x: cp1x, y: cp1y }\n          });\n          lastPos = { x, y };\n          break;\n        }\n        case 'T': {\n          const x = rel ? lastPos.x + cmd.values[0] : cmd.values[0];\n          const y = rel ? lastPos.y + cmd.values[1] : cmd.values[1];\n          segments.push({ type: SegmentType.SmoothQuadratic, relative: rel, point: { x, y } });\n          lastPos = { x, y };\n          break;\n        }\n        case 'A': {\n          const x = rel ? lastPos.x + cmd.values[5] : cmd.values[5];\n          const y = rel ? lastPos.y + cmd.values[6] : cmd.values[6];\n          segments.push({\n            type: SegmentType.Arc, relative: rel,\n            point: { x, y },\n            arc: {\n              rx: cmd.values[0], ry: cmd.values[1],\n              rotation: cmd.values[2],\n              largeArc: cmd.values[3] === 1,\n              sweep: cmd.values[4] === 1\n            }\n          });\n          lastPos = { x, y };\n          break;\n        }\n        case 'Z': {\n          closed = true;\n          segments.push({ type: SegmentType.Close, relative: rel, point: { ...firstPos } });\n          lastPos = { ...firstPos };\n          break;\n        }\n      }\n    }\n\n    return { segments, closed };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const pathData: PathData[] = [];\n    let lastPos: IPoint = { x: 0, y: 0 };\n\n    for (const seg of geometry.segments) {\n      const rel = seg.relative;\n\n      switch (seg.type) {\n        case SegmentType.Move: {\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 'm' : 'M', values: [x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.Line: {\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 'l' : 'L', values: [x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.HorizontalLine: {\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          pathData.push({ type: rel ? 'h' : 'H', values: [x] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.VerticalLine: {\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 'v' : 'V', values: [y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.CubicBezier: {\n          const cp1x = rel ? seg.cp1.x - lastPos.x : seg.cp1.x;\n          const cp1y = rel ? seg.cp1.y - lastPos.y : seg.cp1.y;\n          const cp2x = rel ? seg.cp2.x - lastPos.x : seg.cp2.x;\n          const cp2y = rel ? seg.cp2.y - lastPos.y : seg.cp2.y;\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 'c' : 'C', values: [cp1x, cp1y, cp2x, cp2y, x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.SmoothCubic: {\n          const cp2x = rel ? seg.cp2.x - lastPos.x : seg.cp2.x;\n          const cp2y = rel ? seg.cp2.y - lastPos.y : seg.cp2.y;\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 's' : 'S', values: [cp2x, cp2y, x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.QuadraticBezier: {\n          const cp1x = rel ? seg.cp1.x - lastPos.x : seg.cp1.x;\n          const cp1y = rel ? seg.cp1.y - lastPos.y : seg.cp1.y;\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 'q' : 'Q', values: [cp1x, cp1y, x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.SmoothQuadratic: {\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({ type: rel ? 't' : 'T', values: [x, y] } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.Arc: {\n          const x = rel ? seg.point.x - lastPos.x : seg.point.x;\n          const y = rel ? seg.point.y - lastPos.y : seg.point.y;\n          pathData.push({\n            type: rel ? 'a' : 'A',\n            values: [\n              seg.arc.rx, seg.arc.ry, seg.arc.rotation,\n              seg.arc.largeArc ? 1 : 0, seg.arc.sweep ? 1 : 0,\n              x, y\n            ]\n          } as any);\n          lastPos = { ...seg.point };\n          break;\n        }\n        case SegmentType.Close: {\n          pathData.push({ type: rel ? 'z' : 'Z', values: [] } as any);\n          break;\n        }\n      }\n    }\n\n    return [{ attribute: 'd', value: createPathD(pathData) }];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgPolygonGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\nexport class SvgPolygonGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const poly = element as SVGPolygonElement;\n    const points = poly.points;\n    const segments: IGeometrySegment[] = [];\n\n    for (let i = 0; i < points.numberOfItems; i++) {\n      const pt = points.getItem(i);\n      if (i === 0) {\n        segments.push({ type: SegmentType.Move, relative: false, point: { x: pt.x, y: pt.y } });\n      } else {\n        segments.push({ type: SegmentType.Line, relative: false, point: { x: pt.x, y: pt.y } });\n      }\n    }\n\n    if (segments.length > 0) {\n      segments.push({ type: SegmentType.Close, relative: false, point: { ...segments[0].point } });\n    }\n\n    return { segments, closed: true };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    const pointsStr = pts.map(s => `${s.point.x},${s.point.y}`).join(' ');\n    return [{ attribute: 'points', value: pointsStr }];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgPolylineGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, SegmentType } from './IGeometry.js';\n\nexport class SvgPolylineGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const poly = element as SVGPolylineElement;\n    const points = poly.points;\n    const segments: IGeometrySegment[] = [];\n\n    for (let i = 0; i < points.numberOfItems; i++) {\n      const pt = points.getItem(i);\n      if (i === 0) {\n        segments.push({ type: SegmentType.Move, relative: false, point: { x: pt.x, y: pt.y } });\n      } else {\n        segments.push({ type: SegmentType.Line, relative: false, point: { x: pt.x, y: pt.y } });\n      }\n    }\n\n    return { segments, closed: false };\n  }\n\n  serialize(geometry: IGeometry): { attribute: string; value: string }[] {\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    const pointsStr = pts.map(s => `${s.point.x},${s.point.y}`).join(' ');\n    return [{ attribute: 'points', value: pointsStr }];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/svg/geometry/SvgRectGeometryReader.ts",
    "content": "import { IGeometry, IGeometryReader, IGeometrySegment, IGeometrySerializationHint, IGeometryWrite, SegmentType } from './IGeometry.js';\n\nconst rectGeometryProperties = ['x', 'y', 'width', 'height'] as const;\ntype RectGeometryProperty = typeof rectGeometryProperties[number];\n\nfunction getRectSerializationHint(rect: SVGRectElement, property: RectGeometryProperty): IGeometrySerializationHint {\n  const styleValue = rect.style.getPropertyValue(property).trim();\n  if (styleValue) {\n    return { target: 'style', unit: extractStyleUnit(styleValue) };\n  }\n  return { target: 'attribute' };\n}\n\nfunction extractStyleUnit(value: string): string {\n  const match = value.trim().match(/^-?(?:\\d+|\\d*\\.\\d+)([a-z%]*)$/i);\n  if (match && match[1]) {\n    return match[1];\n  }\n  return 'px';\n}\n\nfunction serializeRectValue(property: RectGeometryProperty, value: number, hints?: IGeometry['serializationHints']): IGeometryWrite {\n  const hint = hints?.[property];\n  if (hint?.target === 'style') {\n    return {\n      attribute: property,\n      value: `${value}${hint.unit ?? 'px'}`,\n      target: 'style'\n    };\n  }\n\n  return {\n    attribute: property,\n    value: value.toString()\n  };\n}\n\nexport class SvgRectGeometryReader implements IGeometryReader {\n\n  read(element: Element): IGeometry {\n    const rect = element as SVGRectElement;\n    const bbox = rect.getBBox();\n    const x = bbox.x;\n    const y = bbox.y;\n    const w = bbox.width;\n    const h = bbox.height;\n\n    const segments: IGeometrySegment[] = [\n      { type: SegmentType.Move, relative: false, point: { x, y } },\n      { type: SegmentType.Line, relative: false, point: { x: x + w, y } },\n      { type: SegmentType.Line, relative: false, point: { x: x + w, y: y + h } },\n      { type: SegmentType.Line, relative: false, point: { x, y: y + h } },\n      { type: SegmentType.Close, relative: false, point: { x, y } },\n    ];\n\n    const serializationHints = Object.fromEntries(\n      rectGeometryProperties.map(property => [property, getRectSerializationHint(rect, property)])\n    );\n\n    return { segments, closed: true, serializationHints };\n  }\n\n  serialize(geometry: IGeometry): IGeometryWrite[] {\n    const pts = geometry.segments.filter(s => s.type !== SegmentType.Close);\n    if (pts.length < 2) return [];\n\n    const xs = pts.map(p => p.point.x);\n    const ys = pts.map(p => p.point.y);\n    const minX = Math.min(...xs);\n    const minY = Math.min(...ys);\n    const maxX = Math.max(...xs);\n    const maxY = Math.max(...ys);\n\n    return [\n      serializeRectValue('x', minX, geometry.serializationHints),\n      serializeRectValue('y', minY, geometry.serializationHints),\n      serializeRectValue('width', maxX - minX, geometry.serializationHints),\n      serializeRectValue('height', maxY - minY, geometry.serializationHints),\n    ];\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/ProjectiveTransformExtension.ts",
    "content": "import { EventNames } from '../../../../../enums/EventNames.js';\nimport { IPoint } from '../../../../../interfaces/IPoint.js';\nimport { getElementSize } from '../../../../helper/getBoxQuads.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { AbstractExtension } from '../AbstractExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\n\ntype QuadCornerIndex = 0 | 1 | 2 | 3;\n\nexport class ProjectiveTransformExtension extends AbstractExtension {\n  private _outline?: SVGPathElement;\n  private _handles: SVGCircleElement[] = [];\n  private _handleCrossHorizontalLines: SVGLineElement[] = [];\n  private _handleCrossVerticalLines: SVGLineElement[] = [];\n  private _activeHandleIndex: QuadCornerIndex | null = null;\n  private _baseTransform = '';\n  private _projectiveTransform = '';\n  private _targetPoints: IPoint[] = [];\n\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n  }\n\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\n    this.refresh(cache, event);\n  }\n\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\n    const transformedCornerPoints = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\n    if (!transformedCornerPoints) {\n      return;\n    }\n\n    const points = [transformedCornerPoints.p1, transformedCornerPoints.p2, transformedCornerPoints.p3, transformedCornerPoints.p4];\n    if (points.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y))) {\n      this.remove();\n      return;\n    }\n\n    if (this._valuesHaveChanges(this.designerCanvas.zoomFactor, ...points.flatMap(point => [point.x, point.y]))) {\n      this._outline = this._drawTransformedRect(transformedCornerPoints, 'svg-primary-projective-outline', this._outline);\n      this._outline.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n      this._outline.style.pointerEvents = 'none';\n\n      const radius = 5 / this.designerCanvas.zoomFactor;\n      const crossRadius = radius * 0.55;\n      for (let index = 0; index < points.length; index++) {\n        const cornerIndex = index as QuadCornerIndex;\n        const point = points[index];\n        let handle = this._handles[index];\n        if (!handle) {\n          handle = this._drawCircle(point.x, point.y, radius, 'svg-primary-projective-handle');\n          handle.addEventListener(EventNames.PointerDown, event => this._handlePointerEvent(cornerIndex, event));\n          handle.addEventListener(EventNames.PointerMove, event => this._handlePointerEvent(cornerIndex, event));\n          handle.addEventListener(EventNames.PointerUp, event => this._handlePointerEvent(cornerIndex, event));\n          this._handles[index] = handle;\n        } else {\n          handle = this._drawCircle(point.x, point.y, radius, 'svg-primary-projective-handle', handle);\n          this._handles[index] = handle;\n        }\n        handle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n        handle.style.cursor = 'move';\n\n        let horizontalLine = this._handleCrossHorizontalLines[index];\n        horizontalLine = this._drawLine(point.x - crossRadius, point.y, point.x + crossRadius, point.y, 'svg-primary-projective-handle-cross', horizontalLine);\n        horizontalLine.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n        horizontalLine.style.pointerEvents = 'none';\n        this._handleCrossHorizontalLines[index] = horizontalLine;\n\n        let verticalLine = this._handleCrossVerticalLines[index];\n        verticalLine = this._drawLine(point.x, point.y - crossRadius, point.x, point.y + crossRadius, 'svg-primary-projective-handle-cross', verticalLine);\n        verticalLine.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n        verticalLine.style.pointerEvents = 'none';\n        this._handleCrossVerticalLines[index] = verticalLine;\n      }\n    }\n  }\n\n  override dispose() {\n    this._activeHandleIndex = null;\n    this._restorePersistedTransform();\n    this._removeAllOverlays();\n  }\n\n  private _handlePointerEvent(index: QuadCornerIndex, event: PointerEvent) {\n    event.stopPropagation();\n    const target = event.target as Element;\n\n    switch (event.type) {\n      case EventNames.PointerDown:\n        if (!this._startDrag(index)) {\n          return;\n        }\n        target.setPointerCapture(event.pointerId);\n        this._activeHandleIndex = index;\n        break;\n      case EventNames.PointerMove:\n        if (this._activeHandleIndex === index && event.buttons > 0) {\n          const localPoint = this._getLocalPointerPoint(event);\n          if (!localPoint) {\n            return;\n          }\n          this._targetPoints[index] = localPoint;\n          this._applyPreviewTransform();\n        }\n        break;\n      case EventNames.PointerUp:\n        if (this._activeHandleIndex === index) {\n          const localPoint = this._getLocalPointerPoint(event);\n          if (localPoint) {\n            this._targetPoints[index] = localPoint;\n            this._applyPreviewTransform();\n          }\n          target.releasePointerCapture(event.pointerId);\n          this._commitTransform();\n          this._activeHandleIndex = null;\n        }\n        break;\n    }\n  }\n\n  private _startDrag(index: QuadCornerIndex) {\n    const { baseTransform, projectiveTransform } = this._parseTransform(this.extendedItem.getStyleFromSheetOrLocal('transform') ?? '');\n    this._baseTransform = baseTransform;\n    this._projectiveTransform = projectiveTransform;\n\n    const currentQuad = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\n    if (!currentQuad) {\n      return false;\n    }\n\n    const element = this.extendedItem.element as HTMLElement;\n    element.style.transform = this._buildTransform(this._baseTransform, '');\n    this._targetPoints = this._convertQuadToLocalPoints(currentQuad);\n    if (this._targetPoints.length !== 4 || this._targetPoints.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y))) {\n      this._restorePersistedTransform();\n      return false;\n    }\n    element.style.transform = this._buildTransform(this._baseTransform, this._projectiveTransform);\n    return this._targetPoints[index] != null;\n  }\n\n  private _convertQuadToLocalPoints(quad: DOMQuad) {\n    return [quad.p1, quad.p2, quad.p3, quad.p4].map(point => {\n      const localPoint = this.extendedItem.element.convertPointFromNode({ x: point.x, y: point.y }, this.designerCanvas.canvas);\n      return { x: localPoint.x, y: localPoint.y };\n    });\n  }\n\n  private _getLocalPointerPoint(event: PointerEvent) {\n    const element = this.extendedItem.element as HTMLElement;\n    const previewTransform = element.style.transform;\n    element.style.transform = this._buildTransform(this._baseTransform, '');\n    const mousePoint = this.designerCanvas.getNormalizedEventCoordinates(event);\n    const localPoint = element.convertPointFromNode({ x: mousePoint.x, y: mousePoint.y }, this.designerCanvas.canvas);\n    element.style.transform = previewTransform;\n\n    if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y)) {\n      return null;\n    }\n\n    return { x: localPoint.x, y: localPoint.y };\n  }\n\n  private _applyPreviewTransform() {\n    const projectiveTransform = this._buildProjectiveTransform(this._targetPoints);\n    if (projectiveTransform == null) {\n      return;\n    }\n\n    this._projectiveTransform = projectiveTransform;\n    (this.extendedItem.element as HTMLElement).style.transform = this._buildTransform(this._baseTransform, this._projectiveTransform);\n  }\n\n  private _commitTransform() {\n    const projectiveTransform = this._buildProjectiveTransform(this._targetPoints);\n    if (projectiveTransform != null) {\n      this._projectiveTransform = projectiveTransform;\n    }\n\n    const finalTransform = this._buildTransform(this._baseTransform, this._projectiveTransform);\n    if (finalTransform) {\n      this.extendedItem.updateStyleInSheetOrLocal('transform', finalTransform);\n    } else if (this.extendedItem.hasStyle('transform')) {\n      this.extendedItem.removeStyle('transform');\n    } else {\n      this.extendedItem.updateStyleInSheetOrLocal('transform', null);\n    }\n\n    this._restorePersistedTransform();\n  }\n\n  private _restorePersistedTransform() {\n    const element = this.extendedItem.element as HTMLElement;\n    if (this.extendedItem.hasStyle('transform')) {\n      element.style.transform = this.extendedItem.getStyle('transform') ?? '';\n    } else {\n      element.style.transform = '';\n    }\n  }\n\n  private _buildProjectiveTransform(points: IPoint[]) {\n    const size = getElementSize(this.extendedItem.element);\n    if (!size.width || !size.height || points.length !== 4) {\n      return '';\n    }\n\n    const sourcePoints: IPoint[] = [\n      { x: 0, y: 0 },\n      { x: size.width, y: 0 },\n      { x: size.width, y: size.height },\n      { x: 0, y: size.height }\n    ];\n\n    if (this._pointsApproximatelyEqual(points, sourcePoints)) {\n      return '';\n    }\n\n    const matrix = this._createProjectiveMatrix(points, size.width, size.height);\n    if (!matrix) {\n      return null;\n    }\n\n    const transformOrigin = getComputedStyle(this.extendedItem.element).transformOrigin.split(' ');\n    const origin = {\n      x: parseFloat(transformOrigin[0]) || 0,\n      y: parseFloat(transformOrigin[1]) || 0\n    };\n    const correctedMatrix = new DOMMatrix()\n      .translate(-origin.x, -origin.y)\n      .multiply(matrix)\n      .multiply(new DOMMatrix().translate(origin.x, origin.y));\n\n    return this._serializeMatrix3d(correctedMatrix);\n  }\n\n  private _createProjectiveMatrix(points: IPoint[], width: number, height: number) {\n    const [p1, p2, p3, p4] = points;\n\n    const dx1 = p2.x - p3.x;\n    const dx2 = p4.x - p3.x;\n    const dx3 = p1.x - p2.x + p3.x - p4.x;\n    const dy1 = p2.y - p3.y;\n    const dy2 = p4.y - p3.y;\n    const dy3 = p1.y - p2.y + p3.y - p4.y;\n\n    let aUnit: number;\n    let bUnit: number;\n    let dUnit: number;\n    let eUnit: number;\n    let gUnit: number;\n    let hUnit: number;\n\n    if (Math.abs(dx3) < 1e-8 && Math.abs(dy3) < 1e-8) {\n      aUnit = p2.x - p1.x;\n      bUnit = p4.x - p1.x;\n      dUnit = p2.y - p1.y;\n      eUnit = p4.y - p1.y;\n      gUnit = 0;\n      hUnit = 0;\n    } else {\n      const determinant = dx1 * dy2 - dx2 * dy1;\n      if (Math.abs(determinant) < 1e-8) {\n        return null;\n      }\n\n      gUnit = (dx3 * dy2 - dx2 * dy3) / determinant;\n      hUnit = (dx1 * dy3 - dx3 * dy1) / determinant;\n      aUnit = p2.x - p1.x + gUnit * p2.x;\n      bUnit = p4.x - p1.x + hUnit * p4.x;\n      dUnit = p2.y - p1.y + gUnit * p2.y;\n      eUnit = p4.y - p1.y + hUnit * p4.y;\n    }\n\n    const matrix = new DOMMatrix([\n      aUnit / width, dUnit / width, 0, gUnit / width,\n      bUnit / height, eUnit / height, 0, hUnit / height,\n      0, 0, 1, 0,\n      p1.x, p1.y, 0, 1\n    ]);\n\n    const values = [\n      matrix.m11, matrix.m12, matrix.m13, matrix.m14,\n      matrix.m21, matrix.m22, matrix.m23, matrix.m24,\n      matrix.m31, matrix.m32, matrix.m33, matrix.m34,\n      matrix.m41, matrix.m42, matrix.m43, matrix.m44\n    ];\n    if (values.some(value => !Number.isFinite(value))) {\n      return null;\n    }\n\n    return matrix;\n  }\n\n  private _serializeMatrix3d(matrix: DOMMatrix) {\n    const values = [\n      matrix.m11, matrix.m12, matrix.m13, matrix.m14,\n      matrix.m21, matrix.m22, matrix.m23, matrix.m24,\n      matrix.m31, matrix.m32, matrix.m33, matrix.m34,\n      matrix.m41, matrix.m42, matrix.m43, matrix.m44\n    ].map(value => this._formatMatrixNumber(value));\n\n    return `matrix3d(${values.join(', ')})`;\n  }\n\n  private _formatMatrixNumber(value: number) {\n    if (Math.abs(value) < 1e-10) {\n      return '0';\n    }\n\n    let rounded = value.toFixed(8);\n    if (rounded.includes('.')) {\n      rounded = rounded.replace(/0+$/, '').replace(/\\.$/, '');\n    }\n    return rounded === '-0' ? '0' : rounded;\n  }\n\n  private _buildTransform(baseTransform: string, projectiveTransform: string) {\n    const transforms: string[] = [];\n    if (baseTransform?.trim()) {\n      transforms.push(baseTransform.trim());\n    }\n    if (projectiveTransform?.trim()) {\n      transforms.push(projectiveTransform.trim());\n    }\n    return transforms.join(' ').trim();\n  }\n\n  private _parseTransform(transform: string) {\n    if (!transform || transform === 'none') {\n      return { baseTransform: '', projectiveTransform: '' };\n    }\n\n    const parts = this._splitTransformFunctions(transform);\n    if (!parts.length) {\n      return { baseTransform: transform.trim(), projectiveTransform: '' };\n    }\n\n    const lastPart = parts[parts.length - 1];\n    const functionName = lastPart.slice(0, lastPart.indexOf('(')).trim().toLowerCase();\n    if (functionName === 'matrix' || functionName === 'matrix3d') {\n      return {\n        baseTransform: parts.slice(0, -1).join(' ').trim(),\n        projectiveTransform: lastPart.trim()\n      };\n    }\n\n    return { baseTransform: transform.trim(), projectiveTransform: '' };\n  }\n\n  private _splitTransformFunctions(transform: string) {\n    const parts: string[] = [];\n    let startIndex = -1;\n    let depth = 0;\n\n    for (let index = 0; index < transform.length; index++) {\n      const character = transform[index];\n      if (character === '(') {\n        depth++;\n      } else if (character === ')') {\n        depth--;\n        if (depth === 0 && startIndex !== -1) {\n          parts.push(transform.slice(startIndex, index + 1).trim());\n          startIndex = -1;\n        }\n      } else if (depth === 0 && startIndex === -1 && character.trim()) {\n        startIndex = index;\n      }\n    }\n\n    if (!parts.length && transform.trim()) {\n      parts.push(transform.trim());\n    }\n    return parts;\n  }\n\n  private _pointsApproximatelyEqual(pointsA: IPoint[], pointsB: IPoint[]) {\n    if (pointsA.length !== pointsB.length) {\n      return false;\n    }\n\n    return pointsA.every((point, index) => {\n      const compareTo = pointsB[index];\n      return Math.abs(point.x - compareTo.x) < 0.01 && Math.abs(point.y - compareTo.y) < 0.01;\n    });\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/ProjectiveTransformExtensionProvider.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { IDesignerExtension } from '../IDesignerExtension.js';\nimport { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { ProjectiveTransformExtension } from './ProjectiveTransformExtension.js';\n\nexport class ProjectiveTransformExtensionProvider implements IDesignerExtensionProvider {\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\n    if (designerCanvas.readOnly) {\n      return false;\n    }\n    if (designItem.element instanceof SVGElement || designItem.element instanceof HTMLTemplateElement) {\n      return false;\n    }\n    return !designItem.isRootItem;\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new ProjectiveTransformExtension(extensionManager, designerCanvas, designItem);\n  }\n\n  static readonly style = css`\n    .svg-primary-projective-handle { stroke: #3899ec; fill: white; stroke-width: 1; pointer-events: auto; }\n    .svg-primary-projective-handle-cross { stroke: #3899ec; fill: none; stroke-width: 1; stroke-linecap: round; pointer-events: none; }\n    .svg-primary-projective-outline { stroke: #3899ec; fill: none; stroke-width: 1; stroke-dasharray: 4 2; }\n  `;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/RotateExtension.ts",
    "content": "import { IPoint } from '../../../../../interfaces/IPoint.js';\r\nimport { hasCommandKey } from '../../../../helper/KeyboardHelper.js';\r\nimport { roundValue } from '../../../../helper/LayoutHelper.js';\r\nimport { getElementSize } from '../../../../helper/getBoxQuads.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension } from '../AbstractExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { getEdgeOffsetPoint, getQuadCenter } from '../../../../helper/QuadEdgeHandleHelper.js';\r\n\r\nexport class RotateExtension extends AbstractExtension {\r\n\r\n  private _rotateLine?: SVGLineElement;\r\n  private _rotateCircle?: SVGCircleElement;\r\n  private _startPoint: IPoint | null = null;\r\n  private _rotateCirclePosition: IPoint = { x: 0, y: 0 };\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    this.refresh(cache, event);\r\n  }\r\n\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const quad = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n    if (!quad) {\r\n      return;\r\n    }\r\n\r\n    const points = [quad.p1, quad.p2, quad.p3, quad.p4];\r\n    if (points.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y))) {\r\n      this.remove();\r\n      return;\r\n    }\r\n\r\n    if (!this._valuesHaveChanges(this.designerCanvas.zoomFactor, ...points.flatMap(point => [point.x, point.y]))) {\r\n      return;\r\n    }\r\n\r\n    const quadCenter = getQuadCenter(quad);\r\n    const handlePoint = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 30 / this.designerCanvas.zoomFactor, { x: 0, y: -1 });\r\n    const lineStart = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 22 / this.designerCanvas.zoomFactor, { x: 0, y: -1 });\r\n    const lineEnd = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 6 / this.designerCanvas.zoomFactor, { x: 0, y: -1 });\r\n\r\n    this._rotateLine = this._drawLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, 'svg-primary-rotate-line', this._rotateLine);\r\n    this._rotateLine.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n\r\n    if (!this._rotateCircle) {\r\n      this._rotateCircle = this._drawCircle(handlePoint.x, handlePoint.y, 5 / this.designerCanvas.zoomFactor, 'svg-primary-rotate', this._rotateCircle);\r\n      this._rotateCircle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n\r\n      this._rotateCircle.addEventListener(\"pointerdown\", e => {\r\n        e.stopPropagation();\r\n        this._rotateCirclePosition = this._getRotateReferencePoint();\r\n        (<Element>e.target).setPointerCapture(e.pointerId);\r\n        let mp = this.designerCanvas.getNormalizedEventCoordinates(e);\r\n        this._startPoint = this.extendedItem.element.convertPointFromNode({ x: mp.x, y: mp.y }, this.designerCanvas.canvas);\r\n      });\r\n      this._rotateCircle.addEventListener(\"pointermove\", e => {\r\n        e.stopPropagation();\r\n\r\n        if (this._startPoint) {\r\n          //@ts-ignore\r\n          this.extendedItem.element.style.rotate = 'none';\r\n          //@ts-ignore\r\n          this.extendedItem.element.style.rotate = this.getAngle(e) + 'deg';\r\n        }\r\n      });\r\n      this._rotateCircle.addEventListener(\"pointerup\", e => {\r\n        e.stopPropagation();\r\n        (<Element>e.target).releasePointerCapture(e.pointerId);\r\n        this._startPoint = null;\r\n        //@ts-ignore\r\n        this.extendedItem.element.style.rotate = 'none';\r\n        this.extendedItem.setStyle('rotate', this.getAngle(e) + 'deg')\r\n      });\r\n    } else {\r\n      this._rotateCircle = this._drawCircle(handlePoint.x, handlePoint.y, 5 / this.designerCanvas.zoomFactor, 'svg-primary-rotate', this._rotateCircle);\r\n      this._rotateCircle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n    }\r\n  }\r\n\r\n  private _getRotateReferencePoint() {\r\n    const element = this.extendedItem.element as HTMLElement;\r\n    const size = getElementSize(this.extendedItem.element);\r\n    const fallback = { x: size.width / 2, y: -30 / this.designerCanvas.zoomFactor };\r\n    const inlineRotate = element.style.rotate;\r\n\r\n    try {\r\n      element.style.rotate = 'none';\r\n      const quad = element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\r\n      if (!quad) {\r\n        return fallback;\r\n      }\r\n\r\n      const quadCenter = getQuadCenter(quad);\r\n      const handlePoint = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 30 / this.designerCanvas.zoomFactor, { x: 0, y: -1 });\r\n      const localPoint = element.convertPointFromNode(handlePoint, this.designerCanvas.canvas);\r\n      if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y)) {\r\n        return fallback;\r\n      }\r\n      return { x: localPoint.x, y: localPoint.y };\r\n    } finally {\r\n      element.style.rotate = inlineRotate;\r\n    }\r\n  }\r\n\r\n  getAngle(e: MouseEvent) {\r\n    let mp = this.designerCanvas.getNormalizedEventCoordinates(e);\r\n    let p1 = { x: mp.x, y: mp.y };\r\n    let p1t = this.extendedItem.element.convertPointFromNode(p1, this.designerCanvas.canvas);\r\n\r\n    const cs = getComputedStyle(this.extendedItem.element);\r\n    const orig = cs.transformOrigin.split(' ');\r\n    const pOrg = { x: parseFloat(orig[0]), y: parseFloat(orig[1]) };\r\n\r\n    const angleFromRotatingPoint = (Math.atan2(this._rotateCirclePosition.y - pOrg.y, this._rotateCirclePosition.x - pOrg.x) * 180 / Math.PI) + 90;\r\n    let angle = (Math.atan2(p1t.y - pOrg.y, p1t.x - pOrg.x) * 180 / Math.PI) + 90 - angleFromRotatingPoint;\r\n\r\n    if (!hasCommandKey(e))\r\n      angle = Math.round(angle / 15) * 15;\r\n    return roundValue(this.extendedItem, angle);\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}\r\n\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/RotateExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { RotateExtension } from './RotateExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\n\r\nexport class RotateExtensionProvider implements IDesignerExtensionProvider {\r\n  public type: 'center-top' | 'corners';\r\n\r\n  constructor(type: 'center-top' | 'corners' = 'center-top') {\r\n    this.type = type;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (designerCanvas.readOnly)\r\n      return false;\r\n    if (designItem.element instanceof SVGElement || designItem.element instanceof HTMLTemplateElement) {\r\n      return false;\r\n    }\r\n    return !designItem.isRootItem;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new RotateExtension(extensionManager, designerCanvas, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-primary-rotate { stroke: #3899ec; fill: white; stroke-width: 1; pointer-events: auto; cursor: alias; }\r\n    .svg-primary-rotate-transparent { fill: transparent; pointer-events: auto; cursor: alias; }\r\n    .svg-primary-rotate-line { stroke: #3899ec; fill: #3899ec; stroke-width: 1; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/RotateGroupExtension.ts",
    "content": "import { IPoint } from '../../../../../interfaces/IPoint.js';\nimport { hasCommandKey } from '../../../../helper/KeyboardHelper.js';\nimport { filterChildPlaceItems, roundValue } from '../../../../helper/LayoutHelper.js';\nimport { calculateOuterRect } from '../../../../helper/ElementHelper.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { AbstractExtension } from '../AbstractExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\n\ninterface ElementStartState {\n  item: IDesignItem;\n  rotation: number;\n  centerX: number;\n  centerY: number;\n  cssHalfWidth: number;   // half of CSS width (not visual bounding box — matters for pre-rotated elements)\n  cssHalfHeight: number;\n  parentX: number;\n  parentY: number;\n  parentWidth: number;\n  parentHeight: number;\n  usesRight: boolean;\n  usesBottom: boolean;\n}\n\nexport class RotateGroupExtension extends AbstractExtension {\n\n  private _rotateLine: SVGLineElement;\n  private _rotateCircle: SVGCircleElement;\n\n  // rotating rect overlay (only visible during drag)\n  private _rectLine1: SVGLineElement;\n  private _rectLine2: SVGLineElement;\n  private _rectLine3: SVGLineElement;\n  private _rectLine4: SVGLineElement;\n  private _initialCorners: IPoint[];\n\n  // drag-start state\n  private _startAngle: number;\n  private _groupCenter: IPoint;\n  private _initialHandleX: number;\n  private _initialHandleY: number;\n  private _initialLineX1: number;\n  private _initialLineY1: number;\n  private _initialLineX2: number;\n  private _initialLineY2: number;\n  private _currentDelta: number = 0;\n  private _elementStartStates: ElementStartState[];\n\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n  }\n\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\n    this.refresh(cache, event);\n  }\n\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\n    const selection = filterChildPlaceItems(\n      this.designerCanvas.instanceServiceContainer.selectionService.selectedElements\n    );\n    if (selection.length < 2) return;\n\n    let handleX: number, handleY: number;\n    let lineX1: number, lineY1: number, lineX2: number, lineY2: number;\n\n    if (this._elementStartStates) {\n      // During drag: rotate all control points around the fixed group center\n      const rad = this._currentDelta * Math.PI / 180;\n      const cos = Math.cos(rad);\n      const sin = Math.sin(rad);\n      const rx = (x: number, y: number) =>\n        this._groupCenter.x + (x - this._groupCenter.x) * cos - (y - this._groupCenter.y) * sin;\n      const ry = (x: number, y: number) =>\n        this._groupCenter.y + (x - this._groupCenter.x) * sin + (y - this._groupCenter.y) * cos;\n\n      handleX = rx(this._initialHandleX, this._initialHandleY);\n      handleY = ry(this._initialHandleX, this._initialHandleY);\n      lineX1 = rx(this._initialLineX1, this._initialLineY1);\n      lineY1 = ry(this._initialLineX1, this._initialLineY1);\n      lineX2 = rx(this._initialLineX2, this._initialLineY2);\n      lineY2 = ry(this._initialLineX2, this._initialLineY2);\n\n      // Rotate the four bounding-box corners and draw the rect overlay\n      const c = this._initialCorners;\n      const corners = c.map(p => ({ x: rx(p.x, p.y), y: ry(p.x, p.y) }));\n      const sw = (2 / this.designerCanvas.zoomFactor).toString();\n      this._rectLine1 = this._drawLine(corners[0].x, corners[0].y, corners[1].x, corners[1].y, 'svg-rotate-group-rect', this._rectLine1);\n      this._rectLine2 = this._drawLine(corners[1].x, corners[1].y, corners[2].x, corners[2].y, 'svg-rotate-group-rect', this._rectLine2);\n      this._rectLine3 = this._drawLine(corners[2].x, corners[2].y, corners[3].x, corners[3].y, 'svg-rotate-group-rect', this._rectLine3);\n      this._rectLine4 = this._drawLine(corners[3].x, corners[3].y, corners[0].x, corners[0].y, 'svg-rotate-group-rect', this._rectLine4);\n      this._rectLine1.style.strokeWidth = sw;\n      this._rectLine2.style.strokeWidth = sw;\n      this._rectLine3.style.strokeWidth = sw;\n      this._rectLine4.style.strokeWidth = sw;\n    } else {\n      if (this._rectLine1) {\n        this._rectLine1.style.display = 'none';\n        this._rectLine2.style.display = 'none';\n        this._rectLine3.style.display = 'none';\n        this._rectLine4.style.display = 'none';\n      }\n      // Idle: position above the bounding box center\n      const outerRect = calculateOuterRect(selection, this.designerCanvas);\n      const cx = outerRect.x + outerRect.width / 2;\n      const top = outerRect.y;\n\n      handleX = cx;\n      handleY = top - 30 / this.designerCanvas.zoomFactor;\n      lineX1 = cx;\n      lineY1 = top - 22 / this.designerCanvas.zoomFactor;\n      lineX2 = cx;\n      lineY2 = top - 6 / this.designerCanvas.zoomFactor;\n    }\n\n    this._rotateLine = this._drawLine(lineX1, lineY1, lineX2, lineY2, 'svg-primary-rotate-line', this._rotateLine);\n    this._rotateLine.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n\n    if (!this._rotateCircle) {\n      this._rotateCircle = this._drawCircle(handleX, handleY, 5 / this.designerCanvas.zoomFactor, 'svg-primary-rotate', this._rotateCircle);\n      this._rotateCircle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n\n      this._rotateCircle.addEventListener('pointerdown', e => {\n        e.stopPropagation();\n        (<Element>e.target).setPointerCapture(e.pointerId);\n\n        const currentSelection = filterChildPlaceItems(\n          this.designerCanvas.instanceServiceContainer.selectionService.selectedElements\n        );\n        const outerRect = calculateOuterRect(currentSelection, this.designerCanvas);\n        const cx = outerRect.x + outerRect.width / 2;\n        const top = outerRect.y;\n\n        this._groupCenter = {\n          x: cx,\n          y: outerRect.y + outerRect.height / 2,\n        };\n\n        // Store control point positions as canvas coords so they can be rotated during drag\n        this._initialHandleX = cx;\n        this._initialHandleY = top - 30 / this.designerCanvas.zoomFactor;\n        this._initialLineX1 = cx;\n        this._initialLineY1 = top - 22 / this.designerCanvas.zoomFactor;\n        this._initialLineX2 = cx;\n        this._initialLineY2 = top - 6 / this.designerCanvas.zoomFactor;\n\n        // Capture the four corners of the bounding rect for the rotating overlay\n        this._initialCorners = [\n          { x: outerRect.x, y: outerRect.y },\n          { x: outerRect.x + outerRect.width, y: outerRect.y },\n          { x: outerRect.x + outerRect.width, y: outerRect.y + outerRect.height },\n          { x: outerRect.x, y: outerRect.y + outerRect.height },\n        ];\n        // Show rect lines if they were hidden from a previous drag\n        if (this._rectLine1) {\n          this._rectLine1.style.display = '';\n          this._rectLine2.style.display = '';\n          this._rectLine3.style.display = '';\n          this._rectLine4.style.display = '';\n        }\n\n        const mp = this.designerCanvas.getNormalizedEventCoordinates(e);\n        this._startAngle = Math.atan2(mp.y - this._groupCenter.y, mp.x - this._groupCenter.x);\n        this._currentDelta = 0;\n\n        this._elementStartStates = currentSelection.map(item => {\n          const coords = this.designerCanvas.getNormalizedElementCoordinates(item.element);\n          const parent = this.designerCanvas.getNormalizedElementCoordinates(item.parent.element);\n          const rotateStr = item.getStyle('rotate') ?? '0deg';\n          const rotation = parseFloat(rotateStr) || 0;\n          // getBoundingClientRect returns the visual (rotated) bounding box. For position\n          // calculations we need the actual CSS dimensions (unrotated box), otherwise\n          // pre-rotated elements would be placed incorrectly.\n          const cs = getComputedStyle(item.element);\n          const cssHalfWidth = parseFloat(cs.width) / 2;\n          const cssHalfHeight = parseFloat(cs.height) / 2;\n          return {\n            item,\n            rotation,\n            centerX: coords.x + coords.width / 2,\n            centerY: coords.y + coords.height / 2,\n            cssHalfWidth,\n            cssHalfHeight,\n            parentX: parent.x,\n            parentY: parent.y,\n            parentWidth: parent.width,\n            parentHeight: parent.height,\n            usesRight: item.hasStyle('right') && !item.hasStyle('left'),\n            usesBottom: item.hasStyle('bottom') && !item.hasStyle('top'),\n          };\n        });\n      });\n\n      this._rotateCircle.addEventListener('pointermove', e => {\n        e.stopPropagation();\n        if (!this._elementStartStates) return;\n\n        this._currentDelta = this._getDelta(e);\n        for (const state of this._elementStartStates) {\n          this._applyRotation(state, this._currentDelta, false);\n        }\n      });\n\n      this._rotateCircle.addEventListener('pointerup', e => {\n        e.stopPropagation();\n        (<Element>e.target).releasePointerCapture(e.pointerId);\n        if (!this._elementStartStates) return;\n\n        const delta = this._getDelta(e);\n        const grp = this.designerCanvas.instanceServiceContainer.selectionService.primarySelection.openGroup('rotateGroup');\n        for (const state of this._elementStartStates) {\n          this._applyRotation(state, delta, true);\n        }\n        grp.commit();\n\n        this._elementStartStates = null;\n        this._currentDelta = 0;\n      });\n    } else {\n      this._rotateCircle = this._drawCircle(handleX, handleY, 5 / this.designerCanvas.zoomFactor, 'svg-primary-rotate', this._rotateCircle);\n      this._rotateCircle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\n    }\n  }\n\n  private _getDelta(e: MouseEvent): number {\n    const mp = this.designerCanvas.getNormalizedEventCoordinates(e);\n    const currentAngle = Math.atan2(mp.y - this._groupCenter.y, mp.x - this._groupCenter.x);\n    let delta = (currentAngle - this._startAngle) * 180 / Math.PI;\n    // Normalize to (-180, 180] to avoid jumps when crossing the ±180° boundary\n    delta = ((delta + 180) % 360 + 360) % 360 - 180;\n    if (!hasCommandKey(e))\n      delta = Math.round(delta / 15) * 15;\n    return delta;\n  }\n\n  private _applyRotation(state: ElementStartState, deltaAngle: number, commit: boolean) {\n    const rad = deltaAngle * Math.PI / 180;\n    const cos = Math.cos(rad);\n    const sin = Math.sin(rad);\n\n    const dx = state.centerX - this._groupCenter.x;\n    const dy = state.centerY - this._groupCenter.y;\n    const newCenterX = this._groupCenter.x + dx * cos - dy * sin;\n    const newCenterY = this._groupCenter.y + dx * sin + dy * cos;\n\n    const newRotation = roundValue(state.item, state.rotation + deltaAngle);\n\n    const newLeft = newCenterX - state.cssHalfWidth - state.parentX;\n    const newTop = newCenterY - state.cssHalfHeight - state.parentY;\n    const newRight = state.parentX + state.parentWidth - newCenterX - state.cssHalfWidth;\n    const newBottom = state.parentY + state.parentHeight - newCenterY - state.cssHalfHeight;\n\n    if (commit) {\n      if (state.usesRight)\n        state.item.setStyle('right', newRight + 'px');\n      else\n        state.item.setStyle('left', newLeft + 'px');\n\n      if (state.usesBottom)\n        state.item.setStyle('bottom', newBottom + 'px');\n      else\n        state.item.setStyle('top', newTop + 'px');\n\n      state.item.setStyle('rotate', newRotation + 'deg');\n    } else {\n      const el = <HTMLElement>state.item.element;\n      if (state.usesRight)\n        el.style.right = newRight + 'px';\n      else\n        el.style.left = newLeft + 'px';\n\n      if (state.usesBottom)\n        el.style.bottom = newBottom + 'px';\n      else\n        el.style.top = newTop + 'px';\n\n      //@ts-ignore\n      el.style.rotate = newRotation + 'deg';\n    }\n  }\n\n  override dispose() {\n    this._removeAllOverlays();\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/RotateGroupExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { IDesignerExtension } from '../IDesignerExtension.js';\nimport { RotateGroupExtension } from './RotateGroupExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { css } from \"@node-projects/base-custom-webcomponent\";\n\nexport class RotateGroupExtensionProvider implements IDesignerExtensionProvider {\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\n    if (designerCanvas.readOnly)\n      return false;\n    if (designItem.element instanceof SVGElement || designItem.element instanceof HTMLTemplateElement)\n      return false;\n    return !designItem.isRootItem;\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new RotateGroupExtension(extensionManager, designerCanvas, designItem);\n  }\n\n  static readonly style = css`\n    .svg-primary-rotate { stroke: #3899ec; fill: white; stroke-width: 1; pointer-events: auto; cursor: alias; }\n    .svg-primary-rotate-line { stroke: #3899ec; fill: #3899ec; stroke-width: 1; }\n    .svg-rotate-group-rect { stroke: #3899ec; fill: transparent; stroke-width: 2; stroke-dasharray: 5 3; }\n  `;\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/SkewExtension.ts",
    "content": "import { IPoint } from '../../../../../interfaces/IPoint.js';\nimport { hasCommandKey } from '../../../../helper/KeyboardHelper.js';\nimport { roundValue } from '../../../../helper/LayoutHelper.js';\nimport { getElementSize } from '../../../../helper/getBoxQuads.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { AbstractExtension } from '../AbstractExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { getEdgeOffsetPoint, getQuadCenter } from '../../../../helper/QuadEdgeHandleHelper.js';\n\ntype SkewAxis = 'x' | 'y';\n\nexport class SkewExtension extends AbstractExtension {\n  private _skewXLine?: SVGLineElement;\n  private _skewYLine?: SVGLineElement;\n  private _skewXCircle?: SVGCircleElement;\n  private _skewYCircle?: SVGCircleElement;\n  private _skewXReferencePoint: IPoint = { x: 0, y: 0 };\n  private _skewYReferencePoint: IPoint = { x: 0, y: 0 };\n  private _activeAxis: SkewAxis | null = null;\n  private _baseTransform = '';\n  private _skewX = 0;\n  private _skewY = 0;\n\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n  }\n\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\n    this.refresh(cache, event);\n  }\n\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\n    const quad = this.extendedItem.element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\n    if (!quad) {\n      return;\n    }\n\n    const points = [quad.p1, quad.p2, quad.p3, quad.p4];\n    if (points.some(point => !Number.isFinite(point.x) || !Number.isFinite(point.y))) {\n      this.remove();\n      return;\n    }\n\n    const zoomFactor = this.designerCanvas.zoomFactor;\n\n    if (!this._valuesHaveChanges(zoomFactor, ...points.flatMap(point => [point.x, point.y]))) {\n      return;\n    }\n\n    const quadCenter = getQuadCenter(quad);\n    const skewXHandleCanvas = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 56 / zoomFactor, { x: 0, y: -1 });\n    const skewXLineStartCanvas = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 48 / zoomFactor, { x: 0, y: -1 });\n    const skewXLineEndCanvas = getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 38 / zoomFactor, { x: 0, y: -1 });\n\n    const skewYHandleCanvas = getEdgeOffsetPoint(quad.p2, quad.p3, quadCenter, 30 / zoomFactor, { x: 1, y: 0 });\n    const skewYLineStartCanvas = getEdgeOffsetPoint(quad.p2, quad.p3, quadCenter, 22 / zoomFactor, { x: 1, y: 0 });\n    const skewYLineEndCanvas = getEdgeOffsetPoint(quad.p2, quad.p3, quadCenter, 6 / zoomFactor, { x: 1, y: 0 });\n\n    this._skewXLine = this._drawLine(skewXLineStartCanvas.x, skewXLineStartCanvas.y, skewXLineEndCanvas.x, skewXLineEndCanvas.y, 'svg-primary-skew-line', this._skewXLine);\n    this._skewYLine = this._drawLine(skewYLineStartCanvas.x, skewYLineStartCanvas.y, skewYLineEndCanvas.x, skewYLineEndCanvas.y, 'svg-primary-skew-line', this._skewYLine);\n    this._skewXLine.style.strokeWidth = (1 / zoomFactor).toString();\n    this._skewYLine.style.strokeWidth = (1 / zoomFactor).toString();\n\n    if (!this._skewXCircle) {\n      this._skewXCircle = this._drawCircle(skewXHandleCanvas.x, skewXHandleCanvas.y, 5 / zoomFactor, 'svg-primary-skew', this._skewXCircle);\n      this._skewXCircle.addEventListener('pointerdown', event => this._handlePointerEvent('x', event));\n      this._skewXCircle.addEventListener('pointermove', event => this._handlePointerEvent('x', event));\n      this._skewXCircle.addEventListener('pointerup', event => this._handlePointerEvent('x', event));\n    } else {\n      this._skewXCircle = this._drawCircle(skewXHandleCanvas.x, skewXHandleCanvas.y, 5 / zoomFactor, 'svg-primary-skew', this._skewXCircle);\n    }\n    this._skewXCircle.style.strokeWidth = (1 / zoomFactor).toString();\n    this._skewXCircle.style.cursor = 'ew-resize';\n\n    if (!this._skewYCircle) {\n      this._skewYCircle = this._drawCircle(skewYHandleCanvas.x, skewYHandleCanvas.y, 5 / zoomFactor, 'svg-primary-skew', this._skewYCircle);\n      this._skewYCircle.addEventListener('pointerdown', event => this._handlePointerEvent('y', event));\n      this._skewYCircle.addEventListener('pointermove', event => this._handlePointerEvent('y', event));\n      this._skewYCircle.addEventListener('pointerup', event => this._handlePointerEvent('y', event));\n    } else {\n      this._skewYCircle = this._drawCircle(skewYHandleCanvas.x, skewYHandleCanvas.y, 5 / zoomFactor, 'svg-primary-skew', this._skewYCircle);\n    }\n    this._skewYCircle.style.strokeWidth = (1 / zoomFactor).toString();\n    this._skewYCircle.style.cursor = 'ns-resize';\n    this._skewYCircle.style.fill = '#eaf5ff';\n  }\n\n  private _handlePointerEvent(axis: SkewAxis, event: PointerEvent) {\n    event.stopPropagation();\n    const target = event.target as Element;\n\n    switch (event.type) {\n      case 'pointerdown':\n        target.setPointerCapture(event.pointerId);\n        this._readPersistedTransform();\n        if (axis === 'x') {\n          this._skewXReferencePoint = this._getSkewReferencePoint('x');\n        } else {\n          this._skewYReferencePoint = this._getSkewReferencePoint('y');\n        }\n        this._activeAxis = axis;\n        break;\n      case 'pointermove':\n        if (this._activeAxis === axis && event.buttons > 0) {\n          const angle = this._getSkewAngle(axis, event);\n          if (axis === 'x') {\n            this._skewX = angle;\n          } else {\n            this._skewY = angle;\n          }\n          this._setPreviewTransform();\n        }\n        break;\n      case 'pointerup':\n        if (this._activeAxis === axis) {\n          target.releasePointerCapture(event.pointerId);\n          const angle = this._getSkewAngle(axis, event);\n          if (axis === 'x') {\n            this._skewX = angle;\n          } else {\n            this._skewY = angle;\n          }\n          this._commitTransform();\n          this._activeAxis = null;\n        }\n        break;\n    }\n  }\n\n  private _readPersistedTransform() {\n    const { baseTransform, skewX, skewY } = this._parseTransform(this.extendedItem.getStyleFromSheetOrLocal('transform') ?? '');\n    this._baseTransform = baseTransform;\n    this._skewX = skewX;\n    this._skewY = skewY;\n  }\n\n  private _getSkewReferencePoint(axis: SkewAxis) {\n    const element = this.extendedItem.element as HTMLElement;\n    const size = getElementSize(this.extendedItem.element);\n    const zoomFactor = this.designerCanvas.zoomFactor;\n    const fallback = axis === 'x'\n      ? { x: size.width / 2, y: -56 / zoomFactor }\n      : { x: size.width + 30 / zoomFactor, y: size.height / 2 };\n    const inlineTransform = element.style.transform;\n\n    try {\n      element.style.transform = this._buildTransform(this._baseTransform, axis === 'x' ? 0 : this._skewX, axis === 'y' ? 0 : this._skewY);\n      const quad = element.getBoxQuads({ box: 'border', relativeTo: this.designerCanvas.canvas })[0];\n      if (!quad) {\n        return fallback;\n      }\n\n      const quadCenter = getQuadCenter(quad);\n      const handlePoint = axis === 'x'\n        ? getEdgeOffsetPoint(quad.p1, quad.p2, quadCenter, 56 / zoomFactor, { x: 0, y: -1 })\n        : getEdgeOffsetPoint(quad.p2, quad.p3, quadCenter, 30 / zoomFactor, { x: 1, y: 0 });\n      const localPoint = element.convertPointFromNode(handlePoint, this.designerCanvas.canvas);\n      if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y)) {\n        return fallback;\n      }\n      return { x: localPoint.x, y: localPoint.y };\n    } finally {\n      element.style.transform = inlineTransform;\n    }\n  }\n\n  private _setPreviewTransform() {\n    (this.extendedItem.element as HTMLElement).style.transform = this._buildTransform(this._baseTransform, this._skewX, this._skewY);\n  }\n\n  private _commitTransform() {\n    const finalTransform = this._buildTransform(this._baseTransform, this._skewX, this._skewY);\n    if (finalTransform) {\n      this.extendedItem.updateStyleInSheetOrLocal('transform', finalTransform);\n    } else if (this.extendedItem.hasStyle('transform')) {\n      this.extendedItem.removeStyle('transform');\n    } else {\n      this.extendedItem.updateStyleInSheetOrLocal('transform', null);\n    }\n\n    const element = this.extendedItem.element as HTMLElement;\n    if (this.extendedItem.hasStyle('transform')) {\n      element.style.transform = this.extendedItem.getStyle('transform') ?? '';\n    } else {\n      element.style.transform = '';\n    }\n  }\n\n  private _getSkewAngle(axis: SkewAxis, event: PointerEvent) {\n    const element = this.extendedItem.element as HTMLElement;\n    element.style.transform = this._buildTransform(this._baseTransform, axis === 'x' ? 0 : this._skewX, axis === 'y' ? 0 : this._skewY);\n\n    const mousePoint = this.designerCanvas.getNormalizedEventCoordinates(event);\n    const localPoint = element.convertPointFromNode({ x: mousePoint.x, y: mousePoint.y }, this.designerCanvas.canvas);\n\n    const computedStyle = getComputedStyle(element);\n    const transformOrigin = computedStyle.transformOrigin.split(' ');\n    const origin = { x: parseFloat(transformOrigin[0]), y: parseFloat(transformOrigin[1]) };\n    const size = getElementSize(element);\n\n    let angle: number;\n    if (axis === 'x') {\n      let referenceY = this._skewXReferencePoint.y - origin.y;\n      if (Math.abs(referenceY) < 0.5) {\n        referenceY = -Math.max(size.height / 2, 1);\n      }\n      angle = Math.atan((localPoint.x - origin.x) / referenceY) * 180 / Math.PI;\n    } else {\n      let referenceX = this._skewYReferencePoint.x - origin.x;\n      if (Math.abs(referenceX) < 0.5) {\n        referenceX = Math.max(size.width / 2, 1);\n      }\n      angle = Math.atan((localPoint.y - origin.y) / referenceX) * 180 / Math.PI;\n    }\n\n    angle = Math.max(-80, Math.min(80, angle));\n    if (!hasCommandKey(event)) {\n      angle = Math.round(angle / 15) * 15;\n    }\n    return parseFloat(roundValue(this.extendedItem, angle));\n  }\n\n  private _buildTransform(baseTransform: string, skewX: number, skewY: number) {\n    const transforms: string[] = [];\n    if (baseTransform?.trim()) {\n      transforms.push(baseTransform.trim());\n    }\n    if (Math.abs(skewX) > 0.001) {\n      transforms.push(`skewX(${skewX}deg)`);\n    }\n    if (Math.abs(skewY) > 0.001) {\n      transforms.push(`skewY(${skewY}deg)`);\n    }\n    return transforms.join(' ').trim();\n  }\n\n  private _parseTransform(transform: string) {\n    if (!transform || transform === 'none') {\n      return { baseTransform: '', skewX: 0, skewY: 0 };\n    }\n\n    let skewX = 0;\n    let skewY = 0;\n    const baseTransforms: string[] = [];\n\n    for (const part of this._splitTransformFunctions(transform)) {\n      const functionName = part.slice(0, part.indexOf('(')).trim().toLowerCase();\n      const argumentsText = part.slice(part.indexOf('(') + 1, -1);\n      const argumentsList = argumentsText.split(',').map(value => value.trim());\n\n      if (functionName === 'skewx') {\n        skewX = this._parseAngle(argumentsList[0]);\n      } else if (functionName === 'skewy') {\n        skewY = this._parseAngle(argumentsList[0]);\n      } else if (functionName === 'skew') {\n        skewX = this._parseAngle(argumentsList[0]);\n        if (argumentsList.length > 1) {\n          skewY = this._parseAngle(argumentsList[1]);\n        }\n      } else {\n        baseTransforms.push(part);\n      }\n    }\n\n    return { baseTransform: baseTransforms.join(' ').trim(), skewX, skewY };\n  }\n\n  private _splitTransformFunctions(transform: string) {\n    const parts: string[] = [];\n    let startIndex = -1;\n    let depth = 0;\n\n    for (let index = 0; index < transform.length; index++) {\n      const character = transform[index];\n      if (character === '(') {\n        depth++;\n      } else if (character === ')') {\n        depth--;\n        if (depth === 0 && startIndex !== -1) {\n          parts.push(transform.slice(startIndex, index + 1).trim());\n          startIndex = -1;\n        }\n      } else if (depth === 0 && startIndex === -1 && character.trim()) {\n        startIndex = index;\n      }\n    }\n\n    if (!parts.length && transform.trim()) {\n      parts.push(transform.trim());\n    }\n    return parts;\n  }\n\n  private _parseAngle(value: string) {\n    if (!value) {\n      return 0;\n    }\n\n    const numericValue = parseFloat(value);\n    if (isNaN(numericValue)) {\n      return 0;\n    }\n    if (value.endsWith('rad')) {\n      return numericValue * 180 / Math.PI;\n    }\n    if (value.endsWith('grad')) {\n      return numericValue * 0.9;\n    }\n    if (value.endsWith('turn')) {\n      return numericValue * 360;\n    }\n    return numericValue;\n  }\n\n  override dispose() {\n    this._removeAllOverlays();\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/SkewExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\nimport { IDesignerExtension } from '../IDesignerExtension.js';\nimport { SkewExtension } from './SkewExtension.js';\nimport { IExtensionManager } from '../IExtensionManger.js';\nimport { css } from '@node-projects/base-custom-webcomponent';\n\nexport class SkewExtensionProvider implements IDesignerExtensionProvider {\n  shouldExtend(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): boolean {\n    if (designerCanvas.readOnly)\n      return false;\n    if (designItem.element instanceof SVGElement || designItem.element instanceof HTMLTemplateElement) {\n      return false;\n    }\n    return !designItem.isRootItem;\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new SkewExtension(extensionManager, designerCanvas, designItem);\n  }\n\n  static readonly style = css`\n    .svg-primary-skew { stroke: #3899ec; fill: white; stroke-width: 1; pointer-events: auto; }\n    .svg-primary-skew-line { stroke: #3899ec; fill: none; stroke-width: 1; }\n  `;\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/TransformOriginExtension.ts",
    "content": "import { EventNames } from '../../../../../enums/EventNames.js';\r\nimport { IPoint } from '../../../../../interfaces/IPoint.js';\r\nimport { convertCssUnit, getCssUnit } from '../../../../helper/CssUnitConverter.js';\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../../../helper/ElementHelper.js';\r\nimport { clearCache, getResultingTransformationBetweenElementAndAllAncestors } from '../../../../helper/getBoxQuads.js';\r\nimport { roundValue } from '../../../../helper/LayoutHelper.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { AbstractExtension } from '../AbstractExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\n\r\nexport class TransformOriginExtension extends AbstractExtension {\r\n  private _startPos: IPoint;\r\n  private _circle: SVGCircleElement;\r\n  private _circle2: SVGCircleElement;\r\n  private _oldValue: string;\r\n  private _offsetInControl: { x: number; y: number; };\r\n\r\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\r\n    super(extensionManager, designerView, extendedItem);\r\n  }\r\n\r\n  private _getTransformOriginCanvasPoint() {\r\n    const computed = getComputedStyle(this.extendedItem.element);\r\n    const to = computed.transformOrigin.split(' ');\r\n    const transform = getResultingTransformationBetweenElementAndAllAncestors(this.extendedItem.element, this.designerCanvas.canvas, this.designerCanvas.iframes);\r\n    return transform.transformPoint(new DOMPoint(parseFloat(to[0]), parseFloat(to[1])));\r\n  }\r\n\r\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\r\n    const toDOMPoint = this._getTransformOriginCanvasPoint();\r\n    if (this._valuesHaveChanges(toDOMPoint.x, toDOMPoint.y, this.designerCanvas.zoomFactor)) {\r\n      this._removeAllOverlays();\r\n      this._circle = this._drawCircle(toDOMPoint.x, toDOMPoint.y, 5 / this.designerCanvas.zoomFactor, 'svg-transform-origin');\r\n      this._circle.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n      this._circle.style.cursor = 'pointer';\r\n      this._circle2 = this._drawCircle(toDOMPoint.x, toDOMPoint.y, 1 / this.designerCanvas.zoomFactor, 'svg-transform-origin');\r\n      this._circle2.style.strokeWidth = (1 / this.designerCanvas.zoomFactor).toString();\r\n      this._circle2.style.pointerEvents = 'none';\r\n      this._circle.addEventListener(EventNames.PointerDown, event => this.pointerEvent(event));\r\n      this._circle.addEventListener(EventNames.PointerMove, event => this.pointerEvent(event));\r\n      this._circle.addEventListener(EventNames.PointerUp, event => this.pointerEvent(event));\r\n    }\r\n  }\r\n\r\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\r\n    const toDOMPoint = this._getTransformOriginCanvasPoint();\r\n    if (isNaN(toDOMPoint.x) || isNaN(toDOMPoint.y)) {\r\n      this.remove();\r\n      return;\r\n    } else {\r\n      let old = this.extendedItem.getStyleFromSheetOrLocal('transform-origin');\r\n      if (old) {\r\n        this._oldValue = old;\r\n      }\r\n      this.refresh(cache, event);\r\n    }\r\n  }\r\n\r\n  pointerEvent(event: PointerEvent) {\r\n    event.stopPropagation();\r\n    const computed = getComputedStyle(this.extendedItem.element);\r\n    const to = computed.transformOrigin.split(' ');\r\n    const toInPercentage = [];\r\n    toInPercentage[0] = parseFloat(to[0]) / parseFloat((<HTMLElement>this.extendedItem.element).style.width);\r\n    toInPercentage[1] = parseFloat(to[1]) / parseFloat((<HTMLElement>this.extendedItem.element).style.height);\r\n    const toDOMPoint = this._getTransformOriginCanvasPoint();\r\n    const mp = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n    const evPoint = this.extendedItem.element.convertPointFromNode(mp, this.designerCanvas.canvas);\r\n    const normalized = this.designerCanvas.getNormalizedEventCoordinates(event);\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        const rect = getBoundingClientRectAlsoForDisplayContents((<HTMLElement>event.target));\r\n        this._offsetInControl = { x: rect.width / 2 + (rect.x - event.x), y: rect.height / 2 + (rect.y - event.y) };\r\n        this._startPos = { x: normalized.x, y: normalized.y };\r\n        break;\r\n      case EventNames.PointerMove:\r\n        if (this._startPos && event.buttons > 0) {\r\n          const dx = normalized.x - this._startPos.x;\r\n          const dy = normalized.y - this._startPos.y;\r\n          this._circle.setAttribute('cx', <any>(toDOMPoint.x + dx));\r\n          this._circle.setAttribute('cy', <any>(toDOMPoint.y + dy));\r\n          this._circle2.setAttribute('cx', <any>(toDOMPoint.x + dx));\r\n          this._circle2.setAttribute('cy', <any>(toDOMPoint.y + dy));\r\n        }\r\n        break;\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        if (this._startPos) {\r\n          evPoint.x += this._offsetInControl.x;\r\n          evPoint.y += this._offsetInControl.y;\r\n          const cg = this.extendedItem.openGroup('change transform-origin');\r\n          const quadsOld = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.rootDesignItem.element })[0];\r\n          if (this._oldValue) {\r\n            try {\r\n              const oldSplit = this._oldValue.split(' ');\r\n              let newXs = convertCssUnit(evPoint.x, <HTMLElement>this.extendedItem.element, 'width', getCssUnit(oldSplit[0]), (nr) => roundValue(this.extendedItem, nr));\r\n              let newYs = convertCssUnit(evPoint.x, <HTMLElement>this.extendedItem.element, 'width', getCssUnit(oldSplit[0]), (nr) => roundValue(this.extendedItem, nr));\r\n              if (oldSplit.length > 1) {\r\n                newYs = convertCssUnit(evPoint.y, <HTMLElement>this.extendedItem.element, 'height', getCssUnit(oldSplit[1]), (nr) => roundValue(this.extendedItem, nr));\r\n              }\r\n              this.extendedItem.updateStyleInSheetOrLocal('transform-origin', newXs + ' ' + newYs);\r\n            } catch (err) {\r\n              this.extendedItem.updateStyleInSheetOrLocal('transform-origin', roundValue(this.extendedItem, evPoint.x) + 'px' + ' ' + roundValue(this.extendedItem, evPoint.y) + 'px');\r\n            }\r\n          }\r\n          else\r\n            this.extendedItem.updateStyleInSheetOrLocal('transform-origin', roundValue(this.extendedItem, evPoint.x) + 'px' + ' ' + roundValue(this.extendedItem, evPoint.y) + 'px');\r\n          clearCache(); //clear box quads cache to make sure we get the new quads with the updated transform origin\r\n          const quadsNew = this.extendedItem.element.getBoxQuads({ relativeTo: this.designerCanvas.rootDesignItem.element })[0];\r\n          const translateP = { x: quadsOld.p1.x - quadsNew.p1.x, y: quadsOld.p1.y - quadsNew.p1.y };\r\n          if (computed.translate && computed.translate !== 'none') {\r\n            translateP.x += parseFloat(computed.translate.split(' ')[0]);\r\n            if (computed.translate.split(' ').length === 1)\r\n              translateP.y += parseFloat(computed.translate.split(' ')[0]);\r\n            else\r\n              translateP.y += parseFloat(computed.translate.split(' ')[1]);\r\n          }\r\n          this.extendedItem.updateStyleInSheetOrLocal('translate', roundValue(this.extendedItem, translateP.x) + 'px' + ' ' + roundValue(this.extendedItem, translateP.y) + 'px');\r\n          cg.commit();\r\n          this.refresh(null, null);\r\n          this._startPos = null;\r\n        }\r\n        break;\r\n    }\r\n  }\r\n\r\n  override dispose() {\r\n    this._removeAllOverlays();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/extensions/transforms/TransformOriginExtensionProvider.ts",
    "content": "import { IDesignerExtensionProvider } from '../IDesignerExtensionProvider.js';\r\nimport { IDesignItem } from '../../../../item/IDesignItem.js';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { IDesignerExtension } from '../IDesignerExtension.js';\r\nimport { TransformOriginExtension } from './TransformOriginExtension.js';\r\nimport { IExtensionManager } from '../IExtensionManger.js';\r\nimport { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { getBoundingClientRectAlsoForDisplayContents } from '../../../../helper/ElementHelper.js';\r\n\r\nexport class TransformOriginExtensionProvider implements IDesignerExtensionProvider {\r\n  showOnlyWhenSet: boolean;\r\n\r\n  constructor(showOnlyWhenSet = true) {\r\n    this.showOnlyWhenSet = showOnlyWhenSet;\r\n  }\r\n\r\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\r\n    if (!designItem.isRootItem && designItem.node instanceof HTMLElement || (designItem.node instanceof SVGElement && designItem.node.localName === 'svg')) {\r\n      if (!this.showOnlyWhenSet)\r\n        return true;\r\n      if (designItem.hasStyle('transformOrigin'))\r\n        return true;\r\n      const cs = getComputedStyle(designItem.element);\r\n      if (cs.display != 'inline' && designItem.element.getBoundingClientRect) {\r\n        const r = getBoundingClientRectAlsoForDisplayContents(designItem.element);\r\n        const pr = cs.transformOrigin.split(' ');\r\n        const x = parseFloat(pr[0]) - r.width / 2;\r\n        const y = parseFloat(pr[1]) - r.height / 2;\r\n        if (x > 0.5 || x < -0.5 || y > 0.5 || y < -0.5)\r\n          return true;\r\n      }\r\n    }\r\n    return false;\r\n  }\r\n\r\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\r\n    return new TransformOriginExtension(extensionManager, designerView, designItem);\r\n  }\r\n\r\n  static readonly style = css`\r\n    .svg-transform-origin { stroke: #3899ec; fill: black; pointer-events: auto; }\r\n  `;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/overlay/EditTextOverlay.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class EditTextOverlay extends BaseCustomWebComponentConstructorAppend {\r\n  \r\n  public static override readonly style = css``\r\n\r\n  public static override readonly template = html`\r\n    <div>\r\n      <input type=\"text\">\r\n    </div>\r\n  `\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/overlayLayerView.ts",
    "content": "import { css, html, BaseCustomWebComponentConstructorAppend } from '@node-projects/base-custom-webcomponent';\r\nimport { OverlayLayer } from './extensions/OverlayLayer.js';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\n\r\nexport class OverlayLayerView extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override readonly template = html`\r\n    <svg id=\"svg\" style=\"pointer-events: none;\">\r\n      <defs id=\"defs\"></defs>\r\n      <g id=\"background\"></g>\r\n      <g id=\"normal\"></g>\r\n      <g id=\"foreground\"></g>\r\n    </svg>`;\r\n\r\n\r\n  static override readonly style = css`\r\n    svg {\r\n      width: 100%;\r\n      height: 100%;\r\n      overflow: visible;\r\n    }\r\n\r\n    .svg-invisible { stroke: transparent; fill: transparent; pointer-events: auto; }\r\n    .svg-snapline { stroke: purple; stroke-dasharray: 4; fill: transparent; }\r\n    .svg-selector { stroke: black; fill: #3899ec55; stroke-width: 1; stroke-dasharray: 2; }\r\n    .svg-primary-selection-move { stroke: #3899ec; fill: #3899ec; cursor: move; pointer-events: auto; }\r\n    .svg-position  { stroke: black; stroke-dasharray: 2; }\r\n    .svg-path { stroke: #3899ec; fill: orange; pointer-events: auto; }\r\n    .svg-path-line { stroke: #3899ec; stroke-dasharray: 2; }\r\n    .svg-draw-new-element { stroke: black; fill: transparent; stroke-width: 1; }\r\n    .svg-toolbar-container { overflow: visible }\r\n    .svg-toolbar-container > div { padding: 5px; display: flex; gap: 2px; background: white; border-radius: 4px; box-shadow: 0 2px 10px 0 rgba(19,23,32,.2); align-items: center; }\r\n    \r\n    node-projects-image-button-list-selector img {\r\n      height: 16px;\r\n      border: 1px solid black;\r\n      border-radius: 4px;\r\n      box-sizing: border-box;\r\n      pointer-events: auto;\r\n      cursor: pointer;\r\n    }\r\n    node-projects-image-button-list-selector img:hover {\r\n      background: lightgray;\r\n    }\r\n    node-projects-image-button-list-selector img:active {\r\n      translate: 1px 1px\r\n    }\r\n    `;\r\n\r\n  static readonly is = 'node-projects-overlay-layer-view';\r\n\r\n  private _serviceContainer: ServiceContainer;\r\n  private _svg: SVGElement;\r\n  private _gBackground: SVGGElement;\r\n  private _gNormal: SVGGElement;\r\n  private _gForeground: SVGGElement;\r\n  private _defs: SVGDefsElement;\r\n  private _id: number = 0;\r\n\r\n  constructor(serviceContainer: ServiceContainer) {\r\n    super();\r\n\r\n    this._serviceContainer = serviceContainer;\r\n    this._svg = this._getDomElement<SVGElement>('svg');\r\n    this._gBackground = this._getDomElement<SVGGElement>('background');\r\n    this._gNormal = this._getDomElement<SVGGElement>('normal');\r\n    this._gForeground = this._getDomElement<SVGGElement>('foreground');\r\n    this._defs = this._getDomElement<SVGDefsElement>('defs');\r\n\r\n    this._initialize();\r\n  }\r\n\r\n  private _initialize() {\r\n    const styles: CSSStyleSheet[] = [OverlayLayerView.style];\r\n    const alreadyApplied = new Set();\r\n\r\n    for (const extList of this._serviceContainer.designerExtensions) {\r\n      for (const ext of extList[1]) {\r\n        if (ext.constructor.style) {\r\n          if (Array.isArray(ext.constructor.style)) {\r\n            for (const s of ext.constructor.style) {\r\n              if (!alreadyApplied.has(s)) {\r\n                alreadyApplied.add(s);\r\n                styles.push(s);\r\n              }\r\n            }\r\n          } else\r\n            if (!alreadyApplied.has(ext.constructor.style)) {\r\n              alreadyApplied.add(ext.constructor.style);\r\n              styles.push(ext.constructor.style);\r\n            }\r\n        }\r\n        if (ext.style) {\r\n          if (Array.isArray(ext.style)) {\r\n            for (const s of ext.style) {\r\n              if (!alreadyApplied.has(s)) {\r\n                alreadyApplied.add(s);\r\n                styles.push(s);\r\n              }\r\n            }\r\n          } else\r\n            if (!alreadyApplied.has(ext.style)) {\r\n              alreadyApplied.add(ext.style);\r\n              styles.push(ext.style);\r\n            }\r\n        }\r\n        if (ext.svgDefs) {\r\n          if (Array.isArray(ext.svgDefs)) {\r\n            for (const s of ext.svgDefs) {\r\n              if (!alreadyApplied.has(s)) {\r\n                alreadyApplied.add(s);\r\n                const a = document.createElementNS(\"http://www.w3.org/2000/svg\", \"defs\")\r\n                a.innerHTML = s;\r\n                for (let n of [...a.children])\r\n                  this._defs.appendChild(n);\r\n              }\r\n            }\r\n          } else\r\n            if (!alreadyApplied.has(ext.svgDefs)) {\r\n              alreadyApplied.add(ext.svgDefs);\r\n              const a = document.createElementNS(\"http://www.w3.org/2000/svg\", \"defs\")\r\n              a.innerHTML = ext.svgDefs;\r\n              for (let n of [...a.children])\r\n                this._defs.appendChild(n);\r\n            }\r\n        }\r\n        if (ext.constructor.svgDefs) {\r\n          if (Array.isArray(ext.constructor.svgDefs)) {\r\n            for (const s of ext.constructor.svgDefs) {\r\n              if (!alreadyApplied.has(s)) {\r\n                alreadyApplied.add(s);\r\n                const a = document.createElementNS(\"http://www.w3.org/2000/svg\", \"defs\")\r\n                a.innerHTML = s;\r\n                for (let n of [...a.children])\r\n                  this._defs.appendChild(n);\r\n              }\r\n            }\r\n          } else\r\n            if (!alreadyApplied.has(ext.constructor.svgDefs)) {\r\n              alreadyApplied.add(ext.constructor.svgDefs);\r\n              const a = document.createElementNS(\"http://www.w3.org/2000/svg\", \"defs\")\r\n              a.innerHTML = ext.constructor.svgDefs;\r\n              for (let n of [...a.children])\r\n                this._defs.appendChild(n);\r\n            }\r\n        }\r\n      }\r\n    }\r\n\r\n    for (const ext of this._serviceContainer.designerPointerExtensions) {\r\n      if (ext.style) {\r\n        if (!alreadyApplied.has(ext.style)) {\r\n          alreadyApplied.add(ext.style);\r\n          styles.push(ext.style);\r\n        }\r\n      }\r\n      if (ext.svgDefs) {\r\n        if (!alreadyApplied.has(ext.svgDefs)) {\r\n          alreadyApplied.add(ext.svgDefs);\r\n          const a = document.createElementNS(\"http://www.w3.org/2000/svg\", \"defs\")\r\n          a.innerHTML = ext.svgDefs;\r\n          for (let n of [...a.children])\r\n            this._defs.appendChild(n);\r\n        }\r\n      }\r\n    }\r\n\r\n    if (this._serviceContainer.overlayLayerViewAdditionalStyles) {\r\n      for (const style of this._serviceContainer.overlayLayerViewAdditionalStyles) {\r\n        if (!alreadyApplied.has(style)) {\r\n          alreadyApplied.add(style);\r\n          styles.push(style);\r\n        }\r\n      }\r\n    }\r\n\r\n    this.shadowRoot.adoptedStyleSheets = styles;\r\n  }\r\n\r\n  backgroundFragment: DocumentFragment;\r\n  foregroundFragment: DocumentFragment;\r\n  normalFragment: DocumentFragment;\r\n  batchCount = 0;\r\n\r\n  public startBatch() {\r\n    if (this.batchCount == 0) {\r\n      this.backgroundFragment = document.createDocumentFragment();\r\n      this.foregroundFragment = document.createDocumentFragment();\r\n      this.normalFragment = document.createDocumentFragment();\r\n    }\r\n    this.batchCount++;\r\n  }\r\n\r\n  public endBatch() {\r\n    this.batchCount--;\r\n    if (this.batchCount == 0) {\r\n      if (this.backgroundFragment.hasChildNodes)\r\n        this._gBackground.appendChild(this.backgroundFragment);\r\n      if (this.foregroundFragment.hasChildNodes)\r\n        this._gForeground.appendChild(this.foregroundFragment);\r\n      if (this.normalFragment.hasChildNodes)\r\n        this._gNormal.appendChild(this.normalFragment);\r\n      this.backgroundFragment = null;\r\n      this.foregroundFragment = null;\r\n      this.normalFragment = null;\r\n    }\r\n  }\r\n\r\n  public addOverlay(overlaySource: string, element: SVGGraphicsElement, overlayLayer: OverlayLayer = OverlayLayer.Normal) {\r\n    element.setAttribute(\"overlay-source\", overlaySource);\r\n    switch (overlayLayer) {\r\n      case OverlayLayer.Background:\r\n        if (this.backgroundFragment)\r\n          this.backgroundFragment.appendChild(element);\r\n        else\r\n          this._gBackground.appendChild(element);\r\n        break;\r\n      case OverlayLayer.Foreground:\r\n        if (this.foregroundFragment)\r\n          this.foregroundFragment.appendChild(element);\r\n        else\r\n          this._gForeground.appendChild(element);\r\n        break;\r\n      default:\r\n        if (this.normalFragment)\r\n          this.normalFragment.appendChild(element);\r\n        else\r\n          this._gNormal.appendChild(element);\r\n        break;\r\n    }\r\n  }\r\n\r\n  public removeOverlay(element: SVGElement) {\r\n    try {\r\n      element?.parentElement?.removeChild(element);\r\n    } catch (err) {\r\n      console.error(err);\r\n    }\r\n  }\r\n\r\n  public removeAllNodesWithClass(className: string) {\r\n    const nodes = this._svg.querySelectorAll('.' + className);\r\n    for (const e of nodes) {\r\n      e.parentNode.removeChild(e);\r\n    }\r\n  }\r\n\r\n  public removeAllOverlays() {\r\n    const nodes = this._svg.querySelectorAll('svg > g > *');\r\n    for (const e of nodes) {\r\n      e.parentNode.removeChild(e);\r\n    }\r\n  }\r\n\r\n  public createPoint(): DOMPointInit {\r\n    //@ts-ignore\r\n    return this._svg.createSVGPoint();\r\n  }\r\n\r\n  drawGroup(overlaySource: string, className?: string, group?: SVGGElement, overlayLayer?: OverlayLayer) {\r\n    if (!group) {\r\n      group = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\r\n      this.addOverlay(overlaySource, group, overlayLayer);\r\n    }\r\n    if (className)\r\n      group.setAttribute('class', className);\r\n    return group;\r\n  }\r\n\r\n  drawLine(overlaySource: string, x1: number, y1: number, x2: number, y2: number, className?: string, line?: SVGLineElement, overlayLayer?: OverlayLayer) {\r\n    if (!line) {\r\n      line = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n      this.addOverlay(overlaySource, line, overlayLayer);\r\n    }\r\n    line.setAttribute('x1', <string><any>x1);\r\n    line.setAttribute('y1', <string><any>y1);\r\n    line.setAttribute('x2', <string><any>x2);\r\n    line.setAttribute('y2', <string><any>y2);\r\n    if (className)\r\n      line.setAttribute('class', className);\r\n\r\n    return line;\r\n  }\r\n\r\n  drawCircle(overlaySource: string, x: number, y: number, radius: number, className?: string, circle?: SVGCircleElement, overlayLayer?: OverlayLayer) {\r\n    if (!circle) {\r\n      circle = document.createElementNS(\"http://www.w3.org/2000/svg\", \"circle\");\r\n      this.addOverlay(overlaySource, circle, overlayLayer);\r\n    }\r\n    circle.setAttribute('cx', <string><any>x);\r\n    circle.setAttribute('cy', <string><any>y);\r\n    circle.setAttribute('r', <string><any>radius);\r\n    if (className)\r\n      circle.setAttribute('class', className);\r\n    return circle;\r\n  }\r\n\r\n  drawRect(overlaySource: string, x: number, y: number, w: number, h: number, className?: string, rect?: SVGRectElement, overlayLayer?: OverlayLayer) {\r\n    if (!rect) {\r\n      rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n      this.addOverlay(overlaySource, rect, overlayLayer);\r\n    }\r\n    rect.setAttribute('x', <string><any>x);\r\n    rect.setAttribute('y', <string><any>y);\r\n    rect.setAttribute('width', <string><any>w);\r\n    rect.setAttribute('height', <string><any>h);\r\n    if (className)\r\n      rect.setAttribute('class', className);\r\n    return rect;\r\n  }\r\n\r\n  drawPath(overlaySource: string, data: string, className?: string, path?: SVGPathElement, overlayLayer?: OverlayLayer) {\r\n    if (!path) {\r\n      path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n      this.addOverlay(overlaySource, path, overlayLayer);\r\n    }\r\n    path.setAttribute('d', data);\r\n    if (className)\r\n      path.setAttribute('class', className);\r\n    return path;\r\n  }\r\n\r\n  drawText(overlaySource: string, text: string, x: number, y: number, className?: string, textEl?: SVGTextElement, overlayLayer?: OverlayLayer) {\r\n    if (!textEl) {\r\n      textEl = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\r\n      this.addOverlay(overlaySource, textEl, overlayLayer);\r\n    }\r\n    textEl.setAttribute('x', <string><any>x);\r\n    textEl.setAttribute('y', <string><any>y);\r\n    textEl.textContent = text;\r\n    if (className)\r\n      textEl.setAttribute('class', className);\r\n    return textEl;\r\n  }\r\n\r\n  drawHTML(overlaySource: string, html: HTMLElement | string, x: number, y: number, w: number, h: number, className?: string, htmlObj?: SVGForeignObjectElement, overlayLayer?: OverlayLayer) {\r\n    if (!htmlObj) {\r\n      htmlObj = document.createElementNS(\"http://www.w3.org/2000/svg\", \"foreignObject\");\r\n      this.addOverlay(overlaySource, htmlObj, overlayLayer);\r\n    }\r\n    htmlObj.setAttribute('x', <string><any>x);\r\n    htmlObj.setAttribute('y', <string><any>y);\r\n    htmlObj.setAttribute('width', <string><any>w);\r\n    htmlObj.setAttribute('height', <string><any>h);\r\n    if (typeof html === 'string')\r\n      htmlObj.innerHTML = html;\r\n    else\r\n      htmlObj.appendChild(html);\r\n    if (className)\r\n      htmlObj.setAttribute('class', className);\r\n    return htmlObj;\r\n  }\r\n\r\n  drawTextWithBackground(overlaySource: string, text: string, x: number, y: number, backgroundColor: string, className?: string, existingEls?: [SVGFilterElement, SVGFEFloodElement, SVGTextElement, SVGTextElement], overlayLayer?: OverlayLayer): [SVGFilterElement, SVGFEFloodElement, SVGTextElement, SVGTextElement] {\r\n    if (!existingEls) {\r\n      let filter = document.createElementNS(\"http://www.w3.org/2000/svg\", \"filter\");\r\n      filter.setAttribute(\"x\", \"0\");\r\n      filter.setAttribute(\"y\", \"0\");\r\n      filter.setAttribute(\"width\", \"1\");\r\n      filter.setAttribute(\"height\", \"1\");\r\n      filter.setAttribute(\"id\", \"solid_\" + (++this._id));\r\n      let flood = document.createElementNS(\"http://www.w3.org/2000/svg\", \"feFlood\");\r\n      flood.setAttribute(\"flood-color\", backgroundColor);\r\n      filter.appendChild(flood);\r\n      let composite = document.createElementNS(\"http://www.w3.org/2000/svg\", \"feComposite\");\r\n      composite.setAttribute(\"in\", \"SourceGraphic\");\r\n      composite.setAttribute(\"operator\", \"xor\");\r\n      filter.appendChild(composite);\r\n      this._defs.appendChild(filter);\r\n      let textEl1 = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\r\n      textEl1.setAttribute(\"filter\", \"url(#solid_\" + this._id + \")\");\r\n      let textEl2 = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\r\n      this.addOverlay(overlaySource, textEl1, overlayLayer);\r\n      this.addOverlay(overlaySource, textEl2, overlayLayer);\r\n      filter.setAttribute(\"overlay-source\", overlaySource);\r\n      flood.setAttribute(\"overlay-source\", overlaySource);\r\n      existingEls = [filter, flood, textEl1, textEl2]\r\n    }\r\n    existingEls[2].setAttribute('x', <string><any>x);\r\n    existingEls[3].setAttribute('x', <string><any>x);\r\n    existingEls[2].setAttribute('y', <string><any>y);\r\n    existingEls[3].setAttribute('y', <string><any>y);\r\n    existingEls[2].textContent = text;\r\n    existingEls[3].textContent = text;\r\n    if (className) {\r\n      existingEls[2].setAttribute('class', className);\r\n      existingEls[3].setAttribute('class', className);\r\n    }\r\n    return existingEls;\r\n  }\r\n}\r\n\r\ncustomElements.define(OverlayLayerView.is, OverlayLayerView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/DrawElementTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\nimport { roundValue } from '../../../helper/LayoutHelper.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IElementDefinition } from '../../../services/elementsService/IElementDefinition.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { ChangeGroup } from '../../../services/undoService/ChangeGroup.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class DrawElementTool implements ITool {\r\n  private _elementDefinition: IElementDefinition;\r\n  private _createdItem: IDesignItem;\r\n  private _startPosition: IPoint;\r\n  private _changeGroup: ChangeGroup;\r\n\r\n  readonly cursor = 'crosshair';\r\n  private _rect: any;\r\n\r\n  constructor(elementDefinition: IElementDefinition) {\r\n    this._elementDefinition = elementDefinition;\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n    if (this._createdItem)\r\n      this._createdItem.element.parentElement.removeChild(this._createdItem.element);\r\n  }\r\n\r\n  pointerEventHandler(designerView: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        this._onPointerDown(designerView, event);\r\n        break;\r\n      case EventNames.PointerMove:\r\n        this._onPointerMove(designerView, event);\r\n        break;\r\n      case EventNames.PointerUp:\r\n        this._onPointerUp(designerView, event);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n\r\n  sizeOverlapThreshold = false;\r\n  private async _onPointerDown(designerCanvas: IDesignerCanvas, event: PointerEvent) {\r\n    const evPos = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    event.preventDefault();\r\n    this._startPosition = { x: evPos.x, y: evPos.y };\r\n\r\n    this._changeGroup = designerCanvas.rootDesignItem.openGroup(\"Insert Item\");\r\n    this._createdItem = await designerCanvas.serviceContainer.forSomeServicesTillResult(\"instanceService\", (service) => service.getElement(this._elementDefinition, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer));\r\n    this._createdItem.setStyle('position', 'absolute');\r\n    this._createdItem.setStyle('left', roundValue(this._createdItem, evPos.x) + 'px');\r\n    this._createdItem.setStyle('top', roundValue(this._createdItem, evPos.y) + 'px');\r\n    this._createdItem.setStyle('width', '0');\r\n    this._createdItem.setStyle('height', '0');\r\n\r\n    designerCanvas.rootDesignItem.insertChild(this._createdItem);\r\n    //draw via containerService??? how to draw into a grid, a stackpanel???\r\n    designerCanvas.instanceServiceContainer.selectionService.clearSelectedElements();\r\n  }\r\n\r\n  private async _onPointerMove(designerCanvas: IDesignerCanvas, event: PointerEvent) {\r\n    const evPos = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    if (this._createdItem) {\r\n      if (!this._rect) {\r\n        this._rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._rect, OverlayLayer.Foreground);\r\n        this._rect.setAttribute('class', 'svg-draw-new-element');\r\n        this._rect.setAttribute('x', <string><any>roundValue(this._createdItem, this._startPosition.x));\r\n        this._rect.setAttribute('y', <string><any>roundValue(this._createdItem, this._startPosition.y));\r\n      }\r\n\r\n      const w = roundValue(this._createdItem, evPos.x - this._startPosition.x);\r\n      const h = roundValue(this._createdItem, evPos.y - this._startPosition.y);\r\n      if (parseFloat('' + w) >= 0) {\r\n        this._rect.setAttribute('width', w);\r\n        this._createdItem.setStyle('width', w + 'px');\r\n      }\r\n      if (parseFloat('' + h) >= 0) {\r\n        this._rect.setAttribute('height', h);\r\n        this._createdItem.setStyle('height', h + 'px');\r\n      }\r\n\r\n      if (parseFloat('' + w) > 5 || parseFloat('' + h) > 5)\r\n        this.sizeOverlapThreshold = true;\r\n    }\r\n  }\r\n\r\n  private async _onPointerUp(designerView: IDesignerCanvas, event: PointerEvent) {\r\n    if (this.sizeOverlapThreshold) {\r\n      this._changeGroup.commit();\r\n      designerView.instanceServiceContainer.selectionService.setSelectedElements([this._createdItem], event);\r\n    } else {\r\n      this._changeGroup.abort();\r\n    }\r\n    designerView.overlayLayer.removeOverlay(this._rect);\r\n    this._startPosition = null;\r\n    this._rect = null;\r\n    this._createdItem = null;\r\n\r\n    designerView.serviceContainer.globalContext.finishedWithTool(this);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/DrawEllipsisTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { calculateNormLegth } from '../../../helper/PathDataPolyfill.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { InsertAction } from '../../../services/undoService/transactionItems/InsertAction.js';\nimport { IPoint } from '../../../../interfaces/IPoint.js';\nimport { hasCommandKey } from '../../../helper/KeyboardHelper.js';\nimport { roundValueToDecimalPlaces } from '../extensions/svg/geometry/GeometryWriteHelper.js';\n\r\nexport class DrawEllipsisTool implements ITool {\r\n\r\n  readonly cursor = 'crosshair';\r\n\r\n  private _path: SVGEllipseElement;\r\n  private _startPoint: IPoint;\r\n  private _radius: IPoint;\r\n  private _cx: number;\r\n  private _cy: number;\r\n\r\n  constructor() {\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n    const offset = 10;\r\n\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        this._startPoint = currentPoint;\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n\r\n        this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"ellipse\");\r\n        this._path.setAttribute(\"stroke\", designerCanvas.serviceContainer.globalContext.strokeColor);\r\n        this._path.setAttribute(\"fill\", designerCanvas.serviceContainer.globalContext.fillBrush);\r\n        this._path.setAttribute(\"stroke-width\", designerCanvas.serviceContainer.globalContext.strokeThickness);\r\n        this._path.setAttribute(\"cx\", currentPoint.x.toString());\r\n        this._path.setAttribute(\"cy\", currentPoint.y.toString());\r\n        this._path.setAttribute(\"rx\", \"0\");\r\n        this._path.setAttribute(\"ry\", \"0\");\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._path, OverlayLayer.Foreground);\r\n        break;\r\n\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._path) {\r\n          this._radius = { x: Math.abs(currentPoint.x - this._startPoint.x), y: Math.abs(currentPoint.y - this._startPoint.y) }\r\n\r\n          if (hasCommandKey(event)) {\r\n            this._path.setAttribute(\"cx\", this._startPoint.x.toString());\r\n            this._path.setAttribute(\"cy\", this._startPoint.y.toString());\r\n            this._cx = this._startPoint.x;\r\n            this._cy = this._startPoint.y;\r\n            if (event.shiftKey) {\r\n              const radius = calculateNormLegth(this._startPoint, currentPoint);\r\n              this._path.setAttribute(\"rx\", radius.toString());\r\n              this._path.setAttribute(\"ry\", radius.toString());\r\n            }\r\n            else {\r\n              this._path.setAttribute(\"rx\", this._radius.x.toString());\r\n              this._path.setAttribute(\"ry\", this._radius.y.toString());\r\n            }\r\n          }\r\n          else {\r\n            if (event.shiftKey) {\r\n              const radius = calculateNormLegth(this._startPoint, currentPoint);\r\n              this._radius = { x: radius, y: radius };\r\n            }\r\n            this._cx = currentPoint.x < this._startPoint.x ? this._startPoint.x - this._radius.x / 2 : this._startPoint.x + this._radius.x / 2;\r\n            this._cy = currentPoint.y < this._startPoint.y ? this._startPoint.y - this._radius.y / 2 : this._startPoint.y + this._radius.y / 2;\r\n            this._path.setAttribute(\"cx\", this._cx.toString());\r\n            this._path.setAttribute(\"cy\", this._cy.toString());\r\n            this._path.setAttribute(\"rx\", (this._radius.x / 2).toString());\r\n            this._path.setAttribute(\"ry\", (this._radius.y / 2).toString());\r\n          }\r\n        }\r\n        break;\r\n\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n\r\n        let coords = designerCanvas.getNormalizedElementCoordinates(this._path);\r\n        designerCanvas.overlayLayer.removeOverlay(this._path);\r\n        const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\r\n        const mvX = coords.x - offset;\n        const mvY = coords.y - offset;\n        const decimalPlaces = designerCanvas.serviceContainer.options.roundPixelsToDecimalPlaces;\n        svg.appendChild(this._path);\n        this._path.setAttribute(\"cx\", roundValueToDecimalPlaces(this._cx - mvX, decimalPlaces));\n        this._path.setAttribute(\"cy\", roundValueToDecimalPlaces(this._cy - mvY, decimalPlaces));\n        this._path.setAttribute(\"rx\", roundValueToDecimalPlaces(Number(this._path.getAttribute(\"rx\")), decimalPlaces));\n        this._path.setAttribute(\"ry\", roundValueToDecimalPlaces(Number(this._path.getAttribute(\"ry\")), decimalPlaces));\n        this._path.removeAttribute(\"stroke\");\r\n        this._path.removeAttribute(\"stroke-width\");\r\n        this._path.removeAttribute(\"overlay-source\");\r\n        svg.style.left = roundValueToDecimalPlaces(mvX, decimalPlaces) + 'px';\n        svg.style.top = roundValueToDecimalPlaces(mvY, decimalPlaces) + 'px';\n        svg.style.position = 'absolute';\r\n        svg.style.width = Math.round(coords.width + 2 * offset) + 'px';\r\n        svg.style.height = Math.round(coords.height + 2 * offset) + 'px';\r\n        svg.style.overflow = 'visible';\r\n        svg.style.stroke = designerCanvas.serviceContainer.globalContext.strokeColor;\r\n        svg.style.strokeWidth = designerCanvas.serviceContainer.globalContext.strokeThickness;\r\n        this._path = null;\r\n        const di = DesignItem.createDesignItemFromInstance(svg, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n        designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/DrawLineTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { straightenLine } from '../../../helper/PathDataPolyfill.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\nimport { InsertAction } from '../../../services/undoService/transactionItems/InsertAction.js';\nimport { IPoint } from '../../../../interfaces/IPoint.js';\nimport { roundValueToDecimalPlaces } from '../extensions/svg/geometry/GeometryWriteHelper.js';\n\r\nexport class DrawLineTool implements ITool {\r\n\r\n  readonly cursor = 'crosshair';\r\n\r\n  private _path: SVGLineElement;\r\n  private _startPoint: IPoint;\r\n  private _endPoint: IPoint;\r\n\r\n  constructor() {\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n    const offset = 10;\r\n\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        this._startPoint = currentPoint;\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n\r\n        this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"line\");\r\n        // this._pathD = \"M\" + currentPoint.x + \" \" + currentPoint.y;\r\n        // this._path.setAttribute(\"d\", this._pathD);\r\n        this._path.setAttribute(\"stroke\", designerCanvas.serviceContainer.globalContext.strokeColor);\r\n        this._path.setAttribute(\"stroke-width\", designerCanvas.serviceContainer.globalContext.strokeThickness);\r\n        this._path.setAttribute(\"x1\", currentPoint.x.toString());\r\n        this._path.setAttribute(\"y1\", currentPoint.y.toString());\r\n        this._path.setAttribute(\"x2\", currentPoint.x.toString());\r\n        this._path.setAttribute(\"y2\", currentPoint.y.toString());\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._path, OverlayLayer.Foreground);\r\n        break;\r\n\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._path) {\r\n          if (event.shiftKey) {\r\n            let straightLine = straightenLine(this._startPoint, currentPoint);\r\n            this._path.setAttribute(\"x2\", straightLine.x.toString());\r\n            this._path.setAttribute(\"y2\", straightLine.y.toString());\r\n            this._endPoint = straightLine;\r\n          }\r\n          else {\r\n            //this._path.setAttribute(\"d\", this._pathD + \"L\" + currentPoint.x + \" \" + currentPoint.y);\r\n            this._path.setAttribute(\"x2\", currentPoint.x.toString());\r\n            this._path.setAttribute(\"y2\", currentPoint.y.toString());\r\n            this._endPoint = currentPoint;\r\n          }\r\n        }\r\n        break;\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n\r\n        let coords = designerCanvas.getNormalizedElementCoordinates(this._path);\r\n        designerCanvas.overlayLayer.removeOverlay(this._path);\r\n        const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n        const mvX = coords.x - offset;\n        const mvY = coords.y - offset;\n        const decimalPlaces = designerCanvas.serviceContainer.options.roundPixelsToDecimalPlaces;\n        this._path.setAttribute(\"x1\", roundValueToDecimalPlaces(this._startPoint.x - mvX, decimalPlaces));\n        this._path.setAttribute(\"y1\", roundValueToDecimalPlaces(this._startPoint.y - mvY, decimalPlaces));\n        this._path.setAttribute(\"x2\", roundValueToDecimalPlaces(this._endPoint.x - mvX, decimalPlaces));\n        this._path.setAttribute(\"y2\", roundValueToDecimalPlaces(this._endPoint.y - mvY, decimalPlaces));\n        this._path.removeAttribute(\"stroke\");\r\n        this._path.removeAttribute(\"stroke-width\");\r\n        this._path.removeAttribute(\"overlay-source\");\r\n        svg.appendChild(this._path);\r\n        svg.style.left = roundValueToDecimalPlaces(mvX, decimalPlaces) + 'px';\n        svg.style.top = roundValueToDecimalPlaces(mvY, decimalPlaces) + 'px';\n        svg.style.position = 'absolute';\r\n        svg.style.width = Math.round(coords.width + 2 * offset) + 'px';\r\n        svg.style.height = Math.round(coords.height + 2 * offset) + 'px';\r\n        svg.style.overflow = 'visible';\r\n        svg.style.stroke = designerCanvas.serviceContainer.globalContext.strokeColor;\r\n        svg.style.strokeWidth = designerCanvas.serviceContainer.globalContext.strokeThickness;\r\n        this._path = null;\r\n        const di = DesignItem.createDesignItemFromInstance(svg, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n        designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/DrawPathTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { interpolateLinePoints, moveSVGPath, straightenLine } from '../../../helper/PathDataPolyfill.js';\r\nimport { InsertAction } from '../../../services/undoService/transactionItems/InsertAction.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\nimport { IPoint } from '../../../../interfaces/IPoint.js';\nimport { DesignerCanvas } from '../designerCanvas.js';\nimport { roundNumericParts, roundValueToDecimalPlaces } from '../extensions/svg/geometry/GeometryWriteHelper.js';\n\r\nconst offset = 10;\r\nconst freehandInterpolationDistance = 5;\r\n\r\ntype optionsType = {\r\n  angleStep?: number; // if true, lines will be straightened to the nearest angle defined by angleStep,\r\n  strokeColor?: string,\r\n  fillBrush?: string,\r\n  strokeThickness?: string,\r\n  interpolatePoints?: boolean,\r\n  interpolationDistance?: number\r\n}\r\n\r\nexport class DrawPathTool implements ITool {\r\n\r\n  readonly cursor = 'crosshair';\r\n\r\n  private _pathD?: string;\r\n  private _path?: SVGPathElement;\r\n  private _samePoint = false;\r\n  private _p2pMode = false;\r\n  private _dragMode = false;\r\n  private _pointerMoved = false;\r\n  private _eventStarted = false;\r\n  private _lastPoint?: IPoint;\r\n  private _startPoint?: IPoint;\r\n  private _captureElement?: Element;\r\n  private _pointerId?: number;\r\n  private _angleStep?: number;\r\n\r\n  constructor(private options?: optionsType) {\r\n    if (options?.angleStep !== undefined) {\r\n      this._angleStep = options.angleStep;\r\n    }\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<DesignerCanvas>designerCanvas).clickOverlay.focus();\r\n        this._eventStarted = true;\r\n\r\n        if (!this._p2pMode) {\r\n          this._captureElement = event.target as Element;\r\n          this._pointerId = event.pointerId;\r\n          this._captureElement.setPointerCapture(this._pointerId);\r\n          designerCanvas.captureActiveTool(this);\r\n\r\n          this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n          this._pathD = \"M \" + currentPoint.x + \" \" + currentPoint.y + \" \";\r\n          this._path.setAttribute(\"d\", this._pathD);\r\n          this._path.setAttribute(\"stroke\", this.options?.strokeColor ?? designerCanvas.serviceContainer.globalContext.strokeColor);\r\n          this._path.setAttribute(\"fill\", this.options?.fillBrush ?? designerCanvas.serviceContainer.globalContext.fillBrush);\r\n          this._path.setAttribute(\"stroke-width\", this.options?.strokeThickness ?? designerCanvas.serviceContainer.globalContext.strokeThickness);\r\n          designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._path, OverlayLayer.Foreground);\r\n          this._startPoint = currentPoint;\r\n        }\r\n\r\n        if (this._lastPoint != null && this._lastPoint.x === currentPoint.x && this._lastPoint.y === currentPoint.y && !this._samePoint) {\r\n          this._samePoint = true;\r\n        }\r\n        if (this._lastPoint == null) {\r\n          this._lastPoint = currentPoint;\r\n        }\r\n        if (this._startPoint == null) {\r\n          this._startPoint = currentPoint;\r\n        }\r\n        break;\r\n\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._eventStarted) {\r\n          this._pointerMoved = true;\r\n        }\r\n        if (!this._p2pMode) {\r\n          this._dragMode = true;\r\n          if (this._path && this._lastPoint) {\r\n            const points = this.options?.interpolatePoints\r\n              ? interpolateLinePoints(this._lastPoint, currentPoint, this.options.interpolationDistance ?? freehandInterpolationDistance)\r\n              : [currentPoint];\r\n\r\n            for (const point of points) {\r\n              this._pathD += \"L \" + point.x + \" \" + point.y + \" \";\r\n            }\r\n            this._path.setAttribute(\"d\", this._pathD!);\r\n            this._lastPoint = currentPoint;\r\n          }\r\n        } else {  // shows line preview\r\n          if (this._path) {\r\n            let straightLine = currentPoint;\r\n            if (event.shiftKey || this._angleStep) {\r\n              straightLine = straightenLine(this._lastPoint!, currentPoint, this._angleStep ?? 45);\r\n            }\r\n            this._path.setAttribute(\"d\", this._pathD + \"L \" + straightLine.x + \" \" + straightLine.y) + \" \";\r\n          }\r\n        }\r\n        break;\r\n\r\n\r\n      case EventNames.PointerUp:\r\n        if (this._eventStarted && !this._pointerMoved) {\r\n          this._p2pMode = true;\r\n        }\r\n        if (this._p2pMode && !this._samePoint && this._startPoint!.x != currentPoint.x && this._startPoint!.y != currentPoint.y) {\r\n          if (this._path) {\r\n            if (event.shiftKey || this._angleStep) {\r\n              let straightLine = straightenLine(this._lastPoint!, currentPoint, this._angleStep ?? 45);\r\n              this._pathD += \"L \" + straightLine.x + \" \" + straightLine.y + \" \";\r\n              this._path.setAttribute(\"d\", this._pathD!);\r\n              this._lastPoint = straightLine;\r\n            }\r\n            else {\r\n              this._pathD += \"L \" + currentPoint.x + \" \" + currentPoint.y + \" \";\r\n              this._path.setAttribute(\"d\", this._pathD!);\r\n              this._lastPoint = currentPoint;\r\n            }\r\n          }\r\n        }\r\n\r\n        if (this._samePoint && this._p2pMode || this._dragMode && !this._p2pMode) {\r\n          this._finalizePath(designerCanvas);\r\n        }\r\n        //TODO: Better Path drawing (like in SVGEDIT & Adding via Undo Framework. And adding to correct container)\r\n        break;\r\n    }\r\n    event.preventDefault();\r\n    event.stopPropagation();\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement?: Element) {\r\n    if (event.key === \"Escape\") {\r\n      this._finalizePath(designerCanvas);\r\n    }\r\n  }\r\n\r\n  private _finalizePath(designerCanvas: IDesignerCanvas) {\r\n    this._captureElement?.releasePointerCapture(this._pointerId!);\r\n    this._captureElement = undefined;\r\n    designerCanvas.releaseActiveTool();\r\n\r\n    this._eventStarted = false;\r\n    this._p2pMode = false;\r\n    this._pointerMoved = false;\r\n    this._samePoint = false;\r\n    this._dragMode = false;\r\n\r\n    let coords = designerCanvas.getNormalizedElementCoordinates(this._path!);\r\n    designerCanvas.overlayLayer.removeOverlay(this._path!);\r\n    const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n    const mvX = coords.x - offset;\n    const mvY = coords.y - offset;\n    const decimalPlaces = designerCanvas.serviceContainer.options.roundPixelsToDecimalPlaces;\n\n    this._path!.setAttribute(\"d\", this._pathD!);\n    const d = moveSVGPath(this._path!, mvX, mvY);\n    this._path!.setAttribute(\"d\", roundNumericParts(d, decimalPlaces));\n    this._path!.removeAttribute(\"stroke\");\n    this._path!.removeAttribute(\"stroke-width\");\n    this._path!.removeAttribute(\"overlay-source\");\n    svg.appendChild(this._path!);\n    svg.style.left = roundValueToDecimalPlaces(mvX, decimalPlaces) + 'px';\n    svg.style.top = roundValueToDecimalPlaces(mvY, decimalPlaces) + 'px';\n    svg.style.position = 'absolute';\r\n    svg.style.width = Math.round(coords.width + 2 * offset) + 'px';\r\n    svg.style.height = Math.round(coords.height + 2 * offset) + 'px';\r\n    svg.style.overflow = 'visible';\r\n    svg.style.stroke = this.options?.strokeColor ?? designerCanvas.serviceContainer.globalContext.strokeColor;\r\n    svg.style.fill = this.options?.fillBrush ?? designerCanvas.serviceContainer.globalContext.fillBrush;\r\n    svg.style.strokeWidth = this.options?.strokeThickness ?? designerCanvas.serviceContainer.globalContext.strokeThickness;\r\n\r\n    //designerView.rootDesignItem.element.appendChild(svg);\r\n    this._path = undefined;\r\n    this._pathD = undefined;\r\n    this._lastPoint = undefined;\r\n\r\n    const di = DesignItem.createDesignItemFromInstance(svg, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n    designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n    designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n  }\r\n\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/DrawRectTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { calculateNormLegth } from '../../../helper/PathDataPolyfill.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { InsertAction } from '../../../services/undoService/transactionItems/InsertAction.js';\nimport { IPoint } from '../../../../interfaces/IPoint.js';\nimport { hasCommandKey } from '../../../helper/KeyboardHelper.js';\nimport { roundValueToDecimalPlaces } from '../extensions/svg/geometry/GeometryWriteHelper.js';\n\r\nexport class DrawRectTool implements ITool {\r\n\r\n  readonly cursor = 'crosshair';\r\n\r\n  private _path: SVGRectElement;\r\n  private _startPoint: IPoint;\r\n  private _minX: number;\r\n  private _minY: number;\r\n  private _maxX: number;\r\n  private _maxY: number;\r\n  private _px: number;\r\n  private _py: number;\r\n\r\n  constructor() {\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n    const offset = 10;\r\n\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        this._startPoint = currentPoint;\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n\r\n        this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        this._path.setAttribute(\"stroke\", designerCanvas.serviceContainer.globalContext.strokeColor);\r\n        this._path.setAttribute(\"fill\", designerCanvas.serviceContainer.globalContext.fillBrush);\r\n        this._path.setAttribute(\"stroke-width\", designerCanvas.serviceContainer.globalContext.strokeThickness);\r\n        this._path.setAttribute(\"x\", currentPoint.x.toString());\r\n        this._path.setAttribute(\"y\", currentPoint.y.toString());\r\n        this._path.setAttribute(\"width\", \"0\");\r\n        this._path.setAttribute(\"height\", \"0\");\r\n\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._path, OverlayLayer.Foreground);\r\n        break;\r\n\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._path) {\r\n          this._minX = currentPoint.x < this._startPoint.x ? currentPoint.x : this._startPoint.x;\r\n          this._maxX = currentPoint.x > this._startPoint.x ? currentPoint.x : this._startPoint.x;\r\n          this._minY = currentPoint.y < this._startPoint.y ? currentPoint.y : this._startPoint.y;\r\n          this._maxY = currentPoint.y > this._startPoint.y ? currentPoint.y : this._startPoint.y;\r\n\r\n          if (hasCommandKey(event)) {\r\n            if (event.shiftKey) {\r\n              const normLength = 2 * calculateNormLegth(this._startPoint, currentPoint);\r\n              this._px = this._startPoint.x - normLength / 2;\r\n              this._py = this._startPoint.y - normLength / 2;\r\n              this._path.setAttribute(\"width\", (normLength).toString());\r\n              this._path.setAttribute(\"height\", (normLength).toString());\r\n            }\r\n            else {\r\n              const w = 2 * (this._maxX - this._minX);\r\n              const h = 2 * (this._maxY - this._minY);\r\n              this._px = currentPoint.x < this._startPoint.x ? currentPoint.x : this._startPoint.x - w / 2;\r\n              this._py = currentPoint.y < this._startPoint.y ? currentPoint.y : this._startPoint.y - h / 2;\r\n              this._path.setAttribute(\"width\", (w).toString());\r\n              this._path.setAttribute(\"height\", (h).toString());\r\n            }\r\n            this._path.setAttribute(\"x\", this._px.toString());\r\n            this._path.setAttribute(\"y\", this._py.toString());\r\n          }\r\n          else {\r\n            if (event.shiftKey) {\r\n              const normLength = calculateNormLegth(this._startPoint, currentPoint);\r\n              this._px = currentPoint.x < this._startPoint.x ? this._startPoint.x - normLength : this._startPoint.x;\r\n              this._py = currentPoint.y < this._startPoint.y ? this._startPoint.y - normLength : this._startPoint.y;\r\n              this._path.setAttribute(\"width\", (normLength).toString());\r\n              this._path.setAttribute(\"height\", (normLength).toString());\r\n            }\r\n            else {\r\n              this._px = currentPoint.x < this._startPoint.x ? currentPoint.x : this._startPoint.x;\r\n              this._py = currentPoint.y < this._startPoint.y ? currentPoint.y : this._startPoint.y;\r\n              this._path.setAttribute(\"width\", (this._maxX - this._minX).toString());\r\n              this._path.setAttribute(\"height\", (this._maxY - this._minY).toString());\r\n            }\r\n            this._path.setAttribute(\"x\", this._px.toString());\r\n            this._path.setAttribute(\"y\", this._py.toString());\r\n          }\r\n        }\r\n        break;\r\n\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n\r\n        let coords = designerCanvas.getNormalizedElementCoordinates(this._path);\r\n        designerCanvas.overlayLayer.removeOverlay(this._path);\r\n        const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n        const mvX = coords.x - offset;\n        const mvY = coords.y - offset;\n        const decimalPlaces = designerCanvas.serviceContainer.options.roundPixelsToDecimalPlaces;\n        this._path.setAttribute(\"x\", roundValueToDecimalPlaces(this._px - mvX, decimalPlaces));\n        this._path.setAttribute(\"y\", roundValueToDecimalPlaces(this._py - mvY, decimalPlaces));\n        this._path.setAttribute(\"width\", roundValueToDecimalPlaces(Number(this._path.getAttribute(\"width\")), decimalPlaces));\n        this._path.setAttribute(\"height\", roundValueToDecimalPlaces(Number(this._path.getAttribute(\"height\")), decimalPlaces));\n        this._path.removeAttribute(\"stroke\");\r\n        this._path.removeAttribute(\"stroke-width\");\r\n        this._path.removeAttribute(\"overlay-source\");\r\n        svg.appendChild(this._path);\r\n        svg.style.left = roundValueToDecimalPlaces(mvX, decimalPlaces) + 'px';\n        svg.style.top = roundValueToDecimalPlaces(mvY, decimalPlaces) + 'px';\n        svg.style.position = 'absolute';\r\n        svg.style.width = Math.round(coords.width + 2 * offset) + 'px';\r\n        svg.style.height = Math.round(coords.height + 2 * offset) + 'px';\r\n        svg.style.overflow = 'visible';\r\n        svg.style.stroke = designerCanvas.serviceContainer.globalContext.strokeColor;\r\n        svg.style.strokeWidth = designerCanvas.serviceContainer.globalContext.strokeThickness;\r\n        this._path = null;\r\n        const di = DesignItem.createDesignItemFromInstance(svg, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n        designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/ITool.ts",
    "content": "import { IDisposable } from '../../../../interfaces/IDisposable.js';\r\nimport { ServiceContainer } from \"../../../services/ServiceContainer.js\";\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\n\r\n//TODO: in tools dispose should be renamed, tools will be reused, so maybe cancel would be better\r\nexport interface ITool extends IDisposable {\r\n  readonly cursor: string;\r\n  activated(serviceContainer: ServiceContainer);\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element);\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement?: Element);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/MagicWandSelectorTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { hasCommandKey } from '../../../helper/KeyboardHelper.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class MagicWandSelectorTool implements ITool {\r\n  cursor: string = 'progress';\r\n\r\n  private _pathD: string;\r\n  private _path: SVGPathElement;\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    if (hasCommandKey(event))\r\n      this.cursor = 'copy';\r\n    else if (event.altKey)\r\n      this.cursor = 'default';\r\n    else\r\n      this.cursor = 'default';\r\n\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n\r\n        this._path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\r\n        this._path.setAttribute('class', 'svg-selector');\r\n        this._path.style.strokeWidth = '' + (1 / designerCanvas.scaleFactor);\r\n        this._path.style.strokeDasharray = '' + (2 / designerCanvas.scaleFactor);\r\n        this._pathD = \"M\" + currentPoint.x + \" \" + currentPoint.y;\r\n        this._path.setAttribute(\"D\", this._pathD);\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._path, OverlayLayer.Foreground);\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._path) {\r\n          this._pathD += \"L\" + currentPoint.x + \" \" + currentPoint.y;\r\n          this._path.setAttribute(\"d\", this._pathD + \"Z\");\r\n        }\r\n        break;\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n\r\n        const elements = designerCanvas.rootDesignItem.querySelectorAll('*');\r\n        const inSelectionElements: IDesignItem[] = [];\r\n\r\n        if ((hasCommandKey(event) || event.altKey) && designerCanvas.instanceServiceContainer.selectionService.selectedElements)\r\n          inSelectionElements.push(...designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n\r\n        let point: DOMPointInit = designerCanvas.overlayLayer.createPoint();\r\n        for (let e of elements) {\r\n          let elementRect = designerCanvas.getNormalizedElementCoordinates(e);\r\n          point.x = elementRect.x;\r\n          point.y = elementRect.y;\r\n          const p1 = this._path.isPointInFill(point) || this._path.isPointInStroke(point);\r\n          point.x = elementRect.x + elementRect.width;\r\n          point.y = elementRect.y;\r\n          const p2 = this._path.isPointInFill(point) || this._path.isPointInStroke(point);\r\n          point.x = elementRect.x;\r\n          point.y = elementRect.y + elementRect.height;\r\n          const p3 = this._path.isPointInFill(point) || this._path.isPointInStroke(point);\r\n          point.x = elementRect.x + elementRect.width;\r\n          point.y = elementRect.y + elementRect.height;\r\n          const p4 = this._path.isPointInFill(point) || this._path.isPointInStroke(point);\r\n          if (p1 && p2 && p3 && p4) {\r\n            const desItem = DesignItem.GetOrCreateDesignItem(e, e, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n            if (!inSelectionElements.includes(desItem) && !event.altKey) {\r\n              inSelectionElements.push(desItem);\r\n            } else if (event.altKey) {\r\n              const idx = inSelectionElements.indexOf(desItem);\r\n              inSelectionElements.splice(idx, 1)\r\n            }\r\n          }\r\n        }\r\n\r\n        designerCanvas.overlayLayer.removeOverlay(this._path);\r\n        this._path = null;\r\n        this._pathD = null;\r\n\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(inSelectionElements);\r\n\r\n        designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/MarginTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\nimport { ChangeGroup } from '../../../services/undoService/ChangeGroup.js';\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\nimport { ITool } from './ITool.js';\nimport { NamedTools } from './NamedTools.js';\n\nexport class MarginTool implements ITool {\n\n  readonly cursor: string = 'pointer';\n  private _changeGroup: ChangeGroup;\n\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n    (<ITool>designerCanvas.serviceContainer.designerTools.get(NamedTools.Pointer)).pointerEventHandler(designerCanvas, event, currentElement);\n  }\n\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) {\n    event.preventDefault();\n    const sel = designerCanvas.instanceServiceContainer.selectionService.primarySelection;\n    const cs = getComputedStyle(sel.element);\n    let nm = \"\"\n    switch (event.key) {\n      case \"ArrowLeft\":\n        nm = \"margin-left\";\n        break;\n      case \"ArrowRight\":\n        nm = \"margin-right\";\n        break;\n      case \"ArrowUp\":\n        nm = \"margin-top\";\n        break;\n      case \"ArrowDown\":\n        nm = \"margin-bottom\";\n        break;\n    }\n    if (nm) {\n      if (event.type == EventNames.KeyDown && !this._changeGroup)\n        this._changeGroup = sel.openGroup(\"change margin\");\n      if (this._changeGroup) {\n        sel.setStyleAsync(nm, (parseFloat(cs[nm]) + (event.altKey ? -1 : 1)) + \"px\");\n        if (event.type == EventNames.KeyUp) {\n          this._changeGroup.commit();\n          this._changeGroup = null;\n        }\n      }\n    }\n  }\n\n  activated(serviceContainer: ServiceContainer) {\n  }\n\n  dispose(): void {\n    if (this._changeGroup) {\n      this._changeGroup.abort();\n      this._changeGroup = null;\n    }\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/NamedTools.ts",
    "content": "export enum NamedTools {\r\n  Pointer = 'Pointer',\r\n  DrawSelection = 'DrawSelection',\r\n\r\n  DrawPath = 'DrawPath',\r\n  DrawRect = 'DrawRect',\r\n  DrawEllipsis = 'DrawEllipsis',\r\n  DrawLine = 'DrawLine',\r\n  Zoom = 'Zoom',\r\n  Pan = 'Pan',\r\n  MagicWandSelector = 'MagicWandSelector',\r\n  RectangleSelector = 'RectangleSelector',\r\n  PickColor = 'PickColor',\r\n  Text = 'Text',\r\n\r\n  DrawElementTool = \"DrawElementTool\",\r\n\r\n  Guides = 'Guides',\r\n  Inspect = 'Inspect',\r\n  Accessibility = 'Accessibility',\r\n  Position = 'Position',\r\n  Padding = \"Padding\",\r\n  Margin = 'Margin',\r\n  Flexbox = 'Flexbox',\r\n  Move = 'Move',\r\n  HueShift = 'HueShift',\r\n  BoxShadows = 'BoxShadows',\r\n  FontStyles = 'FontStyles'\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/PaddingTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\nimport { ChangeGroup } from '../../../services/undoService/ChangeGroup.js';\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\nimport { ITool } from './ITool.js';\nimport { NamedTools } from './NamedTools.js';\n\nexport class PaddingTool implements ITool {\n\n  readonly cursor: string = 'pointer';\n  private _changeGroup: ChangeGroup;\n\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n    (<ITool>designerCanvas.serviceContainer.designerTools.get(NamedTools.Pointer)).pointerEventHandler(designerCanvas, event, currentElement);\n  }\n\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) {\n    event.preventDefault();\n    const sel = designerCanvas.instanceServiceContainer.selectionService.primarySelection;\n    const cs = getComputedStyle(sel.element);\n    let nm = \"\"\n    switch (event.key) {\n      case \"ArrowLeft\":\n        nm = \"padding-left\";\n        break;\n      case \"ArrowRight\":\n        nm = \"padding-right\";\n        break;\n      case \"ArrowUp\":\n        nm = \"padding-top\";\n        break;\n      case \"ArrowDown\":\n        nm = \"padding-bottom\";\n        break;\n    }\n    if (nm) {\n      if (event.type == EventNames.KeyDown && !this._changeGroup)\n        this._changeGroup = sel.openGroup(\"change padding\");\n      if (this._changeGroup) {\n        sel.setStyleAsync(nm, (parseFloat(cs[nm]) + (event.altKey ? -1 : 1)) + \"px\");\n        if (event.type == EventNames.KeyUp) {\n          this._changeGroup.commit();\n          this._changeGroup = null;\n        }\n      }\n    }\n  }\n\n  activated(serviceContainer: ServiceContainer) {\n  }\n\n  dispose(): void {\n    if (this._changeGroup) {\n      this._changeGroup.abort();\n      this._changeGroup = null;\n    }\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/PanTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class PanTool implements ITool {\r\n\r\n  readonly cursor: string = 'grab';\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (event.buttons == 1 || event.buttons == 4) {\r\n          designerCanvas.canvasOffset = { x: designerCanvas.canvasOffset.x + event.movementX / designerCanvas.zoomFactor, y: designerCanvas.canvasOffset.y + event.movementY / designerCanvas.zoomFactor };\r\n        }\r\n        break;\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) \r\n  { }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/PickColorTool.ts",
    "content": "import { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\n\r\nexport class PickColorTool implements ITool {\r\n\r\n  readonly cursor = 'crosshair';\r\n\r\n  async activated(serviceContainer: ServiceContainer) {\r\n    try {\r\n      //@ts-ignore\r\n      const eyeDropper = new EyeDropper();\r\n      const colorSelectionResult = await eyeDropper.open();\r\n      const color = colorSelectionResult.sRGBHex;\r\n\r\n      serviceContainer.globalContext.strokeColor = color;\r\n    }\r\n    finally {\r\n      serviceContainer.globalContext.finishedWithTool(this);\r\n    }\r\n  }\r\n\r\n  async pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) \r\n  { }\r\n  \r\n  dispose(): void {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/PointerTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { PointerActionType } from '../../../../enums/PointerActionType.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { IPlacementService } from '../../../services/placementService/IPlacementService.js';\r\nimport { ExtensionType } from '../extensions/ExtensionType.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\nimport { NamedTools } from './NamedTools.js';\r\nimport { ServiceContainer } from \"../../../services/ServiceContainer.js\";\r\nimport { ChangeGroup } from '../../../services/undoService/ChangeGroup.js';\r\nimport { hasCommandKey } from '../../../helper/KeyboardHelper.js';\r\n\r\nexport class PointerTool implements ITool {\r\n\r\n  public cursor: string = 'default';\r\n\r\n  private _minMoveOffset = 5;\r\n\r\n  private _movedSinceStartedAction: boolean = false;\r\n  private _initialPoint: IPoint;\r\n  private _actionType?: PointerActionType;\r\n  private _actionStartedDesignItem?: IDesignItem;\r\n  private _actionStartedClickDesignItem?: IDesignItem;\r\n  private _actionStartedDesignItems?: IDesignItem[];\r\n  private _clonedItems?: IDesignItem[];\r\n  private _copiedItemsInserted = false;\r\n\r\n  private _previousEventName: EventNames;\r\n\r\n  private _dragOverExtensionItem: IDesignItem;\r\n  private _dragParentExtensionItem: IDesignItem;\r\n\r\n  private _moveItemsOffset: IPoint = { x: 0, y: 0 };\r\n  private _initialOffset: IPoint;\r\n  private _started: boolean = false;\r\n  private _holdTimeout: any;\r\n\r\n  private _firstTimeInMove: boolean;\r\n  private _secondTimeInMove: boolean;\r\n  private _changeGroup: ChangeGroup\r\n\r\n  constructor() {\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  private _showContextMenu(event: MouseEvent, designerCanvas: IDesignerCanvas) {\r\n    event.preventDefault();\r\n    if (!hasCommandKey(event)) {\r\n      let items = designerCanvas.elementsFromPoint(event.x, event.y);\r\n      for (let e of designerCanvas.instanceServiceContainer.selectionService.selectedElements) {\r\n        if (items.indexOf(e.element) >= 0) {\r\n          designerCanvas.showDesignItemContextMenu(designerCanvas.instanceServiceContainer.selectionService.primarySelection, event);\r\n          return;\r\n        }\r\n      }\r\n      let newEl = designerCanvas.serviceContainer.elementAtPointService.getElementAtPoint(designerCanvas, { x: event.x, y: event.y });\r\n      const designItem = DesignItem.GetOrCreateDesignItem(newEl, newEl, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n      if (!designerCanvas.instanceServiceContainer.selectionService.isSelected(designItem)) {\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([designItem], event);\r\n      }\r\n\r\n      designerCanvas.showDesignItemContextMenu(designItem, event);\r\n    }\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    if (hasCommandKey(event))\r\n      this.cursor = 'copy';\r\n    else\r\n      this.cursor = 'default';\r\n\r\n    const interactionServices = designerCanvas.serviceContainer.elementInteractionServices;\r\n    if (interactionServices)\r\n      for (let s of interactionServices) {\r\n        if (s.stopEventHandling(designerCanvas, event, currentElement))\r\n          return;\r\n      }\r\n\r\n    if (event.button == 2 && event.type == EventNames.PointerDown) {\r\n      this._showContextMenu(event, designerCanvas)\r\n      return;\r\n    }\r\n\r\n    if ((hasCommandKey(event) && event.shiftKey) || event.buttons == 4) {\r\n      const panTool = <ITool>designerCanvas.serviceContainer.designerTools.get(NamedTools.Pan);\r\n      if (panTool) {\r\n        panTool.pointerEventHandler(designerCanvas, event, currentElement);\r\n        return;\r\n      }\r\n    }\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n        this._movedSinceStartedAction = false;\r\n        this._copiedItemsInserted = false;\r\n        this._clonedItems = null;\r\n        this._firstTimeInMove = false;\r\n        this._secondTimeInMove = false;\r\n        break;\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n        this._copiedItemsInserted = false;\r\n        this._clonedItems = null;\r\n        this._firstTimeInMove = false;\r\n        this._secondTimeInMove = false;\r\n        break;\r\n      case EventNames.PointerMove:\r\n        if (this._firstTimeInMove)\r\n          this._secondTimeInMove = true;\r\n        if (this._secondTimeInMove)\r\n          this._firstTimeInMove = false;\r\n        else\r\n          this._firstTimeInMove = true;\r\n        break;\r\n    }\r\n\r\n    if (!currentElement)\r\n      return;\r\n\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n    const currentDesignItem = DesignItem.GetOrCreateDesignItem(currentElement, currentElement, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n\r\n    if (this._actionType == null) {\r\n      this._initialPoint = currentPoint;\r\n      if (event.shiftKey) {\r\n        event.preventDefault();\r\n        this._actionType = PointerActionType.DrawSelection;\r\n      } else if (event.type == EventNames.PointerDown) {\r\n        this._actionStartedDesignItem = currentDesignItem;\r\n        this._actionStartedDesignItems = [...designerCanvas.instanceServiceContainer.selectionService.selectedElements];\r\n        designerCanvas.snapLines.clearSnaplines();\r\n        if (currentDesignItem !== designerCanvas.rootDesignItem) {\r\n          this._actionType = PointerActionType.Drag;\r\n        } else if (currentElement === <any>designerCanvas || currentElement === designerCanvas.rootDesignItem.element || currentElement == null) {\r\n          //if (!hasCommandKey(event) && !event.shiftKey)\r\n          //  designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(null, event);\r\n          this._actionType = PointerActionType.DrawSelection;\r\n        } else {\r\n          this._actionType = PointerActionType.DragOrSelect;\r\n        }\r\n      }\r\n    }\r\n\r\n    if (event.type === EventNames.PointerMove) {\r\n      this._movedSinceStartedAction = this._movedSinceStartedAction || Math.abs(currentPoint.x - this._initialPoint.x) > this._minMoveOffset || Math.abs(currentPoint.y - this._initialPoint.y) > this._minMoveOffset;\r\n      if (this._actionType == PointerActionType.DrawSelection)\r\n        this._actionType = PointerActionType.DrawingSelection;\r\n    }\r\n\r\n    if (this._actionType == PointerActionType.DrawSelection || this._actionType == PointerActionType.DrawingSelection) {\r\n      this._pointerActionTypeDrawSelection(designerCanvas, event, (<HTMLElement>currentElement));\r\n    } else if (this._actionType == PointerActionType.DragOrSelect || this._actionType == PointerActionType.Drag) {\r\n      this._pointerActionTypeDragOrSelect(designerCanvas, event, currentDesignItem, currentPoint);\r\n    }\r\n    if (event.type == EventNames.PointerUp) {\r\n      designerCanvas.snapLines.clearSnaplines();\r\n      if (this._actionType == PointerActionType.DrawSelection) {\r\n        if (currentDesignItem !== designerCanvas.rootDesignItem)\r\n          designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([currentDesignItem], event);\r\n      }\r\n      this._resetTool();\r\n    }\r\n\r\n    this._previousEventName = <EventNames>event.type;\r\n  }\r\n\r\n  private _resetTool() {\r\n    this._actionType = null;\r\n    this._actionStartedDesignItem = null;\r\n    this._actionStartedClickDesignItem = null;\r\n    this._actionStartedDesignItems = null;\r\n    this._movedSinceStartedAction = false;\r\n    this._initialPoint = null;\r\n    this._initialOffset = null;\r\n  }\r\n\r\n  private _pointerActionTypeDrawSelection(designerView: IDesignerCanvas, event: PointerEvent, currentElement: HTMLElement) {\r\n    const drawSelectionTool = <ITool>designerView.serviceContainer.designerTools.get(NamedTools.DrawSelection);\r\n    if (drawSelectionTool) {\r\n      this._resetTool();\r\n      drawSelectionTool.pointerEventHandler(designerView, event, currentElement);\r\n    }\r\n  }\r\n\r\n  private async _pointerActionTypeDragOrSelect(designerCanvas: IDesignerCanvas, event: PointerEvent, currentDesignItem: IDesignItem, currentPoint: IPoint, raisedFromHold = false) {\r\n    if (this._holdTimeout) {\r\n      clearTimeout(this._holdTimeout);\r\n      this._holdTimeout = null;\r\n    }\r\n\r\n    let clickDesignItem = currentDesignItem;\r\n\r\n    if (event.altKey) {\r\n      if (event.type == EventNames.PointerDown) {\r\n        const currentSelection = designerCanvas.instanceServiceContainer.selectionService.primarySelection;\r\n        if (currentSelection) {\r\n          const elements = designerCanvas.elementsFromPoint(event.x, event.y);\r\n          let idx = elements.indexOf(currentSelection.element);\r\n          if (idx >= 0) {\r\n            idx++;\r\n          }\r\n          let currentElement = elements[idx];\r\n          if (currentElement)\r\n            currentDesignItem = DesignItem.GetOrCreateDesignItem(currentElement, currentElement, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        }\r\n      }\r\n      clickDesignItem = currentDesignItem;\r\n    } else if (event.type == EventNames.PointerDown) {\r\n      currentDesignItem = this._getDesignItemToStartDrag(designerCanvas, currentDesignItem, currentPoint);\r\n    }\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        {\r\n          this._actionStartedClickDesignItem = clickDesignItem;\r\n          this._actionStartedDesignItem = currentDesignItem;\r\n          this._initialOffset = designerCanvas.getNormalizedOffsetInElement(event, this._actionStartedDesignItem.element);\r\n\r\n          this._moveItemsOffset = { x: 0, y: 0 };\r\n\r\n          this._actionStartedDesignItems = [...designerCanvas.instanceServiceContainer.selectionService.selectedElements];\r\n\r\n          if (designerCanvas.alignOnSnap)\r\n            designerCanvas.snapLines.calculateSnaplines(designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n\r\n          break;\r\n        }\r\n      case EventNames.PointerMove:\r\n        {\r\n\r\n          if (event.buttons == 0) {\r\n            return;\r\n          }\r\n\r\n          if (this._firstTimeInMove) {\r\n            const dragDesignItem = this._actionStartedDesignItem ?? currentDesignItem;\r\n            if (!dragDesignItem.instanceServiceContainer.selectionService.selectedElements.includes(dragDesignItem)) {\r\n              if (hasCommandKey(event))\r\n                dragDesignItem.instanceServiceContainer.selectionService.setSelectedElements([...dragDesignItem.instanceServiceContainer.selectionService.selectedElements, dragDesignItem], event);\r\n              else\r\n                dragDesignItem.instanceServiceContainer.selectionService.setSelectedElements([dragDesignItem], event);\r\n              this._actionStartedDesignItems = [...designerCanvas.instanceServiceContainer.selectionService.selectedElements];\r\n              if (designerCanvas.alignOnSnap)\r\n                designerCanvas.snapLines.calculateSnaplines(designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n            }\r\n          }\r\n\r\n          if (designerCanvas.readOnly) {\r\n            return;\r\n          }\r\n\r\n          // *** Copy Items via Ctrl Drag ***\r\n\r\n          if (!this._clonedItems) {\r\n            this._clonedItems = [];\r\n            for (let d of this._actionStartedDesignItems) {\r\n              const clone = await d.clone();\r\n              if (this._clonedItems && clone)\r\n                this._clonedItems.push(clone);\r\n            }\r\n          }\r\n\r\n          if (!this._actionStartedDesignItem)\r\n            return;\r\n\r\n          if (!this._changeGroup) {\r\n            this._changeGroup = designerCanvas.rootDesignItem.openGroup(\"Move Elements\");\r\n            window.addEventListener('pointerup', () => { this._changeGroup?.abort(); this._changeGroup = null; }, { once: true });\r\n          }\r\n\r\n          if (hasCommandKey(event) && !this._copiedItemsInserted) {\r\n            this._changeGroup.title = \"Copy Elements\";\r\n            this._copiedItemsInserted = true;\r\n            for (let i = 0; i < this._clonedItems.length; i++) {\r\n              this._actionStartedDesignItems[i].insertAdjacentElement(this._clonedItems[i], 'beforebegin');\r\n            }\r\n            //TODO: check if this is needed? designerCanvas.instanceServiceContainer.onContentChanged.emit({ changeType: 'added', designItems: this._clonedItems });\r\n          } else if (!hasCommandKey(event) && this._copiedItemsInserted) {\r\n            this._changeGroup.title = \"Move Elements\";\r\n            for (let d of this._clonedItems) {\r\n              d.remove();\r\n            }\r\n            this._copiedItemsInserted = false;\r\n            //TODO: check if this is needed? designerCanvas.instanceServiceContainer.onContentChanged.emit({ changeType: 'removed', designItems: this._clonedItems });\r\n          }\r\n\r\n          // *** End Copy Items Part ***\r\n\r\n          const elementMoved = currentPoint.x != this._initialPoint.x || currentPoint.y != this._initialPoint.y;\r\n          if (this._actionType != PointerActionType.Drag && elementMoved) {\r\n            this._actionType = PointerActionType.Drag;\r\n          }\r\n\r\n          if (this._movedSinceStartedAction) {\r\n            const containerStyle = getComputedStyle(this._actionStartedDesignItem.parent.element);\r\n            const currentContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle, this._actionStartedDesignItem));\r\n            if (currentContainerService) {\r\n              const dragItem = this._actionStartedDesignItem.parent;\r\n              if (this._dragParentExtensionItem != dragItem) {\r\n                designerCanvas.extensionManager.removeExtension(this._dragParentExtensionItem, ExtensionType.ContainerDrag);\r\n                designerCanvas.extensionManager.applyExtension(dragItem, ExtensionType.ContainerDrag, event);\r\n                this._dragParentExtensionItem = dragItem;\r\n              }\r\n              else {\r\n                designerCanvas.extensionManager.refreshExtension(dragItem, ExtensionType.ContainerDrag);\r\n              }\r\n\r\n              const canLeave = currentContainerService.canLeave(this._actionStartedDesignItem.parent, this._actionStartedDesignItems);\r\n\r\n              let newContainerElementDesignItem: IDesignItem = null;\r\n              let newContainerService: IPlacementService = null;\r\n\r\n              if (canLeave) {\r\n                [newContainerElementDesignItem, newContainerService] = PointerTool.FindPossibleContainer(this._actionStartedDesignItem, this._actionStartedDesignItems, event);\r\n\r\n                //if we found a new enterable container create extensions \r\n                if (newContainerElementDesignItem != null) {\r\n                  if (this._dragOverExtensionItem != newContainerElementDesignItem) {\r\n                    designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerDragOverAndCanBeEntered);\r\n                    designerCanvas.extensionManager.applyExtension(newContainerElementDesignItem, ExtensionType.ContainerDragOverAndCanBeEntered, event);\r\n                    this._dragOverExtensionItem = newContainerElementDesignItem;\r\n                  }\r\n                  else {\r\n                    designerCanvas.extensionManager.refreshExtension(newContainerElementDesignItem, ExtensionType.ContainerDragOverAndCanBeEntered, event);\r\n                  }\r\n                } else {\r\n                  if (this._dragOverExtensionItem) {\r\n                    designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerDragOverAndCanBeEntered);\r\n                    this._dragOverExtensionItem = null;\r\n                  }\r\n                }\r\n              }\r\n\r\n              if (newContainerService) {\r\n                this._holdTimeout = setTimeout(() => {\r\n                  this._pointerActionTypeDragOrSelect(designerCanvas, event, currentDesignItem, currentPoint, true);\r\n                }, 1000);\r\n              }\r\n\r\n              if (newContainerService && (event.altKey || raisedFromHold)) {\r\n                //TODO: all items, fix position\r\n                const oldOffset = currentContainerService.getElementOffset(this._actionStartedDesignItem.parent, this._actionStartedDesignItem);\r\n                const newOffset = newContainerService.getElementOffset(newContainerElementDesignItem, this._actionStartedDesignItem);\r\n                this._moveItemsOffset = { x: newOffset.x - oldOffset.x + this._moveItemsOffset.x, y: newOffset.y - oldOffset.y + this._moveItemsOffset.y };\r\n                currentContainerService.leaveContainer(this._actionStartedDesignItem.parent, this._actionStartedDesignItems);\r\n\r\n                const cp: IPoint = { x: currentPoint.x - this._moveItemsOffset.x, y: currentPoint.y - this._moveItemsOffset.y };\r\n                newContainerService.enterContainer(newContainerElementDesignItem, this._actionStartedDesignItems, 'normal');\r\n                newContainerService.place(event, designerCanvas, this._actionStartedDesignItem.parent, this._initialPoint, this._initialOffset, cp, this._actionStartedDesignItems);\r\n\r\n                designerCanvas.extensionManager.removeExtension(this._dragParentExtensionItem, ExtensionType.ContainerDrag);\r\n                designerCanvas.extensionManager.applyExtension(newContainerElementDesignItem, ExtensionType.ContainerDrag, event);\r\n                this._dragParentExtensionItem = newContainerElementDesignItem;\r\n                designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerDragOverAndCanBeEntered);\r\n                this._dragOverExtensionItem = null;\r\n\r\n                designerCanvas.extensionManager.refreshAllAppliedExtentions();\r\n              } else {\r\n                const cp: IPoint = { x: currentPoint.x - this._moveItemsOffset.x, y: currentPoint.y - this._moveItemsOffset.y };\r\n                if (!this._started) {\r\n                  if (!currentContainerService.startPlacementAllowed || currentContainerService.startPlacementAllowed(event, designerCanvas, this._actionStartedDesignItem.parent, this._actionStartedDesignItems)) {\r\n                    for (const item of this._actionStartedDesignItems) {\r\n                      designerCanvas.extensionManager.removeExtension(item, ExtensionType.Placement);\r\n                      designerCanvas.extensionManager.removeExtension(item, ExtensionType.MouseOver);\r\n                      designerCanvas.extensionManager.applyExtension(item, ExtensionType.Placement, event);\r\n                    }\r\n                    currentContainerService.startPlace(event, designerCanvas, this._actionStartedDesignItem.parent, this._initialPoint, this._initialOffset, cp, this._actionStartedDesignItems);\r\n                    this._started = true;\r\n                  }\r\n                } else\r\n                  currentContainerService.place(event, designerCanvas, this._actionStartedDesignItem.parent, this._initialPoint, this._initialOffset, cp, this._actionStartedDesignItems);\r\n              }\r\n              designerCanvas.extensionManager.refreshExtensions(this._actionStartedDesignItems, null, event, null, 20);\r\n            }\r\n          }\r\n          break;\r\n        }\r\n      case EventNames.PointerUp:\r\n        {\r\n          this._started = false;\r\n          if (!this._movedSinceStartedAction || this._actionType == PointerActionType.DragOrSelect) {\r\n            if (this._previousEventName == EventNames.PointerDown && !hasCommandKey(event)) {\r\n              designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([this._actionStartedClickDesignItem ?? this._actionStartedDesignItem], event);\r\n            } else {\r\n              this.checkSelectElement(event, designerCanvas, currentDesignItem);\r\n            }\r\n            return;\r\n          }\r\n\r\n          if (this._movedSinceStartedAction) {\r\n            const containerStyle = getComputedStyle(this._actionStartedDesignItem.parent.element);\r\n            let containerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(this._actionStartedDesignItem.parent, containerStyle, this._actionStartedDesignItem))\r\n            const cp = { x: currentPoint.x - this._moveItemsOffset.x, y: currentPoint.y - this._moveItemsOffset.y };\r\n\r\n            if (containerService) {\r\n              if (!this._changeGroup)\r\n                this._changeGroup = designerCanvas.rootDesignItem.openGroup(\"Move Elements\");\r\n              try {\r\n                containerService.finishPlace(event, designerCanvas, this._actionStartedDesignItem.parent, this._initialPoint, this._initialOffset, cp, designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n                this._changeGroup.commit();\r\n                this._changeGroup = null;\r\n              }\r\n              catch (err) {\r\n                console.error(err);\r\n                this._changeGroup.abort();\r\n              }\r\n              this._changeGroup = null;\r\n              let elements = designerCanvas.elementsFromPoint(event.x, event.y);\r\n              for (const item of this._actionStartedDesignItems) {\r\n                if (elements.includes(item.element))\r\n                  designerCanvas.extensionManager.applyExtension(item, ExtensionType.MouseOver, event);\r\n                designerCanvas.extensionManager.removeExtension(item, ExtensionType.Placement);\r\n              }\r\n            } else {\r\n              if (this._changeGroup)\r\n                this._changeGroup.abort();\r\n              this._changeGroup = null;\r\n            }\r\n\r\n            designerCanvas.extensionManager.removeExtension(this._dragParentExtensionItem, ExtensionType.ContainerDrag);\r\n            this._dragParentExtensionItem = null;\r\n            designerCanvas.extensionManager.removeExtension(this._dragOverExtensionItem, ExtensionType.ContainerDragOverAndCanBeEntered);\r\n            this._dragOverExtensionItem = null;\r\n            this._moveItemsOffset = { x: 0, y: 0 };\r\n          }\r\n\r\n          designerCanvas.extensionManager.refreshExtensions(designerCanvas.instanceServiceContainer.selectionService.selectedElements, null, event, null, 20);\r\n\r\n          if (this._changeGroup) {\r\n            this._changeGroup.abort();\r\n            this._changeGroup = null;\r\n          }\r\n\r\n          break;\r\n        }\r\n    }\r\n  }\r\n\r\n  private _getDesignItemToStartDrag(designerCanvas: IDesignerCanvas, currentDesignItem: IDesignItem, currentPoint: IPoint): IDesignItem {\r\n    const selectedElements = designerCanvas.instanceServiceContainer.selectionService.selectedElements;\r\n    if (!selectedElements?.length || currentDesignItem.isRootItem || selectedElements.includes(currentDesignItem)) {\r\n      return currentDesignItem;\r\n    }\r\n\r\n    for (const designItem of selectedElements) {\r\n      if (!designItem || designItem.isRootItem || designItem === currentDesignItem) {\r\n        continue;\r\n      }\r\n      if (this._isPointInsideDesignItemQuad(designerCanvas, designItem, currentPoint)) {\r\n        return designItem;\r\n      }\r\n    }\r\n\r\n    return currentDesignItem;\r\n  }\r\n\r\n  private _isPointInsideDesignItemQuad(designerCanvas: IDesignerCanvas, designItem: IDesignItem, point: IPoint): boolean {\r\n    const quad = designItem.element.getBoxQuads({ box: 'border', relativeTo: designerCanvas.canvas, iframes: designerCanvas.iframes })[0];\r\n    if (!quad) {\r\n      return false;\r\n    }\r\n\r\n    return this._isPointInsideQuad(point, quad);\r\n  }\r\n\r\n  private _isPointInsideQuad(point: IPoint, quad: DOMQuad): boolean {\r\n    const points = [quad.p1, quad.p2, quad.p3, quad.p4];\r\n    for (let i = 0; i < points.length; i++) {\r\n      const start = points[i];\r\n      const end = points[(i + 1) % points.length];\r\n      if (this._isPointOnSegment(point, start, end)) {\r\n        return true;\r\n      }\r\n    }\r\n\r\n    let inside = false;\r\n    for (let i = 0, j = points.length - 1; i < points.length; j = i++) {\r\n      const start = points[i];\r\n      const end = points[j];\r\n      const intersects = ((start.y > point.y) !== (end.y > point.y)) &&\r\n        (point.x < ((end.x - start.x) * (point.y - start.y)) / ((end.y - start.y) || Number.EPSILON) + start.x);\r\n      if (intersects) {\r\n        inside = !inside;\r\n      }\r\n    }\r\n\r\n    return inside;\r\n  }\r\n\r\n  private _isPointOnSegment(point: IPoint, start: DOMPoint, end: DOMPoint): boolean {\r\n    const epsilon = 0.0001;\r\n    const crossProduct = (point.y - start.y) * (end.x - start.x) - (point.x - start.x) * (end.y - start.y);\r\n    if (Math.abs(crossProduct) > epsilon) {\r\n      return false;\r\n    }\r\n\r\n    const dotProduct = (point.x - start.x) * (end.x - start.x) + (point.y - start.y) * (end.y - start.y);\r\n    if (dotProduct < -epsilon) {\r\n      return false;\r\n    }\r\n\r\n    const squaredLength = (end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y);\r\n    return dotProduct <= squaredLength + epsilon;\r\n  }\r\n\r\n  private checkSelectElement(event: PointerEvent, designerCanvas: IDesignerCanvas, currentDesignItem: IDesignItem) {\r\n    if (hasCommandKey(event)) {\r\n      const index = designerCanvas.instanceServiceContainer.selectionService.selectedElements.indexOf(currentDesignItem);\r\n      if (index >= 0) {\r\n        let newSelectedList = designerCanvas.instanceServiceContainer.selectionService.selectedElements.slice(0);\r\n        newSelectedList.splice(index, 1);\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(newSelectedList, event);\r\n      }\r\n      else {\r\n        let newSelectedList = designerCanvas.instanceServiceContainer.selectionService.selectedElements.slice(0);\r\n        newSelectedList.push(currentDesignItem);\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(newSelectedList, event);\r\n      }\r\n    } else {\r\n      if (designerCanvas.instanceServiceContainer.selectionService.selectedElements.indexOf(currentDesignItem) < 0)\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([currentDesignItem], event);\r\n    }\r\n  }\r\n\r\n  static FindPossibleContainer(designItem: IDesignItem, designItems: IDesignItem[], event: IPoint): [newContainerElementDesignItem: IDesignItem, newContainerService: IPlacementService] {\r\n    let newContainerElementDesignItem: IDesignItem = null;\r\n    let newContainerService: IPlacementService = null;\r\n\r\n    const designerCanvas = designItem.instanceServiceContainer.designerCanvas;\r\n    const elementsFromPoint = designerCanvas.elementsFromPoint(event.x, event.y);\r\n    elementsFromPoint.push(designerCanvas.rootDesignItem.element);\r\n    for (let e of elementsFromPoint) {\r\n      if (e == designItem.element) {\r\n        continue;\r\n      } else if (e == designItem.parent.element) {\r\n        break;\r\n      } else if (e == designerCanvas.rootDesignItem.element) {\r\n        newContainerElementDesignItem = designerCanvas.rootDesignItem;\r\n        const containerStyle = getComputedStyle(newContainerElementDesignItem.element);\r\n        newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle, designItem));\r\n        if (newContainerService) {\r\n          if (newContainerService.canEnter(newContainerElementDesignItem, designItems)) {\r\n            break;\r\n          } else {\r\n            newContainerElementDesignItem = null;\r\n            newContainerService = null;\r\n            break;\r\n          }\r\n        }\r\n        break;\r\n      } else if (false) {\r\n        //check we don't try to move a item over one of its children..\r\n      } else {\r\n        newContainerElementDesignItem = DesignItem.GetOrCreateDesignItem(e, e, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        const containerStyle = getComputedStyle(newContainerElementDesignItem.element);\r\n        newContainerService = designerCanvas.serviceContainer.getLastServiceWhere('containerService', x => x.serviceForContainer(newContainerElementDesignItem, containerStyle, designItem));\r\n        if (newContainerService) {\r\n          if (newContainerService.canEnter(newContainerElementDesignItem, designItems)) {\r\n            break;\r\n          } else {\r\n            newContainerElementDesignItem = null;\r\n            newContainerService = null;\r\n            continue;\r\n          }\r\n        }\r\n      }\r\n    }\r\n    return [newContainerElementDesignItem, newContainerService];\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/RectangleSelectorTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\nimport { hasCommandKey } from '../../../helper/KeyboardHelper.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { IDesignItem } from '../../../item/IDesignItem.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class RectangleSelectorTool implements ITool {\r\n  cursor: string = 'progress';\r\n\r\n  private _rect: SVGRectElement;\r\n  private _initialPoint: IPoint;\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    if (hasCommandKey(event) || event.shiftKey)\r\n      this.cursor = 'copy';\r\n    else if (event.altKey)\r\n      this.cursor = 'default';\r\n    else\r\n      this.cursor = 'default';\r\n\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n\r\n        this._initialPoint = currentPoint;\r\n        if (!this._rect)\r\n          this._rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        this._rect.setAttribute('class', 'svg-selector');\r\n        this._rect.setAttribute('x', <string><any>(this._initialPoint.x * designerCanvas.scaleFactor));\r\n        this._rect.setAttribute('y', <string><any>(this._initialPoint.y * designerCanvas.scaleFactor));\r\n        this._rect.setAttribute('width', <string><any>0);\r\n        this._rect.setAttribute('height', <string><any>0);\r\n        this._rect.style.strokeWidth = '' + (1 / designerCanvas.scaleFactor);\r\n        this._rect.style.strokeDasharray = '' + (2 / designerCanvas.scaleFactor);\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._rect, OverlayLayer.Foreground);\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._initialPoint) {\r\n          let w = currentPoint.x - this._initialPoint.x;\r\n          let h = currentPoint.y - this._initialPoint.y;\r\n          if (w >= 0) {\r\n            this._rect.setAttribute('x', <string><any>this._initialPoint.x);\r\n            this._rect.setAttribute('width', <string><any>w);\r\n          } else {\r\n            this._rect.setAttribute('x', <string><any>currentPoint.x);\r\n            this._rect.setAttribute('width', <string><any>(-1 * w));\r\n          }\r\n          if (h >= 0) {\r\n            this._rect.setAttribute('y', <string><any>this._initialPoint.y);\r\n            this._rect.setAttribute('height', <string><any>h);\r\n          } else {\r\n            this._rect.setAttribute('y', <string><any>currentPoint.y);\r\n            this._rect.setAttribute('height', <string><any>(-1 * h));\r\n          }\r\n        }\r\n        break;\r\n\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n\r\n        const elements = designerCanvas.rootDesignItem.querySelectorAll('*');\r\n        let inSelectionElements: IDesignItem[] = [];\r\n\r\n        if ((hasCommandKey(event) || event.shiftKey || event.altKey) && designerCanvas.instanceServiceContainer.selectionService.selectedElements)\r\n          inSelectionElements.push(...designerCanvas.instanceServiceContainer.selectionService.selectedElements);\r\n\r\n        let point = designerCanvas.overlayLayer.createPoint();\r\n        for (let e of elements) {\r\n          let elementRect = designerCanvas.getNormalizedElementCoordinates(e);\r\n          point.x = elementRect.x;\r\n          point.y = elementRect.y;\r\n          const p1 = this._rect.isPointInFill(point);\r\n          point.x = elementRect.x + elementRect.width;\r\n          point.y = elementRect.y;\r\n          const p2 = p1 && this._rect.isPointInFill(point);\r\n          point.x = elementRect.x;\r\n          point.y = elementRect.y + elementRect.height;\r\n          const p3 = p2 && this._rect.isPointInFill(point);\r\n          point.x = elementRect.x + elementRect.width;\r\n          point.y = elementRect.y + elementRect.height;\r\n          const p4 = p3 && this._rect.isPointInFill(point);\r\n          if (p4) {\r\n            const desItem = DesignItem.GetOrCreateDesignItem(e, e, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n            if (!inSelectionElements.includes(desItem) && !event.altKey) {\r\n              inSelectionElements.push(desItem);\r\n            } else if (event.altKey) {\r\n              const idx = inSelectionElements.indexOf(desItem);\r\n              inSelectionElements.splice(idx, 1)\r\n            }\r\n          }\r\n        }\r\n        \r\n        designerCanvas.overlayLayer.removeOverlay(this._rect);\r\n        this._rect = null;\r\n        this._initialPoint = null;\r\n\r\n        designerCanvas.instanceServiceContainer.selectionService.setSelectedElements(inSelectionElements, event);\r\n\r\n        designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n        break;\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n\r\n  dispose(): void {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/TextTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { DesignItem } from '../../../item/DesignItem.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { InsertAction } from '../../../services/undoService/transactionItems/InsertAction.js';\r\nimport { EditTextExtension, handlesPointerEvent } from '../extensions/EditText/EditTextExtension.js';\r\nimport { IDesignerExtension } from '../extensions/IDesignerExtension.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class TextTool implements ITool {\r\n\r\n  private _textEditExtensions: IDesignerExtension[];\r\n\r\n  constructor(editExistingText?: boolean) {\r\n    if (editExistingText)\r\n      this._editExistingText = true;\r\n  }\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n    this._textCreated = false;\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  readonly cursor = 'text';\r\n\r\n  private _textCreated = false;\r\n  private _editExistingText = false;\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const currentPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        if (!this._textCreated && !this._editExistingText) {\r\n          this._textCreated = true;\r\n          const span = document.createElement('span')\r\n          const di = DesignItem.createDesignItemFromInstance(span, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n          di.setStyle('position', 'absolute');\r\n          di.setStyle('left', currentPoint.x + 'px');\r\n          di.setStyle('top', currentPoint.y + 'px');\r\n          designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n          designerCanvas.extensionManager.applyExtensionInstance(di, new EditTextExtension(designerCanvas.extensionManager, designerCanvas, di));\r\n          designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n          setTimeout(() => { span.focus(); }, 50);\r\n        } else {\r\n          for (let e of this._textEditExtensions) {\r\n            if ((<handlesPointerEvent><unknown>e).handlesPointerEvent) {\r\n              const ret = (<handlesPointerEvent><unknown>e).handlesPointerEvent(designerCanvas, event, currentElement);\r\n              if (!ret) {\r\n                designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\r\n              }\r\n            }\r\n          }\r\n        }\r\n        break;\r\n\r\n    }\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/ZoomTool.ts",
    "content": "import { EventNames } from '../../../../enums/EventNames.js';\r\nimport { IPoint } from '../../../../interfaces/IPoint.js';\r\nimport { ServiceContainer } from '../../../services/ServiceContainer.js';\r\nimport { OverlayLayer } from '../extensions/OverlayLayer.js';\r\nimport { IDesignerCanvas } from '../IDesignerCanvas.js';\r\nimport { ITool } from './ITool.js';\r\n\r\nexport class ZoomTool implements ITool {\r\n\r\n  cursor: string = 'zoom-in';\r\n\r\n  private _rect: SVGRectElement;\r\n\r\n  private _startPoint: IPoint;\r\n  private _endPoint: IPoint;\r\n\r\n  private _pointerMovementTolerance: number = 5;\r\n  private _zoomStepSize: number = 0.2; //number x 100 = Scale in percent\r\n\r\n  activated(serviceContainer: ServiceContainer) {\r\n  }\r\n\r\n  pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\r\n    const eventPoint = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n    switch (event.type) {\r\n      case EventNames.PointerDown:\r\n        (<Element>event.target).setPointerCapture(event.pointerId);\r\n        designerCanvas.captureActiveTool(this);\r\n        this._startPoint = eventPoint;\r\n        if (!this._rect)\r\n          this._rect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n        this._rect.setAttribute('class', 'svg-selector');\r\n        this._rect.setAttribute('x', <string><any>(this._startPoint.x * designerCanvas.zoomFactor));\r\n        this._rect.setAttribute('y', <string><any>(this._startPoint.y * designerCanvas.zoomFactor));\r\n        this._rect.setAttribute('width', <string><any>0);\r\n        this._rect.setAttribute('height', <string><any>0);\r\n        this._rect.style.strokeWidth = '' + (1 / designerCanvas.scaleFactor);\r\n        this._rect.style.strokeDasharray = '' + (2 / designerCanvas.scaleFactor);\r\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._rect, OverlayLayer.Foreground);\r\n        break;\r\n\r\n      case EventNames.PointerMove:\r\n        if (this._startPoint) {\r\n          let width = eventPoint.x - this._startPoint.x;\r\n          let height = eventPoint.y - this._startPoint.y;\r\n\r\n          if (width >= 0) {\r\n            this._rect.setAttribute('x', <string><any>this._startPoint.x);\r\n            this._rect.setAttribute('width', <string><any>width);\r\n          } else {\r\n            this._rect.setAttribute('x', <string><any>eventPoint.x);\r\n            this._rect.setAttribute('width', <string><any>(-1 * width));\r\n          }\r\n          if (height >= 0) {\r\n            this._rect.setAttribute('y', <string><any>this._startPoint.y);\r\n            this._rect.setAttribute('height', <string><any>height);\r\n          } else {\r\n            this._rect.setAttribute('y', <string><any>eventPoint.y);\r\n            this._rect.setAttribute('height', <string><any>(-1 * height));\r\n          }\r\n        }\r\n        break;\r\n      case EventNames.PointerUp:\r\n        (<Element>event.target).releasePointerCapture(event.pointerId);\r\n        designerCanvas.releaseActiveTool();\r\n        this._endPoint = eventPoint;\r\n        let isLeftClick: boolean = event.button == 0;\r\n        switch (event.button) {\r\n          case 0: //Left-Click\r\n          case 2: //Right-Click\r\n            this._zoomOnto(isLeftClick, this._startPoint, this._endPoint, designerCanvas);\r\n            break;\r\n        }\r\n\r\n        designerCanvas.overlayLayer.removeOverlay(this._rect);\r\n        this._rect = null;\r\n        this._startPoint = null;\r\n        break;\r\n    }\r\n\r\n  }\r\n\r\n  private _zoomOnto(isZoomInto: boolean, startPoint: IPoint, endPoint: IPoint, designerCanvas: IDesignerCanvas) {\r\n    if (this._isPositionEqual(startPoint, endPoint)) {\r\n      const oldZoom = designerCanvas.zoomFactor;\r\n      const newZoom = isZoomInto ? oldZoom + this._zoomStepSize : oldZoom - this._zoomStepSize;\r\n\r\n      designerCanvas.zoomTowardsPoint(endPoint, newZoom);\r\n    } else {\r\n      designerCanvas.zoomOntoRectangle(startPoint, endPoint);\r\n    }\r\n  }\r\n\r\n  private _isPositionEqual(startPoint: IPoint, endPoint: IPoint) {\r\n    let tolerance = this._pointerMovementTolerance;\r\n    return Math.abs(startPoint.x - endPoint.x) <= tolerance && Math.abs(startPoint.y - endPoint.y) <= tolerance;\r\n  }\r\n\r\n  keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent, currentElement: Element) { }\r\n\r\n  dispose(): void {\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/DesignerToolbar.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { CommandType } from \"../../../../../commandHandling/CommandType.js\";\r\nimport { ServiceContainer } from \"../../../../services/ServiceContainer.js\";\r\nimport { DesignerView } from \"../../designerView.js\";\r\nimport { DesignerToolbarButton } from './DesignerToolbarButton.js';\r\nimport { DraggableToolWindow } from './popups/DraggableToolWindow.js';\r\n\r\nexport class DesignerToolbar extends BaseCustomWebComponentConstructorAppend {\r\n  static override readonly style = css`\r\n        node-projects-designer-tools-buttons {\r\n            height: 100%;\r\n            width: 100%;\r\n        }        \r\n\r\n        #toolButtons {\r\n          scrollbar-width: none; /* for Firefox */\r\n          overflow-y: auto;\r\n          height: 100%;\r\n        }\r\n        #toolButtons::-webkit-scrollbar {\r\n          display: none; /* for Chrome, Safari, and Opera */\r\n        }\r\n\r\n        #popup {\r\n            position: absolute;\r\n            top: calc(0px + 10px);\r\n            height: 100%;\r\n            left: calc(24px + 4px + 10px);\r\n        }`;\r\n\r\n  static override readonly template = html`\r\n        <div id=\"popup\"></div>\r\n        <div id=\"toolButtons\"></div>`;\r\n\r\n  private _toolButtonsElem: HTMLDivElement;\r\n  private _serviceContainer: ServiceContainer;\r\n  private _popupContainer: HTMLDivElement;\r\n  public designerView: DesignerView;\r\n\r\n  constructor() {\r\n    super();\r\n    this._toolButtonsElem = this._getDomElement<HTMLDivElement>(\"toolButtons\");\r\n    this._toolButtonsElem.onwheel = (e) => e.stopPropagation();\r\n    this._popupContainer = this._getDomElement<HTMLDivElement>(\"popup\");\r\n  }\r\n\r\n  public initialize(serviceContainer: ServiceContainer, designerView: DesignerView) {\r\n    this._serviceContainer = serviceContainer;\r\n    this.designerView = designerView;\r\n\r\n    for (let tb of this._serviceContainer.designViewToolbarButtons) {\r\n      this._toolButtonsElem.appendChild(tb.provideButton(designerView.designerCanvas));\r\n    }\r\n\r\n    this._serviceContainer.globalContext.onToolChanged.on((e) => {\r\n      for (const el of this._toolButtonsElem.children) {\r\n        if (el instanceof DesignerToolbarButton) {\r\n          el.setActiveTool(e.newValue.name);\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  public showPopup(designerToolbarButton: DesignerToolbarButton) {\r\n    if (this._popupContainer.children.length) {\r\n      this._popupContainer.innerHTML = '';\r\n    }\r\n    else {\r\n      let instance: HTMLElement;\r\n      if (typeof designerToolbarButton.popup === 'string')\r\n        instance = document.createElement(designerToolbarButton.popup);\r\n      else\r\n        instance = new designerToolbarButton.popup(this.designerView.designerCanvas);\r\n      if (instance instanceof DraggableToolWindow) {\r\n        DraggableToolWindow.showWindow(instance, designerToolbarButton);\r\n      } else {\r\n        this._popupContainer.appendChild(instance);\r\n      }\r\n    }\r\n  }\r\n\r\n  public setTool(tool: string) {\r\n    if (this._popupContainer.children.length) {\r\n      this._popupContainer.innerHTML = '';\r\n    }\r\n    this.designerView.designerCanvas.executeCommand({ type: CommandType.setTool, parameter: tool });\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-designer-toolbar', DesignerToolbar);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/DesignerToolbarButton.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\r\nimport { IDesignerCanvas } from '../../IDesignerCanvas.js';\r\nimport { NamedTools } from '../NamedTools.js';\r\nimport { DesignerToolbar } from './DesignerToolbar.js';\r\n\r\nexport class DesignerToolbarButton extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override style = css`\r\n    div {\r\n      width: 24px;\r\n      height: 24px;\r\n      display: flex;\r\n      justify-content: center; \r\n      align-items: center;\r\n      background-color: inherit;\r\n    }\r\n\r\n    div:hover {\r\n      background-color: darkgray;\r\n    }\r\n\r\n    img {\r\n      width: calc(100% - 4px);\r\n      height: calc(100% - 4px);\r\n      -webkit-user-drag: none;\r\n    }    \r\n    `;\r\n\r\n  static override template = html`<div id=\"div\"><img id=\"img\"></div>`;\r\n\r\n  public tools: Record<string | NamedTools, { icon: string }>;\r\n\r\n  public popup: string | (new (designerCanvas?: IDesignerCanvas) => HTMLElement);\r\n  public currentToolOnButton: string;\r\n\r\n  private _img: HTMLImageElement;\r\n  private _div: HTMLImageElement;\r\n  private _longPress;\r\n\r\n  constructor(designerCanvas: IDesignerCanvas, tools: Record<string | NamedTools, { icon: string }>) {\r\n    super();\r\n\r\n    this.tools = tools;\r\n    this._img = this._getDomElement<HTMLImageElement>('img');\r\n    this._div = this._getDomElement<HTMLImageElement>('div');\r\n    this._div.onpointerdown = () => {\r\n      if (this.currentToolOnButton) {\r\n        (<DesignerToolbar>(<ShadowRoot>this.getRootNode()).host).setTool(this.currentToolOnButton);\r\n        setTimeout(() => {\r\n          designerCanvas.clickOverlay.focus();\r\n        }, 50);\r\n        if (this.popup) {\r\n          this._longPress = setTimeout(() => {\r\n            this._longPress = null;\r\n            (<DesignerToolbar>(<ShadowRoot>this.getRootNode()).host).showPopup(this);\r\n          }, 200)\r\n        }\r\n      }\r\n      else if (this.popup)\r\n        (<DesignerToolbar>(<ShadowRoot>this.getRootNode()).host).showPopup(this);\r\n    };\r\n\r\n    this._div.onpointerup = () => {\r\n      if (this._longPress) {\r\n        clearTimeout(this._longPress);\r\n        this._longPress = null;\r\n      }\r\n    };\r\n\r\n\r\n    this.showTool(Object.getOwnPropertyNames(tools)[0])\r\n  }\r\n\r\n  public showTool(name: string) {\r\n    const tool = this.tools[name];\r\n    if (tool) {\r\n      this._img.title = name\r\n      this._img.src = tool.icon;\r\n      this.currentToolOnButton = name;\r\n    }\r\n  }\r\n\r\n  public setActiveTool(name: string) {\r\n    this.showTool(name);\r\n    const tool = this.tools[name];\r\n    if (tool) {\r\n      this._div.style.backgroundColor = 'lightgreen';\r\n    } else {\r\n      this._div.style.backgroundColor = '';\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-designer-toolbar-button', DesignerToolbarButton);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/IDesignViewToolbarButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../IDesignerCanvas.js\";\r\n\r\nexport interface IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/DrawToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { DrawToolPopup } from \"../popups/DrawToolPopup.js\";\r\n\r\nexport class DrawToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    if (designerCanvas.readOnly)\r\n      return null;\r\n    const button = new DesignerToolbarButton(designerCanvas, {\r\n      'DrawLine': { icon: assetsPath + 'images/tools/DrawLineTool.svg' },\r\n      'DrawPath': { icon: assetsPath + 'images/tools/DrawPathTool.svg' },\r\n      'DrawRect': { icon: assetsPath + 'images/tools/DrawRectTool.svg' },\r\n      'DrawEllipsis': { icon: assetsPath + 'images/tools/DrawEllipTool.svg' },\r\n      'PickColor': { icon: assetsPath + 'images/tools/ColorPickerTool.svg' }\r\n    });\r\n    button.popup = DrawToolPopup\r\n    return button;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/PointerToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { PointerToolPopup } from \"../popups/PointerToolPopup.js\";\r\n\r\nexport class PointerToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    const button = new DesignerToolbarButton(designerCanvas, { 'Pointer': { icon: assetsPath + 'images/tools/PointerTool.svg' } });\r\n    button.popup = PointerToolPopup\r\n    return button;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/SelectorToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { SelectionToolPopup } from \"../popups/SelectionToolPopup.js\";\r\n\r\nexport class SelectorToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    const button = new DesignerToolbarButton(designerCanvas, {\r\n      'RectangleSelector': { icon: assetsPath + 'images/tools/SelectRectTool.svg' },\r\n      'MagicWandSelector': { icon: assetsPath + 'images/tools/MagicWandTool.svg' }\r\n    });\r\n    button.popup = SelectionToolPopup\r\n    return button;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/SeperatorToolProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\n\r\nexport class SeperatorToolProvider implements IDesignViewToolbarButtonProvider {\r\n  constructor(distance: number) {\r\n    this.distance = distance;\r\n  }\r\n\r\n  distance: number;\r\n\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    const div = document.createElement('div');\r\n    div.style.marginTop = this.distance + 'px';\r\n    return div;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/SimpleToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\n\r\nexport class SimpleToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  private _name: string;\r\n  private _icon: string;\r\n  constructor(name: string, icon: string) {\r\n    this._name = name;\r\n    this._icon = icon;\r\n  }\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    let obj = {};\r\n    obj[this._name] = { icon: this._icon }\r\n    return new DesignerToolbarButton(designerCanvas, obj);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/TextToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\n\r\nexport class TextToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    if (designerCanvas.readOnly)\r\n      return null;\r\n    return new DesignerToolbarButton(designerCanvas, { 'Text': { icon: assetsPath + 'images/tools/TextTool.svg' } });\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/TransformToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\nimport { assetsPath } from \"../../../../../../Constants.js\";\nimport { TransformToolPopup } from \"../popups/TransformToolPopup.js\";\n\nexport class TransformToolButtonProvider implements IDesignViewToolbarButtonProvider {\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\n    if (designerCanvas.readOnly)\n      return null;\n    const button =  new DesignerToolbarButton(designerCanvas, { '': { icon: assetsPath + 'images/tools/TransformTool.svg' } });\n    button.popup = TransformToolPopup;\n    return button;\n  }\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/buttons/ZoomToolButtonProvider.ts",
    "content": "import { IDesignerCanvas } from \"../../../IDesignerCanvas.js\";\r\nimport { IDesignViewToolbarButtonProvider } from \"../IDesignViewToolbarButtonProvider.js\";\r\nimport { DesignerToolbarButton } from '../DesignerToolbarButton.js';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\n\r\nexport class ZoomToolButtonProvider implements IDesignViewToolbarButtonProvider {\r\n  provideButton(designerCanvas: IDesignerCanvas): HTMLElement {\r\n    return new DesignerToolbarButton(designerCanvas, { 'Zoom': { icon: assetsPath + 'images/tools/ZoomTool.svg' } });\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/AbstractBaseToolPopup.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css } from '@node-projects/base-custom-webcomponent';\r\nimport { DesignerToolbar } from '../DesignerToolbar.js';\r\n\r\nexport abstract class AbstractBaseToolPopup extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override style: CSSStyleSheet | CSSStyleSheet[] = css`\r\n      .container {\r\n          width: 120px;\r\n          min-height: 100px;\r\n          color: white;\r\n          background-color: rgb(64, 64, 64);\r\n          border: 1px solid black;\r\n      }\r\n      header {\r\n          text-align: center;\r\n      }\r\n      .tool {\r\n          height: 32px;\r\n          width: 32px;\r\n          background-color: rgb(255, 255, 255);\r\n          background-size: 65%;\r\n          background-repeat: no-repeat;\r\n          background-position: center center;\r\n          flex-shrink: 0;\r\n          border-bottom: 1px solid black;\r\n      }\r\n      .tools {\r\n          display: flex;\r\n          flex-wrap: wrap;\r\n          margin-bottom: 5px;\r\n          gap: 3px;\r\n      }`\r\n\r\n  constructor() {\r\n    super();\r\n\r\n    for (let e of [...this.shadowRoot.querySelectorAll(\"div.tool\")]) {\r\n      let div = (<HTMLDivElement>e);\r\n      div.onclick = () => (<DesignerToolbar>(<ShadowRoot>this.getRootNode()).host).setTool(div.dataset['commandParameter']);\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/BorderRadiusEditorWindow.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { DraggableToolWindow } from './DraggableToolWindow.js';\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\n\ninterface BorderRadiusCorner {\n  x: number;      // horizontal radius (%)\n  y: number;      // vertical radius (%)\n  shape: 'round' | 'bevel' | 'scoop' | 'inset' | 'notch'; // corner-shape\n}\n\ninterface BorderSide {\n  width: number; // px\n  style: string; // solid, dashed, dotted, etc.\n  color: string; // hex color\n  opacity: number; // 0-100\n}\n\ninterface BorderConfig {\n  topLeft: BorderRadiusCorner;\n  topRight: BorderRadiusCorner;\n  bottomRight: BorderRadiusCorner;\n  bottomLeft: BorderRadiusCorner;\n  uniform: boolean; // if true, all corners are the same\n  // Border properties\n  width: number;  // px (uniform across all sides)\n  borderUniform: boolean; // if true, style and color are uniform\n  top: BorderSide;\n  right: BorderSide;\n  bottom: BorderSide;\n  left: BorderSide;\n}\n\nfunction defaultCorner(): BorderRadiusCorner {\n  return { x: 0, y: 0, shape: 'round' };\n}\n\nfunction defaultSide(): BorderSide {\n  return { width: 1, style: 'solid', color: '#000000', opacity: 100 };\n}\n\nfunction defaultConfig(): BorderConfig {\n  return {\n    topLeft: { ...defaultCorner() },\n    topRight: { ...defaultCorner() },\n    bottomRight: { ...defaultCorner() },\n    bottomLeft: { ...defaultCorner() },\n    uniform: true,\n    width: 1,\n    borderUniform: true,\n    top: { ...defaultSide() },\n    right: { ...defaultSide() },\n    bottom: { ...defaultSide() },\n    left: { ...defaultSide() },\n  };\n}\n\nfunction hexToRgb(hex: string): string {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `${r},${g},${b}`;\n}\n\nfunction radiusToCss(c: BorderRadiusCorner): string {\n  return c.x === c.y ? `${c.x}%` : `${c.x}% ${c.y}%`;\n}\n\nfunction configToRadiusCss(cfg: BorderConfig): string {\n  const corners = [cfg.topLeft, cfg.topRight, cfg.bottomRight, cfg.bottomLeft];\n  const xs = corners.map(c => `${c.x}%`);\n  const ys = corners.map(c => `${c.y}%`);\n\n  const allCornersSame = corners.every(c => c.x === corners[0].x && c.y === corners[0].y);\n  if (allCornersSame)\n    return radiusToCss(corners[0]);\n\n  const allCircular = corners.every(c => c.x === c.y);\n  if (allCircular)\n    return xs.join(' ');\n\n  return `${xs.join(' ')} / ${ys.join(' ')}`;\n}\n\nfunction configToShapeCss(cfg: BorderConfig): string | null {\n  const shapes = [cfg.topLeft.shape, cfg.topRight.shape, cfg.bottomRight.shape, cfg.bottomLeft.shape];\n  const allRound = shapes.every(s => s === 'round');\n  if (allRound) return null; // Don't output corner-shape if all are round (the default)\n\n  const allSame = shapes[0] === shapes[1] && shapes[1] === shapes[2] && shapes[2] === shapes[3];\n  if (allSame) return shapes[0];\n\n  return shapes.join(' ');\n}\n\nfunction configToBorderCss(cfg: BorderConfig): { width: string; style: string; color: string } {\n  const width = cfg.width > 0 ? `${cfg.width}px` : '0px';\n  \n  if (cfg.borderUniform) {\n    // Uniform border style and color\n    const { style, color, opacity } = cfg.top;\n    const rgba = `${hexToRgb(color)},${(opacity / 100).toFixed(2)}`;\n    return {\n      width,\n      style,\n      color: `rgba(${rgba})`,\n    };\n  } else {\n    // Per-side border style and color\n    const sides = [cfg.top, cfg.right, cfg.bottom, cfg.left];\n    const styles = sides.map(s => s.style);\n    const colors = sides.map(s => {\n      const rgba = `${hexToRgb(s.color)},${(s.opacity / 100).toFixed(2)}`;\n      return `rgba(${rgba})`;\n    });\n    \n    const styleCss = styles[0] === styles[1] && styles[1] === styles[2] && styles[2] === styles[3]\n      ? styles[0]\n      : styles.join(' ');\n    const colorCss = colors[0] === colors[1] && colors[1] === colors[2] && colors[2] === colors[3]\n      ? colors[0]\n      : colors.join(' ');\n    \n    return {\n      width,\n      style: styleCss,\n      color: colorCss,\n    };\n  }\n}\n\n// Parse \"10px 20px 30px 40px / 10px 20px 30px 40px\" format\n// or just \"10% 20% 30% 40%\" format\nfunction parseCornerRadius(value: string): BorderConfig {\n  const cfg = defaultConfig();\n  if (!value) return cfg;\n\n  value = value.trim();\n  if (!value) return cfg;\n\n  // Parse radius: \"X1% X2% X3% X4% / Y1% Y2% Y3% Y4%\" or simpler formats\n  const parts = value.split('/').map(p => p.trim());\n  const xStr = parts[0] ?? '';\n  const yStr = parts[1] ?? xStr; // if no Y values, use X values\n\n  const parseTokens = (str: string): number[] => {\n    return str.split(/\\s+/).map(t => {\n      const n = parseFloat(t);\n      return isNaN(n) ? 0 : n;\n    });\n  };\n\n  const xTokens = parseTokens(xStr);\n  const yTokens = parseTokens(yStr);\n\n  // Fill in corners: 1→all, 2→[TL/BR, TR/BL], 3→[TL, TR/BL, BR], 4→[TL, TR, BR, BL]\n  const getVal = (tokens: number[], idx: number): number => {\n    if (tokens.length === 1) return tokens[0];\n    if (tokens.length === 2) return tokens[idx === 1 || idx === 2 ? 1 : 0];\n    if (tokens.length === 3) return tokens[idx === 3 ? 1 : (idx === 2 ? 2 : 0)];\n    return tokens[idx] ?? 0;\n  };\n\n  cfg.topLeft.x = getVal(xTokens, 0);\n  cfg.topRight.x = getVal(xTokens, 1);\n  cfg.bottomRight.x = getVal(xTokens, 2);\n  cfg.bottomLeft.x = getVal(xTokens, 3);\n\n  cfg.topLeft.y = getVal(yTokens, 0);\n  cfg.topRight.y = getVal(yTokens, 1);\n  cfg.bottomRight.y = getVal(yTokens, 2);\n  cfg.bottomLeft.y = getVal(yTokens, 3);\n\n  return cfg;\n}\n\nfunction splitTopLevelWhitespace(value: string): string[] {\n  const tokens: string[] = [];\n  let depth = 0;\n  let current = '';\n  for (const ch of value.trim()) {\n    if (ch === '(') depth++;\n    else if (ch === ')') depth--;\n\n    if (/\\s/.test(ch) && depth === 0) {\n      const trimmed = current.trim();\n      if (trimmed) tokens.push(trimmed);\n      current = '';\n      continue;\n    }\n    current += ch;\n  }\n  const trimmed = current.trim();\n  if (trimmed) tokens.push(trimmed);\n  return tokens;\n}\n\nfunction expandQuadTokens<T>(tokens: T[]): [T | undefined, T | undefined, T | undefined, T | undefined] {\n  if (tokens.length === 1) return [tokens[0], tokens[0], tokens[0], tokens[0]];\n  if (tokens.length === 2) return [tokens[0], tokens[1], tokens[0], tokens[1]];\n  if (tokens.length === 3) return [tokens[0], tokens[1], tokens[2], tokens[1]];\n  return [tokens[0], tokens[1], tokens[2], tokens[3]];\n}\n\nexport class BorderRadiusEditorWindow extends DraggableToolWindow {\n  private _designerCanvas: IDesignerCanvas;\n  private _config: BorderConfig = defaultConfig();\n\n  private _previewBox: HTMLDivElement;\n  private _uniformCheck: HTMLInputElement;\n\n  // Unified corner controls\n  private _uniformXInput: HTMLInputElement;\n  private _uniformYInput: HTMLInputElement;\n  private _uniformShapeSelect: HTMLSelectElement;\n  private _uniformXVal: HTMLSpanElement;\n  private _uniformYVal: HTMLSpanElement;\n\n  // Top-left\n  private _tlXInput: HTMLInputElement;\n  private _tlYInput: HTMLInputElement;\n  private _tlShapeSelect: HTMLSelectElement;\n  private _tlXVal: HTMLSpanElement;\n  private _tlYVal: HTMLSpanElement;\n\n  // Top-right\n  private _trXInput: HTMLInputElement;\n  private _trYInput: HTMLInputElement;\n  private _trShapeSelect: HTMLSelectElement;\n  private _trXVal: HTMLSpanElement;\n  private _trYVal: HTMLSpanElement;\n\n  // Bottom-right\n  private _brXInput: HTMLInputElement;\n  private _brYInput: HTMLInputElement;\n  private _brShapeSelect: HTMLSelectElement;\n  private _brXVal: HTMLSpanElement;\n  private _brYVal: HTMLSpanElement;\n\n  // Bottom-left\n  private _blXInput: HTMLInputElement;\n  private _blYInput: HTMLInputElement;\n  private _blShapeSelect: HTMLSelectElement;\n  private _blXVal: HTMLSpanElement;\n  private _blYVal: HTMLSpanElement;\n\n  // Section divs\n  private _cornersUniformDiv: HTMLDivElement;\n  private _cornersPersideDiv: HTMLDivElement;\n\n  // Border controls\n  private _widthInput: HTMLInputElement;\n  private _borderUniformCheck: HTMLInputElement;\n  private _widthVal: HTMLSpanElement;\n  \n  // Uniform border controls\n  private _styleSelect: HTMLSelectElement;\n  private _colorInput: HTMLInputElement;\n  private _opacityInput: HTMLInputElement;\n  private _opacityVal: HTMLSpanElement;\n  \n  // Per-side border controls\n  private _topStyleSelect: HTMLSelectElement;\n  private _topWidthInput: HTMLInputElement;\n  private _topWidthVal: HTMLSpanElement;\n  private _topColorInput: HTMLInputElement;\n  private _topOpacityInput: HTMLInputElement;\n  private _topOpacityVal: HTMLSpanElement;\n  \n  private _rightStyleSelect: HTMLSelectElement;\n  private _rightWidthInput: HTMLInputElement;\n  private _rightWidthVal: HTMLSpanElement;\n  private _rightColorInput: HTMLInputElement;\n  private _rightOpacityInput: HTMLInputElement;\n  private _rightOpacityVal: HTMLSpanElement;\n  \n  private _bottomStyleSelect: HTMLSelectElement;\n  private _bottomWidthInput: HTMLInputElement;\n  private _bottomWidthVal: HTMLSpanElement;\n  private _bottomColorInput: HTMLInputElement;\n  private _bottomOpacityInput: HTMLInputElement;\n  private _bottomOpacityVal: HTMLSpanElement;\n  \n  private _leftStyleSelect: HTMLSelectElement;\n  private _leftWidthInput: HTMLInputElement;\n  private _leftWidthVal: HTMLSpanElement;\n  private _leftColorInput: HTMLInputElement;\n  private _leftOpacityInput: HTMLInputElement;\n  private _leftOpacityVal: HTMLSpanElement;\n  \n  private _borderUniformDiv: HTMLDivElement;\n  private _borderPersideDiv: HTMLDivElement;\n\n  private _cssOutput: HTMLTextAreaElement;\n  private _loadBtn: HTMLButtonElement;\n  private _copyBtn: HTMLButtonElement;\n  private _applyBtn: HTMLButtonElement;\n\n  protected override get windowTitle(): string { return 'Border Radius Editor'; }\n\n  protected override get windowContentStyle(): CSSStyleSheet {\n    return css`\n      * { box-sizing: border-box; }\n      .bre-root {\n        display: flex;\n        flex-direction: column;\n        width: 860px;\n        max-width: calc(100vw - 48px);\n        color: #ddd;\n        font-family: sans-serif;\n        font-size: 12px;\n        gap: 4px;\n        padding: 6px;\n        background: #2a2a2a;\n      }\n      .editors-row {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n        gap: 8px;\n        align-items: start;\n      }\n      .section-column {\n        display: flex;\n        flex-direction: column;\n        gap: 4px;\n      }\n      .preview-area {\n        background: #1a1a1a;\n        border: 1px solid #444;\n        border-radius: 3px;\n        height: 70px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        margin-bottom: 2px;\n      }\n      .preview-box {\n        width: 60px;\n        height: 60px;\n        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n        border-radius: 0;\n      }\n      .uniform-row {\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        font-size: 11px;\n      }\n      .uniform-row label {\n        display: flex;\n        align-items: center;\n        gap: 3px;\n        cursor: pointer;\n        user-select: none;\n        color: #aaa;\n      }\n      .uniform-row input[type=checkbox] {\n        cursor: pointer;\n        width: 14px;\n        height: 14px;\n      }\n      .section-header {\n        color: #999;\n        font-size: 10px;\n        font-weight: bold;\n        margin-top: 3px;\n        margin-bottom: 2px;\n        text-transform: uppercase;\n      }\n      .corner-grid {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n        gap: 4px;\n      }\n      .separate-grid {\n        display: grid;\n        grid-template-columns: repeat(2, minmax(0, 1fr));\n        gap: 4px;\n      }\n      .corner-section {\n        background: #1e1e1e;\n        border: 1px solid #444;\n        border-radius: 2px;\n        padding: 4px;\n      }\n      .corner-header {\n        color: #888;\n        font-size: 10px;\n        margin-bottom: 3px;\n        font-weight: bold;\n      }\n      .corner-row {\n        display: grid;\n        grid-template-columns: 35px 1fr 24px;\n        gap: 2px 3px;\n        align-items: center;\n        margin-bottom: 2px;\n      }\n      .corner-row:last-child { margin-bottom: 0; }\n      .corner-row label { color: #888; font-size: 10px; }\n      .corner-row input[type=range] { width: 100%; height: 16px; }\n      .corner-row input[type=color] { width: 100%; height: 18px; cursor: pointer; }\n      .corner-row .val-label { text-align: right; color: #777; font-size: 9px; }\n      .bre-root select {\n        background: #2e2e2e;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 2px;\n        padding: 2px 3px;\n        font-size: 10px;\n        height: 18px;\n      }\n      .corner-row select {\n        grid-column: 1 / -1;\n      }\n      .side-pair {\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n        gap: 4px;\n      }\n      @media (max-width: 900px) {\n        .bre-root {\n          width: 420px;\n        }\n        .editors-row {\n          grid-template-columns: 1fr;\n        }\n      }\n      .separator {\n        height: 1px;\n        background: #444;\n        margin: 2px 0;\n      }\n      .css-out {\n        background: #1e1e1e;\n        border: 1px solid #333;\n        border-radius: 3px;\n        color: #7ec8e3;\n        font-family: monospace;\n        font-size: 10px;\n        padding: 4px;\n        resize: none;\n        width: 100%;\n        height: 50px;\n      }\n      .actions {\n        display: flex;\n        gap: 4px;\n        justify-content: flex-end;\n      }\n      .actions .load-btn { margin-right: auto; }\n      .actions button, .apply-btn {\n        padding: 3px 10px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 2px;\n        cursor: pointer;\n        font-size: 11px;\n      }\n      .actions button:hover, .apply-btn:hover { background: #555; }\n      .apply-btn {\n        background: #3a6a9a;\n        border-color: #2a5a8a;\n      }\n      .apply-btn:hover { background: #4a7aaa; }\n    `;\n  }\n\n  protected override get windowTemplate(): string {\n    return `\n      <div class=\"bre-root\">\n        <div class=\"preview-area\">\n          <div class=\"preview-box\" id=\"bre-preview\"></div>\n        </div>\n\n        <div class=\"editors-row\">\n          <div class=\"section-column\">\n            <div class=\"section-header\">Corners</div>\n            <div class=\"uniform-row\">\n              <label><input type=\"checkbox\" id=\"bre-uniform\"> Separate corners</label>\n            </div>\n\n            <div id=\"bre-corners-uniform\" class=\"corner-section\">\n              <div class=\"corner-row\">\n                <label>X</label>\n                <input type=\"range\" id=\"bre-uniform-x\" min=\"0\" max=\"100\" step=\"1\">\n                <span class=\"val-label\" id=\"bre-uniform-x-val\">0%</span>\n              </div>\n              <div class=\"corner-row\">\n                <label>Y</label>\n                <input type=\"range\" id=\"bre-uniform-y\" min=\"0\" max=\"100\" step=\"1\">\n                <span class=\"val-label\" id=\"bre-uniform-y-val\">0%</span>\n              </div>\n              <select id=\"bre-uniform-shape\">\n                <option value=\"round\">Round</option>\n                <option value=\"bevel\">Bevel</option>\n                <option value=\"scoop\">Scoop</option>\n                <option value=\"inset\">Inset</option>\n                <option value=\"notch\">Notch</option>\n              </select>\n            </div>\n\n            <div id=\"bre-corners-perside\" class=\"corner-grid separate-grid\" style=\"display: none;\">\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Top-left</div>\n                <div class=\"corner-row\">\n                  <label>X</label>\n                  <input type=\"range\" id=\"bre-tl-x\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-tl-x-val\">0%</span>\n                </div>\n                <div class=\"corner-row\">\n                  <label>Y</label>\n                  <input type=\"range\" id=\"bre-tl-y\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-tl-y-val\">0%</span>\n                </div>\n                <select id=\"bre-tl-shape\">\n                  <option value=\"round\">Round</option>\n                  <option value=\"bevel\">Bevel</option>\n                  <option value=\"scoop\">Scoop</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"notch\">Notch</option>\n                </select>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Top-right</div>\n                <div class=\"corner-row\">\n                  <label>X</label>\n                  <input type=\"range\" id=\"bre-tr-x\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-tr-x-val\">0%</span>\n                </div>\n                <div class=\"corner-row\">\n                  <label>Y</label>\n                  <input type=\"range\" id=\"bre-tr-y\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-tr-y-val\">0%</span>\n                </div>\n                <select id=\"bre-tr-shape\">\n                  <option value=\"round\">Round</option>\n                  <option value=\"bevel\">Bevel</option>\n                  <option value=\"scoop\">Scoop</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"notch\">Notch</option>\n                </select>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Bottom-right</div>\n                <div class=\"corner-row\">\n                  <label>X</label>\n                  <input type=\"range\" id=\"bre-br-x\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-br-x-val\">0%</span>\n                </div>\n                <div class=\"corner-row\">\n                  <label>Y</label>\n                  <input type=\"range\" id=\"bre-br-y\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-br-y-val\">0%</span>\n                </div>\n                <select id=\"bre-br-shape\">\n                  <option value=\"round\">Round</option>\n                  <option value=\"bevel\">Bevel</option>\n                  <option value=\"scoop\">Scoop</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"notch\">Notch</option>\n                </select>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Bottom-left</div>\n                <div class=\"corner-row\">\n                  <label>X</label>\n                  <input type=\"range\" id=\"bre-bl-x\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-bl-x-val\">0%</span>\n                </div>\n                <div class=\"corner-row\">\n                  <label>Y</label>\n                  <input type=\"range\" id=\"bre-bl-y\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-bl-y-val\">0%</span>\n                </div>\n                <select id=\"bre-bl-shape\">\n                  <option value=\"round\">Round</option>\n                  <option value=\"bevel\">Bevel</option>\n                  <option value=\"scoop\">Scoop</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"notch\">Notch</option>\n                </select>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"section-column\">\n            <div class=\"section-header\">Border</div>\n            <div class=\"corner-row\">\n              <label>Width</label>\n              <input type=\"range\" id=\"bre-width\" min=\"0\" max=\"50\" step=\"1\">\n              <span class=\"val-label\" id=\"bre-width-val\">0px</span>\n            </div>\n            <div class=\"uniform-row\">\n              <label><input type=\"checkbox\" id=\"bre-border-separate\"> Separate sides</label>\n            </div>\n\n            <div id=\"bre-border-uniform-editor\" class=\"corner-section\">\n              <div class=\"corner-row\">\n                <label>Style</label>\n                <select id=\"bre-style\">\n                  <option value=\"solid\">Solid</option>\n                  <option value=\"dashed\">Dashed</option>\n                  <option value=\"dotted\">Dotted</option>\n                  <option value=\"double\">Double</option>\n                  <option value=\"groove\">Groove</option>\n                  <option value=\"ridge\">Ridge</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"outset\">Outset</option>\n                  <option value=\"none\">None</option>\n                </select>\n              </div>\n              <div class=\"corner-row\">\n                <label>Color</label>\n                <input type=\"color\" id=\"bre-color\" value=\"#000000\">\n              </div>\n              <div class=\"corner-row\">\n                <label>Opacity</label>\n                <input type=\"range\" id=\"bre-opacity\" min=\"0\" max=\"100\" step=\"1\">\n                <span class=\"val-label\" id=\"bre-opacity-val\">100%</span>\n              </div>\n            </div>\n\n            <div id=\"bre-border-perside\" class=\"corner-grid separate-grid\" style=\"display: none;\">\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Top</div>\n                <select id=\"bre-top-style\">\n                  <option value=\"solid\">Solid</option>\n                  <option value=\"dashed\">Dashed</option>\n                  <option value=\"dotted\">Dotted</option>\n                  <option value=\"double\">Double</option>\n                  <option value=\"groove\">Groove</option>\n                  <option value=\"ridge\">Ridge</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"outset\">Outset</option>\n                  <option value=\"none\">None</option>\n                </select>\n                <div class=\"corner-row\">\n                  <label>Width</label>\n                  <input type=\"range\" id=\"bre-top-width\" min=\"0\" max=\"50\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-top-width-val\">0px</span>\n                </div>\n                <input type=\"color\" id=\"bre-top-color\" value=\"#000000\" style=\"width: 100%; height: 20px; margin: 2px 0;\">\n                <div class=\"corner-row\" style=\"grid-template-columns: 1fr;\">\n                  <label style=\"grid-column: 1;\">Opacity</label>\n                  <input type=\"range\" id=\"bre-top-opacity\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-top-opacity-val\">100%</span>\n                </div>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Right</div>\n                <select id=\"bre-right-style\">\n                  <option value=\"solid\">Solid</option>\n                  <option value=\"dashed\">Dashed</option>\n                  <option value=\"dotted\">Dotted</option>\n                  <option value=\"double\">Double</option>\n                  <option value=\"groove\">Groove</option>\n                  <option value=\"ridge\">Ridge</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"outset\">Outset</option>\n                  <option value=\"none\">None</option>\n                </select>\n                <div class=\"corner-row\">\n                  <label>Width</label>\n                  <input type=\"range\" id=\"bre-right-width\" min=\"0\" max=\"50\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-right-width-val\">0px</span>\n                </div>\n                <input type=\"color\" id=\"bre-right-color\" value=\"#000000\" style=\"width: 100%; height: 20px; margin: 2px 0;\">\n                <div class=\"corner-row\" style=\"grid-template-columns: 1fr;\">\n                  <label style=\"grid-column: 1;\">Opacity</label>\n                  <input type=\"range\" id=\"bre-right-opacity\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-right-opacity-val\">100%</span>\n                </div>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Bottom</div>\n                <select id=\"bre-bottom-style\">\n                  <option value=\"solid\">Solid</option>\n                  <option value=\"dashed\">Dashed</option>\n                  <option value=\"dotted\">Dotted</option>\n                  <option value=\"double\">Double</option>\n                  <option value=\"groove\">Groove</option>\n                  <option value=\"ridge\">Ridge</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"outset\">Outset</option>\n                  <option value=\"none\">None</option>\n                </select>\n                <div class=\"corner-row\">\n                  <label>Width</label>\n                  <input type=\"range\" id=\"bre-bottom-width\" min=\"0\" max=\"50\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-bottom-width-val\">0px</span>\n                </div>\n                <input type=\"color\" id=\"bre-bottom-color\" value=\"#000000\" style=\"width: 100%; height: 20px; margin: 2px 0;\">\n                <div class=\"corner-row\" style=\"grid-template-columns: 1fr;\">\n                  <label style=\"grid-column: 1;\">Opacity</label>\n                  <input type=\"range\" id=\"bre-bottom-opacity\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-bottom-opacity-val\">100%</span>\n                </div>\n              </div>\n\n              <div class=\"corner-section\">\n                <div class=\"corner-header\">Left</div>\n                <select id=\"bre-left-style\">\n                  <option value=\"solid\">Solid</option>\n                  <option value=\"dashed\">Dashed</option>\n                  <option value=\"dotted\">Dotted</option>\n                  <option value=\"double\">Double</option>\n                  <option value=\"groove\">Groove</option>\n                  <option value=\"ridge\">Ridge</option>\n                  <option value=\"inset\">Inset</option>\n                  <option value=\"outset\">Outset</option>\n                  <option value=\"none\">None</option>\n                </select>\n                <div class=\"corner-row\">\n                  <label>Width</label>\n                  <input type=\"range\" id=\"bre-left-width\" min=\"0\" max=\"50\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-left-width-val\">0px</span>\n                </div>\n                <input type=\"color\" id=\"bre-left-color\" value=\"#000000\" style=\"width: 100%; height: 20px; margin: 2px 0;\">\n                <div class=\"corner-row\" style=\"grid-template-columns: 1fr;\">\n                  <label style=\"grid-column: 1;\">Opacity</label>\n                  <input type=\"range\" id=\"bre-left-opacity\" min=\"0\" max=\"100\" step=\"1\">\n                  <span class=\"val-label\" id=\"bre-left-opacity-val\">100%</span>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <div class=\"separator\"></div>\n\n        <textarea class=\"css-out\" id=\"bre-css-out\"></textarea>\n\n        <div class=\"actions\">\n          <button class=\"load-btn\" id=\"bre-load-btn\">Load</button>\n          <button id=\"bre-copy-btn\">Copy CSS</button>\n          <button class=\"apply-btn\" id=\"bre-apply-btn\">Apply to selection</button>\n        </div>\n      </div>`;\n  }\n\n  constructor(designerCanvas?: IDesignerCanvas) {\n    super();\n    this._designerCanvas = designerCanvas;\n\n    this._previewBox = this._getDomElement<HTMLDivElement>('bre-preview');\n    this._uniformCheck = this._getDomElement<HTMLInputElement>('bre-uniform');\n\n    // Unified corner controls\n    this._uniformXInput = this._getDomElement<HTMLInputElement>('bre-uniform-x');\n    this._uniformYInput = this._getDomElement<HTMLInputElement>('bre-uniform-y');\n    this._uniformShapeSelect = this._getDomElement<HTMLSelectElement>('bre-uniform-shape');\n    this._uniformXVal = this._getDomElement<HTMLSpanElement>('bre-uniform-x-val');\n    this._uniformYVal = this._getDomElement<HTMLSpanElement>('bre-uniform-y-val');\n\n    // Top-left\n    this._tlXInput = this._getDomElement<HTMLInputElement>('bre-tl-x');\n    this._tlYInput = this._getDomElement<HTMLInputElement>('bre-tl-y');\n    this._tlShapeSelect = this._getDomElement<HTMLSelectElement>('bre-tl-shape');\n    this._tlXVal = this._getDomElement<HTMLSpanElement>('bre-tl-x-val');\n    this._tlYVal = this._getDomElement<HTMLSpanElement>('bre-tl-y-val');\n\n    // Top-right\n    this._trXInput = this._getDomElement<HTMLInputElement>('bre-tr-x');\n    this._trYInput = this._getDomElement<HTMLInputElement>('bre-tr-y');\n    this._trShapeSelect = this._getDomElement<HTMLSelectElement>('bre-tr-shape');\n    this._trXVal = this._getDomElement<HTMLSpanElement>('bre-tr-x-val');\n    this._trYVal = this._getDomElement<HTMLSpanElement>('bre-tr-y-val');\n\n    // Bottom-right\n    this._brXInput = this._getDomElement<HTMLInputElement>('bre-br-x');\n    this._brYInput = this._getDomElement<HTMLInputElement>('bre-br-y');\n    this._brShapeSelect = this._getDomElement<HTMLSelectElement>('bre-br-shape');\n    this._brXVal = this._getDomElement<HTMLSpanElement>('bre-br-x-val');\n    this._brYVal = this._getDomElement<HTMLSpanElement>('bre-br-y-val');\n\n    // Bottom-left\n    this._blXInput = this._getDomElement<HTMLInputElement>('bre-bl-x');\n    this._blYInput = this._getDomElement<HTMLInputElement>('bre-bl-y');\n    this._blShapeSelect = this._getDomElement<HTMLSelectElement>('bre-bl-shape');\n    this._blXVal = this._getDomElement<HTMLSpanElement>('bre-bl-x-val');\n    this._blYVal = this._getDomElement<HTMLSpanElement>('bre-bl-y-val');\n\n    // Section divs\n    this._cornersUniformDiv = this._getDomElement<HTMLDivElement>('bre-corners-uniform');\n    this._cornersPersideDiv = this._getDomElement<HTMLDivElement>('bre-corners-perside');\n\n    // Border controls\n    this._widthInput = this._getDomElement<HTMLInputElement>('bre-width');\n    this._borderUniformCheck = this._getDomElement<HTMLInputElement>('bre-border-separate');\n    this._widthVal = this._getDomElement<HTMLSpanElement>('bre-width-val');\n    \n    // Uniform border controls\n    this._styleSelect = this._getDomElement<HTMLSelectElement>('bre-style');\n    this._colorInput = this._getDomElement<HTMLInputElement>('bre-color');\n    this._opacityInput = this._getDomElement<HTMLInputElement>('bre-opacity');\n    this._opacityVal = this._getDomElement<HTMLSpanElement>('bre-opacity-val');\n    \n    // Per-side border controls\n    this._topStyleSelect = this._getDomElement<HTMLSelectElement>('bre-top-style');\n    this._topWidthInput = this._getDomElement<HTMLInputElement>('bre-top-width');\n    this._topWidthVal = this._getDomElement<HTMLSpanElement>('bre-top-width-val');\n    this._topColorInput = this._getDomElement<HTMLInputElement>('bre-top-color');\n    this._topOpacityInput = this._getDomElement<HTMLInputElement>('bre-top-opacity');\n    this._topOpacityVal = this._getDomElement<HTMLSpanElement>('bre-top-opacity-val');\n    \n    this._rightStyleSelect = this._getDomElement<HTMLSelectElement>('bre-right-style');\n    this._rightWidthInput = this._getDomElement<HTMLInputElement>('bre-right-width');\n    this._rightWidthVal = this._getDomElement<HTMLSpanElement>('bre-right-width-val');\n    this._rightColorInput = this._getDomElement<HTMLInputElement>('bre-right-color');\n    this._rightOpacityInput = this._getDomElement<HTMLInputElement>('bre-right-opacity');\n    this._rightOpacityVal = this._getDomElement<HTMLSpanElement>('bre-right-opacity-val');\n    \n    this._bottomStyleSelect = this._getDomElement<HTMLSelectElement>('bre-bottom-style');\n    this._bottomWidthInput = this._getDomElement<HTMLInputElement>('bre-bottom-width');\n    this._bottomWidthVal = this._getDomElement<HTMLSpanElement>('bre-bottom-width-val');\n    this._bottomColorInput = this._getDomElement<HTMLInputElement>('bre-bottom-color');\n    this._bottomOpacityInput = this._getDomElement<HTMLInputElement>('bre-bottom-opacity');\n    this._bottomOpacityVal = this._getDomElement<HTMLSpanElement>('bre-bottom-opacity-val');\n    \n    this._leftStyleSelect = this._getDomElement<HTMLSelectElement>('bre-left-style');\n    this._leftWidthInput = this._getDomElement<HTMLInputElement>('bre-left-width');\n    this._leftWidthVal = this._getDomElement<HTMLSpanElement>('bre-left-width-val');\n    this._leftColorInput = this._getDomElement<HTMLInputElement>('bre-left-color');\n    this._leftOpacityInput = this._getDomElement<HTMLInputElement>('bre-left-opacity');\n    this._leftOpacityVal = this._getDomElement<HTMLSpanElement>('bre-left-opacity-val');\n    \n    this._borderUniformDiv = this._getDomElement<HTMLDivElement>('bre-border-uniform-editor');\n    this._borderPersideDiv = this._getDomElement<HTMLDivElement>('bre-border-perside');\n\n    this._cssOutput = this._getDomElement<HTMLTextAreaElement>('bre-css-out');\n    this._loadBtn = this._getDomElement<HTMLButtonElement>('bre-load-btn');\n    this._copyBtn = this._getDomElement<HTMLButtonElement>('bre-copy-btn');\n    this._applyBtn = this._getDomElement<HTMLButtonElement>('bre-apply-btn');\n\n    // Uniform mode for corners\n    this._uniformCheck.onchange = () => {\n      this._config.uniform = !this._uniformCheck.checked;\n      this._cornersUniformDiv.style.display = this._config.uniform ? 'block' : 'none';\n      this._cornersPersideDiv.style.display = this._config.uniform ? 'none' : 'grid';\n      if (this._config.uniform) {\n        const tl = this._config.topLeft;\n        this._config.topRight = { ...tl };\n        this._config.bottomRight = { ...tl };\n        this._config.bottomLeft = { ...tl };\n      }\n      this._syncControls();\n      this._refresh();\n    };\n\n    // Wire up unified corner controls\n    this._uniformXInput.oninput = () => {\n      this._config.topLeft.x = Number(this._uniformXInput.value);\n      this._config.topRight.x = Number(this._uniformXInput.value);\n      this._config.bottomRight.x = Number(this._uniformXInput.value);\n      this._config.bottomLeft.x = Number(this._uniformXInput.value);\n      this._uniformXVal.textContent = this._config.topLeft.x + '%';\n      this._refresh();\n    };\n    this._uniformYInput.oninput = () => {\n      this._config.topLeft.y = Number(this._uniformYInput.value);\n      this._config.topRight.y = Number(this._uniformYInput.value);\n      this._config.bottomRight.y = Number(this._uniformYInput.value);\n      this._config.bottomLeft.y = Number(this._uniformYInput.value);\n      this._uniformYVal.textContent = this._config.topLeft.y + '%';\n      this._refresh();\n    };\n    this._uniformShapeSelect.onchange = () => {\n      const shape = this._uniformShapeSelect.value as any;\n      this._config.topLeft.shape = shape;\n      this._config.topRight.shape = shape;\n      this._config.bottomRight.shape = shape;\n      this._config.bottomLeft.shape = shape;\n      this._refresh();\n    };\n\n    // Wire up per-corner controls: Top-left\n    this._wireCornerControls('tl', this._tlXInput, this._tlYInput, this._tlShapeSelect, this._tlXVal, this._tlYVal);\n    this._wireCornerControls('tr', this._trXInput, this._trYInput, this._trShapeSelect, this._trXVal, this._trYVal);\n    this._wireCornerControls('br', this._brXInput, this._brYInput, this._brShapeSelect, this._brXVal, this._brYVal);\n    this._wireCornerControls('bl', this._blXInput, this._blYInput, this._blShapeSelect, this._blXVal, this._blYVal);\n\n    // Wire up border controls\n    this._widthInput.oninput = () => {\n      this._config.width = Number(this._widthInput.value);\n      this._config.top.width = this._config.width;\n      this._config.right.width = this._config.width;\n      this._config.bottom.width = this._config.width;\n      this._config.left.width = this._config.width;\n      this._widthVal.textContent = this._config.width + 'px';\n      this._refresh();\n    };\n    \n    this._borderUniformCheck.onchange = () => {\n      this._config.borderUniform = !this._borderUniformCheck.checked;\n      if (this._config.borderUniform) {\n        this._config.width = this._config.top.width;\n        this._config.right.width = this._config.top.width;\n        this._config.bottom.width = this._config.top.width;\n        this._config.left.width = this._config.top.width;\n      }\n      this._borderUniformDiv.style.display = this._config.borderUniform ? 'block' : 'none';\n      this._borderPersideDiv.style.display = this._config.borderUniform ? 'none' : 'grid';\n      this._syncControls();\n      this._refresh();\n    };\n    \n    // Uniform mode handlers for border\n    this._styleSelect.onchange = () => {\n      this._config.top.style = this._styleSelect.value;\n      this._config.right.style = this._styleSelect.value;\n      this._config.bottom.style = this._styleSelect.value;\n      this._config.left.style = this._styleSelect.value;\n      this._refresh();\n    };\n    this._colorInput.oninput = () => {\n      this._config.top.color = this._colorInput.value;\n      this._config.right.color = this._colorInput.value;\n      this._config.bottom.color = this._colorInput.value;\n      this._config.left.color = this._colorInput.value;\n      this._refresh();\n    };\n    this._opacityInput.oninput = () => {\n      const opacity = Number(this._opacityInput.value);\n      this._config.top.opacity = opacity;\n      this._config.right.opacity = opacity;\n      this._config.bottom.opacity = opacity;\n      this._config.left.opacity = opacity;\n      this._opacityVal.textContent = opacity + '%';\n      this._refresh();\n    };\n    \n    // Per-side handlers\n    const wirePerSideBorderControls = (side: 'top' | 'right' | 'bottom' | 'left', \n      styleSelect: HTMLSelectElement,\n      widthInput: HTMLInputElement,\n      widthVal: HTMLSpanElement,\n      colorInput: HTMLInputElement,\n      opacityInput: HTMLInputElement,\n      opacityVal: HTMLSpanElement) => {\n      const getSide = (): BorderSide => this._config[side];\n      styleSelect.onchange = () => {\n        const sideCfg = getSide();\n        sideCfg.style = styleSelect.value;\n        this._refresh();\n      };\n      widthInput.oninput = () => {\n        const sideCfg = getSide();\n        sideCfg.width = Number(widthInput.value);\n        widthVal.textContent = sideCfg.width + 'px';\n        this._refresh();\n      };\n      colorInput.oninput = () => {\n        const sideCfg = getSide();\n        sideCfg.color = colorInput.value;\n        this._refresh();\n      };\n      opacityInput.oninput = () => {\n        const sideCfg = getSide();\n        sideCfg.opacity = Number(opacityInput.value);\n        opacityVal.textContent = sideCfg.opacity + '%';\n        this._refresh();\n      };\n    };\n    \n    wirePerSideBorderControls('top', this._topStyleSelect, this._topWidthInput, this._topWidthVal, this._topColorInput, this._topOpacityInput, this._topOpacityVal);\n    wirePerSideBorderControls('right', this._rightStyleSelect, this._rightWidthInput, this._rightWidthVal, this._rightColorInput, this._rightOpacityInput, this._rightOpacityVal);\n    wirePerSideBorderControls('bottom', this._bottomStyleSelect, this._bottomWidthInput, this._bottomWidthVal, this._bottomColorInput, this._bottomOpacityInput, this._bottomOpacityVal);\n    wirePerSideBorderControls('left', this._leftStyleSelect, this._leftWidthInput, this._leftWidthVal, this._leftColorInput, this._leftOpacityInput, this._leftOpacityVal);\n\n    // Copy / Apply\n    this._copyBtn.onclick = () => { navigator.clipboard?.writeText(this._cssOutput.value).catch(() => {}); };\n    this._applyBtn.onclick = () => this._applyToSelection();\n    this._loadBtn.onclick = () => {\n      this._loadFromPrimarySelection();\n      this._syncControls();\n      this._refresh();\n    };\n\n    this._loadFromPrimarySelection();\n    this._syncControls();\n    this._refresh();\n  }\n\n  private _wireCornerControls(\n    cornerKey: 'tl' | 'tr' | 'br' | 'bl',\n    xInput: HTMLInputElement,\n    yInput: HTMLInputElement,\n    shapeSelect: HTMLSelectElement,\n    xVal: HTMLSpanElement,\n    yVal: HTMLSpanElement\n  ) {\n    const getCorner = (): BorderRadiusCorner => {\n      switch (cornerKey) {\n        case 'tl': return this._config.topLeft;\n        case 'tr': return this._config.topRight;\n        case 'br': return this._config.bottomRight;\n        case 'bl': return this._config.bottomLeft;\n      }\n    };\n\n    xInput.oninput = () => {\n      const liveCorner = getCorner();\n      liveCorner.x = Number(xInput.value);\n      if (this._config.uniform) {\n        this._config.topLeft.x = this._config.topRight.x = this._config.bottomRight.x = this._config.bottomLeft.x = liveCorner.x;\n      }\n      xVal.textContent = liveCorner.x + '%';\n      this._refresh();\n    };\n    yInput.oninput = () => {\n      const liveCorner = getCorner();\n      liveCorner.y = Number(yInput.value);\n      if (this._config.uniform) {\n        this._config.topLeft.y = this._config.topRight.y = this._config.bottomRight.y = this._config.bottomLeft.y = liveCorner.y;\n      }\n      yVal.textContent = liveCorner.y + '%';\n      this._refresh();\n    };\n    shapeSelect.onchange = () => {\n      const liveCorner = getCorner();\n      liveCorner.shape = shapeSelect.value as BorderRadiusCorner['shape'];\n      if (this._config.uniform) {\n        this._config.topLeft.shape = this._config.topRight.shape = this._config.bottomRight.shape = this._config.bottomLeft.shape = liveCorner.shape;\n      }\n      this._refresh();\n    };\n  }\n\n  private _loadFromPrimarySelection() {\n    const primary = this._designerCanvas?.instanceServiceContainer?.selectionService?.primarySelection;\n    if (!primary) return;\n    const br = primary.getStyle('border-radius');\n    if (br) {\n      this._config = parseCornerRadius(br);\n    }\n    // Also load corner-shape property if it exists\n    const cs = primary.getStyle('corner-shape');\n    if (cs) {\n      const shapes = cs.trim().split(/\\s+/).filter(Boolean) as ('round' | 'bevel' | 'scoop' | 'inset' | 'notch')[];\n      const getShape = (idx: number): BorderRadiusCorner['shape'] => {\n        if (shapes.length === 1) return shapes[0];\n        if (shapes.length === 2) return shapes[idx === 1 || idx === 2 ? 1 : 0];\n        if (shapes.length === 3) return shapes[idx === 3 ? 1 : (idx === 2 ? 2 : 0)];\n        return shapes[idx] ?? 'round';\n      };\n      this._config.topLeft.shape = getShape(0);\n      this._config.topRight.shape = getShape(1);\n      this._config.bottomRight.shape = getShape(2);\n      this._config.bottomLeft.shape = getShape(3);\n    }\n\n    const corners = [this._config.topLeft, this._config.topRight, this._config.bottomRight, this._config.bottomLeft];\n    this._config.uniform = corners.every(c =>\n      c.x === corners[0].x &&\n      c.y === corners[0].y &&\n      c.shape === corners[0].shape\n    );\n    // Load border width\n    const bw = primary.getStyle('border-width');\n    if (bw) {\n      const widthTokens = splitTopLevelWhitespace(bw).map(t => parseFloat(t)).filter(v => !isNaN(v));\n      const [top, right, bottom, left] = expandQuadTokens(widthTokens);\n      if (top != null) this._config.top.width = top;\n      if (right != null) this._config.right.width = right;\n      if (bottom != null) this._config.bottom.width = bottom;\n      if (left != null) this._config.left.width = left;\n      if (top != null) this._config.width = top;\n    }\n\n    const topWidth = primary.getStyle('border-top-width');\n    const rightWidth = primary.getStyle('border-right-width');\n    const bottomWidth = primary.getStyle('border-bottom-width');\n    const leftWidth = primary.getStyle('border-left-width');\n    const hasPerSideWidth = !!(topWidth || rightWidth || bottomWidth || leftWidth);\n    if (hasPerSideWidth) {\n      this._config.borderUniform = false;\n      if (topWidth) {\n        const widthNum = parseFloat(topWidth);\n        if (!isNaN(widthNum)) this._config.top.width = widthNum;\n      }\n      if (rightWidth) {\n        const widthNum = parseFloat(rightWidth);\n        if (!isNaN(widthNum)) this._config.right.width = widthNum;\n      }\n      if (bottomWidth) {\n        const widthNum = parseFloat(bottomWidth);\n        if (!isNaN(widthNum)) this._config.bottom.width = widthNum;\n      }\n      if (leftWidth) {\n        const widthNum = parseFloat(leftWidth);\n        if (!isNaN(widthNum)) this._config.left.width = widthNum;\n      }\n      this._config.width = this._config.top.width;\n    }\n    \n    // Load border style and color (per-side or uniform)\n    const parseColorAndOpacity = (colorStr: string): { hex: string; opacity: number } => {\n      const rgbaMatch = colorStr.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/);\n      if (rgbaMatch) {\n        const hex = '#' + [parseInt(rgbaMatch[1]), parseInt(rgbaMatch[2]), parseInt(rgbaMatch[3])].map(v => v.toString(16).padStart(2, '0')).join('');\n        const opacity = rgbaMatch[4] ? Math.round(parseFloat(rgbaMatch[4]) * 100) : 100;\n        return { hex, opacity };\n      } else if (colorStr.startsWith('#')) {\n        return { hex: colorStr.slice(0, 7), opacity: 100 };\n      }\n      return { hex: '#000000', opacity: 100 };\n    };\n    \n    // Try to load per-side styles\n    const topStyle = primary.getStyle('border-top-style');\n    const rightStyle = primary.getStyle('border-right-style');\n    const bottomStyle = primary.getStyle('border-bottom-style');\n    const leftStyle = primary.getStyle('border-left-style');\n    \n    if (topStyle || rightStyle || bottomStyle || leftStyle) {\n      // Per-side mode\n      this._config.borderUniform = false;\n      if (topStyle) this._config.top.style = topStyle;\n      if (rightStyle) this._config.right.style = rightStyle;\n      if (bottomStyle) this._config.bottom.style = bottomStyle;\n      if (leftStyle) this._config.left.style = leftStyle;\n    } else {\n      // Try uniform shorthand\n      const bs = primary.getStyle('border-style');\n      if (bs) {\n        if (!hasPerSideWidth)\n          this._config.borderUniform = true;\n        const [top, right, bottom, left] = expandQuadTokens(splitTopLevelWhitespace(bs));\n        if (top) this._config.top.style = top;\n        if (right) this._config.right.style = right;\n        if (bottom) this._config.bottom.style = bottom;\n        if (left) this._config.left.style = left;\n      }\n    }\n    \n    // Try to load per-side colors\n    const topColor = primary.getStyle('border-top-color');\n    const rightColor = primary.getStyle('border-right-color');\n    const bottomColor = primary.getStyle('border-bottom-color');\n    const leftColor = primary.getStyle('border-left-color');\n    \n    if (topColor || rightColor || bottomColor || leftColor) {\n      // Per-side mode\n      this._config.borderUniform = false;\n      if (topColor) {\n        const parsed = parseColorAndOpacity(topColor);\n        this._config.top.color = parsed.hex;\n        this._config.top.opacity = parsed.opacity;\n      }\n      if (rightColor) {\n        const parsed = parseColorAndOpacity(rightColor);\n        this._config.right.color = parsed.hex;\n        this._config.right.opacity = parsed.opacity;\n      }\n      if (bottomColor) {\n        const parsed = parseColorAndOpacity(bottomColor);\n        this._config.bottom.color = parsed.hex;\n        this._config.bottom.opacity = parsed.opacity;\n      }\n      if (leftColor) {\n        const parsed = parseColorAndOpacity(leftColor);\n        this._config.left.color = parsed.hex;\n        this._config.left.opacity = parsed.opacity;\n      }\n    } else {\n      // Try uniform shorthand\n      const bc = primary.getStyle('border-color');\n      if (bc) {\n        if (!hasPerSideWidth)\n          this._config.borderUniform = true;\n        const [top, right, bottom, left] = expandQuadTokens(splitTopLevelWhitespace(bc));\n        if (top) {\n          const parsed = parseColorAndOpacity(top);\n          this._config.top.color = parsed.hex;\n          this._config.top.opacity = parsed.opacity;\n        }\n        if (right) {\n          const parsed = parseColorAndOpacity(right);\n          this._config.right.color = parsed.hex;\n          this._config.right.opacity = parsed.opacity;\n        }\n        if (bottom) {\n          const parsed = parseColorAndOpacity(bottom);\n          this._config.bottom.color = parsed.hex;\n          this._config.bottom.opacity = parsed.opacity;\n        }\n        if (left) {\n          const parsed = parseColorAndOpacity(left);\n          this._config.left.color = parsed.hex;\n          this._config.left.opacity = parsed.opacity;\n        }\n      }\n    }\n\n    const sides = [this._config.top, this._config.right, this._config.bottom, this._config.left];\n    this._config.borderUniform = sides.every(s =>\n      s.width === sides[0].width &&\n      s.style === sides[0].style &&\n      s.color === sides[0].color &&\n      s.opacity === sides[0].opacity\n    );\n    this._config.width = this._config.top.width;\n  }\n\n  private _syncControls() {\n    // Sync corner controls\n    this._uniformCheck.checked = !this._config.uniform;\n    this._cornersUniformDiv.style.display = this._config.uniform ? 'block' : 'none';\n    this._cornersPersideDiv.style.display = this._config.uniform ? 'none' : 'grid';\n\n    // Sync unified corner controls\n    if (this._config.uniform) {\n      this._uniformXInput.value = String(this._config.topLeft.x);\n      this._uniformYInput.value = String(this._config.topLeft.y);\n      this._uniformShapeSelect.value = this._config.topLeft.shape;\n      this._uniformXVal.textContent = this._config.topLeft.x + '%';\n      this._uniformYVal.textContent = this._config.topLeft.y + '%';\n    }\n\n    const syncCorner = (x: HTMLInputElement, y: HTMLInputElement, shape: HTMLSelectElement, xVal: HTMLSpanElement, yVal: HTMLSpanElement, corner: BorderRadiusCorner) => {\n      x.value = String(corner.x);\n      y.value = String(corner.y);\n      shape.value = corner.shape;\n      xVal.textContent = corner.x + '%';\n      yVal.textContent = corner.y + '%';\n    };\n\n    syncCorner(this._tlXInput, this._tlYInput, this._tlShapeSelect, this._tlXVal, this._tlYVal, this._config.topLeft);\n    syncCorner(this._trXInput, this._trYInput, this._trShapeSelect, this._trXVal, this._trYVal, this._config.topRight);\n    syncCorner(this._brXInput, this._brYInput, this._brShapeSelect, this._brXVal, this._brYVal, this._config.bottomRight);\n    syncCorner(this._blXInput, this._blYInput, this._blShapeSelect, this._blXVal, this._blYVal, this._config.bottomLeft);\n\n    // Sync border controls\n    this._widthInput.value = String(this._config.width);\n    this._widthVal.textContent = this._config.width + 'px';\n    \n    this._borderUniformCheck.checked = !this._config.borderUniform;\n    this._borderUniformDiv.style.display = this._config.borderUniform ? 'block' : 'none';\n    this._borderPersideDiv.style.display = this._config.borderUniform ? 'none' : 'grid';\n    \n    if (this._config.borderUniform) {\n      // Sync uniform controls\n      this._styleSelect.value = this._config.top.style;\n      this._colorInput.value = this._config.top.color;\n      this._opacityInput.value = String(this._config.top.opacity);\n      this._opacityVal.textContent = this._config.top.opacity + '%';\n    } else {\n      // Sync per-side controls\n      this._topStyleSelect.value = this._config.top.style;\n      this._topWidthInput.value = String(this._config.top.width);\n      this._topWidthVal.textContent = this._config.top.width + 'px';\n      this._topColorInput.value = this._config.top.color;\n      this._topOpacityInput.value = String(this._config.top.opacity);\n      this._topOpacityVal.textContent = this._config.top.opacity + '%';\n      \n      this._rightStyleSelect.value = this._config.right.style;\n      this._rightWidthInput.value = String(this._config.right.width);\n      this._rightWidthVal.textContent = this._config.right.width + 'px';\n      this._rightColorInput.value = this._config.right.color;\n      this._rightOpacityInput.value = String(this._config.right.opacity);\n      this._rightOpacityVal.textContent = this._config.right.opacity + '%';\n      \n      this._bottomStyleSelect.value = this._config.bottom.style;\n      this._bottomWidthInput.value = String(this._config.bottom.width);\n      this._bottomWidthVal.textContent = this._config.bottom.width + 'px';\n      this._bottomColorInput.value = this._config.bottom.color;\n      this._bottomOpacityInput.value = String(this._config.bottom.opacity);\n      this._bottomOpacityVal.textContent = this._config.bottom.opacity + '%';\n      \n      this._leftStyleSelect.value = this._config.left.style;\n      this._leftWidthInput.value = String(this._config.left.width);\n      this._leftWidthVal.textContent = this._config.left.width + 'px';\n      this._leftColorInput.value = this._config.left.color;\n      this._leftOpacityInput.value = String(this._config.left.opacity);\n      this._leftOpacityVal.textContent = this._config.left.opacity + '%';\n    }\n  }\n\n  private _buildCss(): { radius: string; shape: string | null; borderCss: { [key: string]: string } } {\n    const radius = configToRadiusCss(this._config);\n    const shape = configToShapeCss(this._config);\n    \n    const borderCss: { [key: string]: string } = {};\n    \n    const { width, style, color } = configToBorderCss(this._config);\n    \n    if (this._config.borderUniform) {\n      // Uniform: use shorthand\n      borderCss['border-width'] = width;\n      borderCss['border-style'] = style;\n      borderCss['border-color'] = color;\n    } else {\n      // Per-side\n      const sides = ['top', 'right', 'bottom', 'left'] as const;\n      const sides_data = [this._config.top, this._config.right, this._config.bottom, this._config.left];\n      \n      for (let i = 0; i < sides.length; i++) {\n        const side = sides[i];\n        const cfg = sides_data[i];\n        borderCss[`border-${side}-width`] = `${cfg.width}px`;\n        borderCss[`border-${side}-style`] = cfg.style;\n        const rgba = `${hexToRgb(cfg.color)},${(cfg.opacity / 100).toFixed(2)}`;\n        borderCss[`border-${side}-color`] = `rgba(${rgba})`;\n      }\n    }\n    \n    return {\n      radius,\n      shape,\n      borderCss,\n    };\n  }\n\n  private _refresh() {\n    const { radius, shape, borderCss } = this._buildCss();\n    this._previewBox.style.borderRadius = radius;\n    // Set corner-shape in preview if not round\n    if (shape) {\n      (this._previewBox.style as any).cornerShape = shape;\n    } else {\n      (this._previewBox.style as any).cornerShape = '';\n    }\n    \n    // Set border in preview\n    Object.entries(borderCss).forEach(([prop, value]) => {\n      (this._previewBox.style as any)[prop.replace(/-([a-z])/g, (_, l) => l.toUpperCase())] = value;\n    });\n\n    // Output all properties\n    let output = `border-radius: ${radius};`;\n    if (shape) {\n      output += `\\ncorner-shape: ${shape};`;\n    }\n    Object.entries(borderCss).forEach(([prop, value]) => {\n      output += `\\n${prop}: ${value};`;\n    });\n    this._cssOutput.value = output;\n  }\n\n  private _applyToSelection() {\n    const items = this._designerCanvas?.instanceServiceContainer?.selectionService?.selectedElements;\n    if (!items?.length) return;\n    const { radius, shape, borderCss } = this._buildCss();\n    const group = items[0].openGroup('set border-radius and border properties');\n    for (const item of items) {\n      item.setStyle('border-radius', radius);\n      if (shape) {\n        item.setStyle('corner-shape', shape);\n      } else {\n        item.removeStyle('corner-shape');\n      }\n      Object.entries(borderCss).forEach(([prop, value]) => {\n        item.setStyle(prop, value);\n      });\n    }\n    group.commit();\n  }\n}\n\ncustomElements.define('node-projects-border-radius-editor-window', BorderRadiusEditorWindow);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/BoxShadowEditorWindow.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { DraggableToolWindow } from './DraggableToolWindow.js';\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\n\ninterface BoxShadowLayer {\n  inset: boolean;\n  offsetX: number;\n  offsetY: number;\n  blur: number;\n  spread: number;\n  color: string;\n  opacity: number;\n  enabled: boolean;\n}\n\nfunction defaultLayer(): BoxShadowLayer {\n  return { inset: false, offsetX: 5, offsetY: 5, blur: 10, spread: 0, color: '#000000', opacity: 50, enabled: true };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n  return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Splits a box-shadow value string into individual shadow tokens,\n * correctly ignoring commas inside color functions like rgba().\n */\nfunction splitShadowLayers(value: string): string[] {\n  const layers: string[] = [];\n  let depth = 0;\n  let current = '';\n  for (let i = 0; i < value.length; i++) {\n    const ch = value[i];\n    if (ch === '(') depth++;\n    else if (ch === ')') depth--;\n    else if (ch === ',' && depth === 0) {\n      const trimmed = current.trim();\n      if (trimmed) layers.push(trimmed);\n      current = '';\n      continue;\n    }\n    current += ch;\n  }\n  const trimmed = current.trim();\n  if (trimmed) layers.push(trimmed);\n  return layers;\n}\n\nfunction parseCssBoxShadow(value: string): BoxShadowLayer[] {\n  if (!value || value === 'none') return [defaultLayer()];\n  const layers: BoxShadowLayer[] = [];\n  for (const token of splitShadowLayers(value)) {\n    const layer = parseOneShadowLayer(token.trim());\n    if (layer) layers.push(layer);\n  }\n  return layers.length ? layers : [defaultLayer()];\n}\n\nfunction parseOneShadowLayer(token: string): BoxShadowLayer | null {\n  // Extract color function first (rgb/rgba/hsl/hsla) to avoid its spaces confusing token split\n  let color = '#000000';\n  let opacity = 100;\n  let remaining = token;\n\n  const colorFnMatch = remaining.match(/(rgba?\\([^)]+\\)|hsla?\\([^)]+\\))/);\n  if (colorFnMatch) {\n    const fn = colorFnMatch[1];\n    remaining = remaining.replace(fn, '').trim();\n    const rgbaMatch = fn.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/);\n    if (rgbaMatch) {\n      color = rgbToHex(parseInt(rgbaMatch[1]), parseInt(rgbaMatch[2]), parseInt(rgbaMatch[3]));\n      opacity = rgbaMatch[4] != null ? Math.round(parseFloat(rgbaMatch[4]) * 100) : 100;\n    }\n  } else {\n    // Try hex color\n    const hexMatch = remaining.match(/(#[0-9a-fA-F]{3,8})/);\n    if (hexMatch) {\n      color = hexMatch[1].length === 4\n        ? '#' + hexMatch[1].slice(1).split('').map(c => c + c).join('')\n        : hexMatch[1].slice(0, 7);\n      remaining = remaining.replace(hexMatch[1], '').trim();\n    }\n  }\n\n  // What remains: [inset] offsetX offsetY [blur] [spread]\n  const parts = remaining.split(/\\s+/).filter(Boolean);\n  let inset = false;\n  const numbers: number[] = [];\n  for (const p of parts) {\n    if (p === 'inset') { inset = true; continue; }\n    const n = parseFloat(p);\n    if (!isNaN(n)) numbers.push(n);\n  }\n  if (numbers.length < 2) return null;\n  return {\n    inset,\n    offsetX: numbers[0] ?? 0,\n    offsetY: numbers[1] ?? 0,\n    blur: numbers[2] ?? 0,\n    spread: numbers[3] ?? 0,\n    color,\n    opacity,\n    enabled: true,\n  };\n}\n\nfunction layerToCss(l: BoxShadowLayer): string {\n  const rgb = hexToRgb(l.color);\n  const a = (l.opacity / 100).toFixed(2);\n  return `${l.inset ? 'inset ' : ''}${l.offsetX}px ${l.offsetY}px ${l.blur}px ${l.spread}px rgba(${rgb},${a})`;\n}\n\nfunction hexToRgb(hex: string): string {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `${r},${g},${b}`;\n}\n\nexport class BoxShadowEditorWindow extends DraggableToolWindow {\n  private _designerCanvas: IDesignerCanvas;\n  private _layers: BoxShadowLayer[] = [defaultLayer()];\n  private _selectedIndex = 0;\n\n  // List\n  private _layerList: HTMLUListElement;\n  private _removeBtn: HTMLButtonElement;\n\n  // Controls\n  private _insetCheck: HTMLInputElement;\n  private _enabledCheck: HTMLInputElement;\n  private _inputX: HTMLInputElement;\n  private _inputY: HTMLInputElement;\n  private _inputBlur: HTMLInputElement;\n  private _inputSpread: HTMLInputElement;\n  private _inputColor: HTMLInputElement;\n  private _inputOpacity: HTMLInputElement;\n  private _opacityVal: HTMLSpanElement;\n\n  // Preview\n  private _previewBox: HTMLDivElement;\n\n  // Output\n  private _cssOutput: HTMLTextAreaElement;\n  private _loadBtn: HTMLButtonElement;\n  private _copyBtn: HTMLButtonElement;\n  private _applyBtn: HTMLButtonElement;\n\n  protected override get windowTitle(): string { return 'Box Shadow Editor'; }\n\n  protected override get windowContentStyle(): CSSStyleSheet {\n    return css`\n      * { box-sizing: border-box; }\n      .bse-root {\n        display: flex;\n        flex-direction: column;\n        width: 380px;\n        color: #ddd;\n        font-family: sans-serif;\n        font-size: 12px;\n        padding: 8px;\n        gap: 8px;\n        background: #2c2c2c;\n      }\n      .preview-area {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        height: 110px;\n        background: repeating-conic-gradient(#444 0% 25%, #333 0% 50%) 0 0 / 16px 16px;\n        border-radius: 4px;\n        border: 1px solid #111;\n      }\n      .preview-box {\n        width: 70px;\n        height: 70px;\n        background: #fff;\n        border-radius: 4px;\n      }\n      .layers-row {\n        display: flex;\n        gap: 6px;\n        align-items: flex-start;\n      }\n      .layer-list {\n        list-style: none;\n        margin: 0;\n        padding: 0;\n        flex: 1;\n        min-height: 60px;\n        max-height: 100px;\n        overflow-y: auto;\n        background: #1e1e1e;\n        border: 1px solid #111;\n        border-radius: 3px;\n      }\n      .layer-list li {\n        padding: 4px 8px;\n        cursor: pointer;\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        white-space: nowrap;\n        overflow: hidden;\n        text-overflow: ellipsis;\n      }\n      .layer-list li.selected {\n        background: #3a3a5a;\n      }\n      .layer-list li .swatch {\n        width: 12px;\n        height: 12px;\n        border-radius: 2px;\n        border: 1px solid #555;\n        flex-shrink: 0;\n        display: inline-block;\n      }\n      .layer-list li .disabled-label { opacity: 0.4; }\n      .layer-buttons {\n        display: flex;\n        flex-direction: column;\n        gap: 4px;\n      }\n      .layer-buttons button {\n        width: 24px;\n        height: 24px;\n        padding: 0;\n        font-size: 14px;\n        background: #444;\n        color: #ddd;\n        border: 1px solid #222;\n        border-radius: 3px;\n        cursor: pointer;\n        line-height: 1;\n      }\n      .layer-buttons button:hover { background: #666; }\n      .controls {\n        display: grid;\n        grid-template-columns: 90px 1fr 36px;\n        gap: 4px 6px;\n        align-items: center;\n      }\n      .controls label { color: #aaa; }\n      .controls input[type=range] { width: 100%; }\n      .controls input[type=number] {\n        width: 100%;\n        background: #1e1e1e;\n        border: 1px solid #444;\n        color: #ddd;\n        border-radius: 3px;\n        padding: 2px 4px;\n      }\n      .controls input[type=color] {\n        width: 100%;\n        height: 24px;\n        padding: 1px;\n        background: #1e1e1e;\n        border: 1px solid #444;\n        border-radius: 3px;\n        cursor: pointer;\n      }\n      .check-row {\n        display: flex;\n        gap: 14px;\n        align-items: center;\n        margin-bottom: 2px;\n      }\n      .check-row label {\n        display: flex;\n        align-items: center;\n        gap: 4px;\n        cursor: pointer;\n        user-select: none;\n      }\n      .css-out {\n        background: #1e1e1e;\n        border: 1px solid #333;\n        border-radius: 3px;\n        color: #7ec8e3;\n        font-family: monospace;\n        font-size: 11px;\n        padding: 6px;\n        resize: none;\n        width: 100%;\n        height: 48px;\n      }\n      .actions {\n        display: flex;\n        gap: 6px;\n        justify-content: flex-end;\n      }\n      .actions .load-btn { margin-right: auto; }\n      .actions button, .apply-btn {\n        padding: 4px 12px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        cursor: pointer;\n        font-size: 12px;\n      }\n      .actions button:hover, .apply-btn:hover { background: #555; }\n      .apply-btn {\n        background: #3a6a9a;\n        border-color: #2a5a8a;\n      }\n      .apply-btn:hover { background: #4a7aaa; }\n      .val-label { text-align: right; color: #888; }\n    `;\n  }\n\n  protected override get windowTemplate(): string {\n    return `\n      <div class=\"bse-root\">\n        <div class=\"preview-area\">\n          <div class=\"preview-box\" id=\"bse-preview\"></div>\n        </div>\n\n        <div class=\"layers-row\">\n          <ul class=\"layer-list\" id=\"bse-layer-list\"></ul>\n          <div class=\"layer-buttons\">\n            <button id=\"bse-add-btn\" title=\"Add layer\">+</button>\n            <button id=\"bse-dup-btn\" title=\"Duplicate layer\">⧉</button>\n            <button id=\"bse-up-btn\" title=\"Move up\">↑</button>\n            <button id=\"bse-down-btn\" title=\"Move down\">↓</button>\n            <button id=\"bse-remove-btn\" title=\"Remove layer\">−</button>\n          </div>\n        </div>\n\n        <div class=\"check-row\">\n          <label><input type=\"checkbox\" id=\"bse-enabled\"> Enabled</label>\n          <label><input type=\"checkbox\" id=\"bse-inset\"> Inset</label>\n        </div>\n\n        <div class=\"controls\">\n          <label>Offset X</label>\n          <input type=\"range\" id=\"bse-x\" min=\"-100\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"bse-x-val\">0px</span>\n\n          <label>Offset Y</label>\n          <input type=\"range\" id=\"bse-y\" min=\"-100\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"bse-y-val\">0px</span>\n\n          <label>Blur</label>\n          <input type=\"range\" id=\"bse-blur\" min=\"0\" max=\"200\" step=\"1\">\n          <span class=\"val-label\" id=\"bse-blur-val\">0px</span>\n\n          <label>Spread</label>\n          <input type=\"range\" id=\"bse-spread\" min=\"-50\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"bse-spread-val\">0px</span>\n\n          <label>Color</label>\n          <input type=\"color\" id=\"bse-color\" value=\"#000000\">\n          <span></span>\n\n          <label>Opacity</label>\n          <input type=\"range\" id=\"bse-opacity\" min=\"0\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"bse-opacity-val\">100%</span>\n        </div>\n\n        <textarea class=\"css-out\" id=\"bse-css-out\"></textarea>\n\n        <div class=\"actions\">\n          <button class=\"load-btn\" id=\"bse-load-btn\">Load</button>\n          <button id=\"bse-copy-btn\">Copy CSS</button>\n          <button class=\"apply-btn\" id=\"bse-apply-btn\">Apply to selection</button>\n        </div>\n      </div>`;\n  }\n\n  constructor(designerCanvas?: IDesignerCanvas) {\n    super();\n    this._designerCanvas = designerCanvas;\n\n    this._layerList = this._getDomElement<HTMLUListElement>('bse-layer-list');\n    this._removeBtn = this._getDomElement<HTMLButtonElement>('bse-remove-btn');\n    this._insetCheck = this._getDomElement<HTMLInputElement>('bse-inset');\n    this._enabledCheck = this._getDomElement<HTMLInputElement>('bse-enabled');\n    this._inputX = this._getDomElement<HTMLInputElement>('bse-x');\n    this._inputY = this._getDomElement<HTMLInputElement>('bse-y');\n    this._inputBlur = this._getDomElement<HTMLInputElement>('bse-blur');\n    this._inputSpread = this._getDomElement<HTMLInputElement>('bse-spread');\n    this._inputColor = this._getDomElement<HTMLInputElement>('bse-color');\n    this._inputOpacity = this._getDomElement<HTMLInputElement>('bse-opacity');\n    this._opacityVal = this._getDomElement<HTMLSpanElement>('bse-opacity-val');\n    this._previewBox = this._getDomElement<HTMLDivElement>('bse-preview');\n    this._cssOutput = this._getDomElement<HTMLTextAreaElement>('bse-css-out');\n    this._loadBtn = this._getDomElement<HTMLButtonElement>('bse-load-btn');\n    this._copyBtn = this._getDomElement<HTMLButtonElement>('bse-copy-btn');\n    this._applyBtn = this._getDomElement<HTMLButtonElement>('bse-apply-btn');\n\n    this._getDomElement<HTMLButtonElement>('bse-add-btn').onclick = () => this._addLayer();\n    this._getDomElement<HTMLButtonElement>('bse-dup-btn').onclick = () => this._duplicateLayer();\n    this._getDomElement<HTMLButtonElement>('bse-up-btn').onclick = () => this._moveLayer(-1);\n    this._getDomElement<HTMLButtonElement>('bse-down-btn').onclick = () => this._moveLayer(1);\n    this._removeBtn.onclick = () => this._removeLayer();\n\n    this._insetCheck.onchange = () => { this._currentLayer().inset = this._insetCheck.checked; this._refresh(); };\n    this._enabledCheck.onchange = () => { this._currentLayer().enabled = this._enabledCheck.checked; this._refresh(); };\n\n    for (const [input, key, valId] of [\n      [this._inputX, 'offsetX', 'bse-x-val'],\n      [this._inputY, 'offsetY', 'bse-y-val'],\n      [this._inputBlur, 'blur', 'bse-blur-val'],\n      [this._inputSpread, 'spread', 'bse-spread-val'],\n    ] as [HTMLInputElement, keyof BoxShadowLayer, string][]) {\n      input.oninput = () => {\n        (this._currentLayer() as any)[key] = Number(input.value);\n        this._getDomElement<HTMLSpanElement>(valId).textContent = input.value + 'px';\n        this._refresh();\n      };\n    }\n\n    this._inputColor.oninput = () => { this._currentLayer().color = this._inputColor.value; this._refresh(); };\n    this._inputOpacity.oninput = () => {\n      this._currentLayer().opacity = Number(this._inputOpacity.value);\n      this._opacityVal.textContent = this._inputOpacity.value + '%';\n      this._refresh();\n    };\n\n    this._copyBtn.onclick = () => {\n      navigator.clipboard?.writeText(this._cssOutput.value).catch(() => {});\n    };\n    this._applyBtn.onclick = () => this._applyToSelection();\n    this._loadBtn.onclick = () => {\n      this._loadFromPrimarySelection();\n      this._renderList();\n      this._loadControls();\n      this._refresh();\n    };\n\n    this._loadFromPrimarySelection();\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _loadFromPrimarySelection() {\n    const primary = this._designerCanvas?.instanceServiceContainer?.selectionService?.primarySelection;\n    if (!primary) return;\n    const existing = primary.getStyle('box-shadow');\n    if (existing) {\n      this._layers = parseCssBoxShadow(existing);\n      this._selectedIndex = 0;\n    }\n  }\n\n  private _currentLayer(): BoxShadowLayer {\n    return this._layers[this._selectedIndex];\n  }\n\n  private _addLayer() {\n    this._layers.push(defaultLayer());\n    this._selectedIndex = this._layers.length - 1;\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _duplicateLayer() {\n    const clone = { ...this._currentLayer() };\n    this._layers.splice(this._selectedIndex + 1, 0, clone);\n    this._selectedIndex = this._selectedIndex + 1;\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _removeLayer() {\n    if (this._layers.length <= 1) return;\n    this._layers.splice(this._selectedIndex, 1);\n    this._selectedIndex = Math.min(this._selectedIndex, this._layers.length - 1);\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _moveLayer(dir: -1 | 1) {\n    const i = this._selectedIndex;\n    const j = i + dir;\n    if (j < 0 || j >= this._layers.length) return;\n    [this._layers[i], this._layers[j]] = [this._layers[j], this._layers[i]];\n    this._selectedIndex = j;\n    this._renderList();\n    this._refresh();\n  }\n\n  private _renderList() {\n    this._layerList.innerHTML = '';\n    this._layers.forEach((l, i) => {\n      const li = document.createElement('li');\n      if (i === this._selectedIndex) li.classList.add('selected');\n      const swatch = document.createElement('span');\n      swatch.className = 'swatch';\n      swatch.style.background = l.color;\n      li.appendChild(swatch);\n      const lbl = document.createElement('span');\n      lbl.textContent = `${l.inset ? 'inset ' : ''}${l.offsetX}px ${l.offsetY}px ${l.blur}px ${l.spread}px`;\n      if (!l.enabled) lbl.classList.add('disabled-label');\n      li.appendChild(lbl);\n      li.onclick = () => { this._selectedIndex = i; this._renderList(); this._loadControls(); };\n      this._layerList.appendChild(li);\n    });\n  }\n\n  private _loadControls() {\n    const l = this._currentLayer();\n    this._insetCheck.checked = l.inset;\n    this._enabledCheck.checked = l.enabled;\n    this._inputX.value = String(l.offsetX);\n    this._getDomElement<HTMLSpanElement>('bse-x-val').textContent = l.offsetX + 'px';\n    this._inputY.value = String(l.offsetY);\n    this._getDomElement<HTMLSpanElement>('bse-y-val').textContent = l.offsetY + 'px';\n    this._inputBlur.value = String(l.blur);\n    this._getDomElement<HTMLSpanElement>('bse-blur-val').textContent = l.blur + 'px';\n    this._inputSpread.value = String(l.spread);\n    this._getDomElement<HTMLSpanElement>('bse-spread-val').textContent = l.spread + 'px';\n    this._inputColor.value = l.color;\n    this._inputOpacity.value = String(l.opacity);\n    this._opacityVal.textContent = l.opacity + '%';\n  }\n\n  private _buildCss(): string {\n    return this._layers.filter(l => l.enabled).map(layerToCss).join(', ') || 'none';\n  }\n\n  private _refresh() {\n    const cssVal = this._buildCss();\n    this._previewBox.style.boxShadow = cssVal;\n    this._cssOutput.value = `box-shadow: ${cssVal};`;\n    this._renderList();\n  }\n\n  private _applyToSelection() {\n    if (!this._designerCanvas) return;\n    const selection = this._designerCanvas.instanceServiceContainer.selectionService.selectedElements;\n    if (!selection?.length) return;\n    const cssVal = this._buildCss();\n    const grp = selection[0].openGroup('Apply box-shadow');\n    for (const item of selection) {\n      item.setStyle('box-shadow', cssVal);\n    }\n    grp.commit();\n  }\n}\n\ncustomElements.define('node-projects-designer-box-shadow-editor', BoxShadowEditorWindow);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/DraggableToolWindow.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\n\n/**\n * A reusable draggable tool window with a title bar and close button.\n * Subclasses override `title` and provide content via the `windowContent` slot\n * by overriding `windowTemplate` and `windowStyle`.\n *\n * Usage:\n *   - Extend this class and override `windowTitle`, `windowTemplate`, and `windowStyle`.\n *   - The subclass template is inserted into the `.window-content` slot area.\n *   - To show: call `DraggableToolWindow.show(instance, anchorElement?)` or append to document.body.\n */\nexport abstract class DraggableToolWindow extends BaseCustomWebComponentConstructorAppend {\n\n  static override readonly style = css`\n    :host {\n      position: fixed;\n      display: block;\n      z-index: 9999;\n      min-width: 200px;\n      user-select: none;\n      touch-action: none;\n    }\n    .window-frame {\n      background: #2c2c2c;\n      border: 1px solid #111;\n      border-radius: 4px;\n      box-shadow: 0 4px 24px rgba(0,0,0,0.7);\n      display: flex;\n      flex-direction: column;\n      overflow: hidden;\n    }\n    .title-bar {\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n      background: #1a1a1a;\n      padding: 2px 5px;\n      cursor: move;\n      flex-shrink: 0;\n      height: 18px;\n      box-sizing: border-box;\n    }\n    .title-text {\n      color: #ccc;\n      font-size: 11px;\n      font-family: sans-serif;\n      font-weight: 500;\n      flex: 1;\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n    .close-btn {\n      width: 14px;\n      height: 14px;\n      background: none;\n      border: none;\n      color: #888;\n      font-size: 12px;\n      line-height: 1;\n      cursor: pointer;\n      padding: 0;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      border-radius: 2px;\n      flex-shrink: 0;\n    }\n    .close-btn:hover {\n      background: #c0392b;\n      color: #fff;\n    }\n    .window-content {\n      overflow: auto;\n    }\n  `;\n\n  static override readonly template = html`\n    <div class=\"window-frame\">\n      <div class=\"title-bar\" id=\"title-bar\">\n        <span class=\"title-text\" id=\"title-text\"></span>\n        <button class=\"close-btn\" id=\"close-btn\" title=\"Close\">&#x2715;</button>\n      </div>\n      <div class=\"window-content\" id=\"window-content\"></div>\n    </div>\n  `;\n\n  /** Override in subclass to set the window title. */\n  protected abstract get windowTitle(): string;\n\n  /** Override in subclass to return the inner content template. */\n  protected abstract get windowTemplate(): HTMLTemplateElement | string;\n\n  /** Override in subclass to return additional styles for the content. */\n  protected get windowContentStyle(): CSSStyleSheet | null { return null; }\n\n  private _dragging = false;\n  private _dragOffsetX = 0;\n  private _dragOffsetY = 0;\n\n  private _titleBar: HTMLElement;\n  private _titleText: HTMLElement;\n  private _closeBtn: HTMLButtonElement;\n  private _contentArea: HTMLElement;\n\n  constructor() {\n    super();\n\n    this._titleBar = this._getDomElement<HTMLElement>('title-bar');\n    this._titleText = this._getDomElement<HTMLElement>('title-text');\n    this._closeBtn = this._getDomElement<HTMLButtonElement>('close-btn');\n    this._contentArea = this._getDomElement<HTMLElement>('window-content');\n\n    //@ts-ignore\n    this._titleText.textContent = this.windowTitle;\n\n    // Inject content\n    //@ts-ignore\n    const tpl = this.windowTemplate;\n    if (typeof tpl === 'string') {\n      const temp = document.createElement('template');\n      temp.innerHTML = tpl;\n      this._contentArea.appendChild(temp.content.cloneNode(true));\n    } else {\n      this._contentArea.appendChild(tpl.content.cloneNode(true));\n    }\n\n    // Apply optional extra styles to shadow root\n    const extraStyle = this.windowContentStyle;\n    if (extraStyle) {\n      this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, extraStyle];\n    }\n\n    this._closeBtn.onclick = () => this._close();\n\n    // Drag via pointer events on title bar\n    this._titleBar.addEventListener('pointerdown', (e: PointerEvent) => this._onPointerDown(e));\n  }\n\n  connectedCallback() {\n    // Default position near top-left if not already placed\n    if (!this.style.left && !this.style.top) {\n      this.style.left = '100px';\n      this.style.top = '100px';\n    }\n  }\n\n  private _close() {\n    this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));\n    this.remove();\n  }\n\n  private _onPointerDown(e: PointerEvent) {\n    if (e.button !== 0) return;\n    if (e.target === this._closeBtn) return;\n    e.preventDefault();\n    this._dragging = true;\n    const rect = this.getBoundingClientRect();\n    this._dragOffsetX = e.clientX - rect.left;\n    this._dragOffsetY = e.clientY - rect.top;\n    this._titleBar.setPointerCapture(e.pointerId);\n    this._titleBar.addEventListener('pointermove', this._onPointerMove);\n    this._titleBar.addEventListener('pointerup', this._onPointerUp);\n  }\n\n  private _onPointerMove = (e: PointerEvent) => {\n    if (!this._dragging) return;\n    let x = e.clientX - this._dragOffsetX;\n    let y = e.clientY - this._dragOffsetY;\n    // Keep within viewport\n    x = Math.max(0, Math.min(x, window.innerWidth - 40));\n    y = Math.max(0, Math.min(y, window.innerHeight - 30));\n    this.style.left = x + 'px';\n    this.style.top = y + 'px';\n  };\n\n  private _onPointerUp = (e: PointerEvent) => {\n    this._dragging = false;\n    this._titleBar.releasePointerCapture(e.pointerId);\n    this._titleBar.removeEventListener('pointermove', this._onPointerMove);\n    this._titleBar.removeEventListener('pointerup', this._onPointerUp);\n  };\n\n  /**\n   * Show the tool window anchored near a reference element, or centered if none.\n   * Appends to document.body.\n   */\n  static showWindow(instance: DraggableToolWindow, anchor?: Element) {\n    if (anchor) {\n      const rect = anchor.getBoundingClientRect();\n      instance.style.left = (rect.right + 8) + 'px';\n      instance.style.top = rect.top + 'px';\n    } else {\n      instance.style.left = Math.max(0, (window.innerWidth - 260) / 2) + 'px';\n      instance.style.top = '120px';\n    }\n    document.body.appendChild(instance);\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/DrawToolPopup.ts",
    "content": "import { html, css } from '@node-projects/base-custom-webcomponent';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { AbstractBaseToolPopup } from './AbstractBaseToolPopup.js';\r\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\r\nimport { CommandType } from '../../../../../../commandHandling/CommandType.js';\r\n\r\nexport class DrawToolPopup extends AbstractBaseToolPopup {\r\n\r\n  static override style = [<CSSStyleSheet>super.style, css`\r\n      .container {\r\n          width: 220px;\r\n          min-height: 300px;\r\n      }\r\n      .inputs{\r\n        display: grid;\r\n        grid-template-columns: 1fr 1fr;\r\n        margin: 5px;\r\n        align-items: center;\r\n        & span {\r\n          height: 100%;\r\n        }\r\n      }\r\n      .text {\r\n        margin-left: 5px;\r\n        font-size: 14px;\r\n      }\r\n      `]\r\n\r\n  static override template = html`\r\n        <div class=\"container\">\r\n          <header><h2 id=\"title\" style=\"margin: 0;\">Draw</h2></header>\r\n          <main id=\"content-area\">\r\n            <div class=\"tools\">\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"DrawLine\" title=\"Draw Line\" style=\"background-image: url('${assetsPath}images/tools/DrawLineTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"DrawPath\" title=\"Pointer Tool\" style=\"background-image: url('${assetsPath}images/tools/DrawPathTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"DrawRect\" title=\"Draw Rectangle\" style=\"background-image: url('${assetsPath}images/tools/DrawRectTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"DrawEllipsis\" title=\"Draw Ellipsis\" style=\"background-image: url('${assetsPath}images/tools/DrawEllipTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"PickColor\" title=\"Pick Color\" style=\"background-image: url('${assetsPath}images/tools/ColorPickerTool.svg');\"></div>\r\n            </div>\r\n            <div class=\"inputs\">   \r\n                <text class=\"text\">Stroke Color</text>\r\n                [[this.getEditor('setStrokeColor', 'color', {}, this.designerCanvas.serviceContainer.globalContext.strokeColor)]] \r\n                <text class=\"text\">Fill Brush</text>\r\n                [[this.getEditor('setFillBrush', 'color', {}, this.designerCanvas.serviceContainer.globalContext.fillBrush)]] \r\n                <text class=\"text\">Stroke Thickness</text>\r\n                [[this.getEditor('setStrokeThickness', 'range', { min: 1, max: 20, step: 1 }, this.designerCanvas.serviceContainer.globalContext.strokeThickness)]] \r\n            </div>\r\n          </main>\r\n        </div>`;\r\n\r\n  constructor(private designerCanvas: IDesignerCanvas) {\r\n    super();\r\n  }\r\n\r\n  ready() {\r\n    this._bindingsParse();\r\n    this.designerCanvas.serviceContainer.globalContext.strokeColor\r\n  }\r\n\r\n  //todo currentvalue\r\n  getEditor(commandType: CommandType, type: string, additional: { [key: string]: any }, currentValue: any) {\r\n    const res = this.designerCanvas.serviceContainer.forSomeServicesTillResult('editorTypeService',\r\n      x => x.getEditor(type, {\r\n        changedCallback: (newValue) => this.designerCanvas.executeCommand({ type: commandType, parameter: newValue }),\r\n        ...additional\r\n      }));\r\n    res.setValue(currentValue);\r\n    return res.element;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-designer-draw-tool-popup', DrawToolPopup);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/GradientEditorWindow.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { DraggableToolWindow } from './DraggableToolWindow.js';\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\n\ninterface GradientStop {\n  color: string;    // hex #rrggbb\n  opacity: number;  // 0-100\n  position: number; // 0-100 (%)\n}\n\ntype GradientType = 'linear' | 'radial' | 'conic';\ntype RadialShape = 'circle' | 'ellipse';\ntype RadialSize = 'closest-side' | 'closest-corner' | 'farthest-side' | 'farthest-corner';\n\ninterface GradientConfig {\n  type: GradientType;\n  angle: number;\n  radialShape: RadialShape;\n  radialSize: RadialSize;\n  posX: number;\n  posY: number;\n  stops: GradientStop[];\n  repeating: boolean;\n}\n\nfunction defaultConfig(): GradientConfig {\n  return {\n    type: 'linear',\n    angle: 135,\n    radialShape: 'ellipse',\n    radialSize: 'farthest-corner',\n    posX: 50,\n    posY: 50,\n    stops: [\n      { color: '#667eea', opacity: 100, position: 0 },\n      { color: '#764ba2', opacity: 100, position: 100 },\n    ],\n    repeating: false,\n  };\n}\n\nfunction hexToRgb(hex: string): string {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `${r},${g},${b}`;\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n  return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');\n}\n\nfunction stopToCss(s: GradientStop): string {\n  return `rgba(${hexToRgb(s.color)},${(s.opacity / 100).toFixed(2)}) ${s.position}%`;\n}\n\nfunction configToCss(cfg: GradientConfig): string {\n  const sorted = [...cfg.stops].sort((a, b) => a.position - b.position);\n  const stops = sorted.map(stopToCss).join(', ');\n  const prefix = cfg.repeating ? 'repeating-' : '';\n  if (cfg.type === 'linear') {\n    return `${prefix}linear-gradient(${cfg.angle}deg, ${stops})`;\n  } else if (cfg.type === 'radial') {\n    return `${prefix}radial-gradient(${cfg.radialShape} ${cfg.radialSize} at ${cfg.posX}% ${cfg.posY}%, ${stops})`;\n  } else {\n    return `${prefix}conic-gradient(from ${cfg.angle}deg at ${cfg.posX}% ${cfg.posY}%, ${stops})`;\n  }\n}\n\n/**\n * Extracts the first gradient function from a CSS value string,\n * using bracket counting to correctly handle nested functions like rgba().\n */\nfunction extractFirstGradient(value: string): string | null {\n  const keywords = ['repeating-linear-gradient', 'repeating-radial-gradient', 'repeating-conic-gradient',\n    'linear-gradient', 'radial-gradient', 'conic-gradient'];\n  for (const kw of keywords) {\n    const idx = value.indexOf(kw + '(');\n    if (idx === -1) continue;\n    const start = idx + kw.length; // points to '('\n    let depth = 0, end = start;\n    for (let i = start; i < value.length; i++) {\n      if (value[i] === '(') depth++;\n      else if (value[i] === ')') { depth--; if (depth === 0) { end = i; break; } }\n    }\n    return value.slice(idx, end + 1);\n  }\n  return null;\n}\n\n// ─── Parser ────────────────────────────────────────────────────────────────\n\nfunction splitTopLevel(s: string): string[] {\n  const result: string[] = [];\n  let depth = 0, current = '';\n  for (const ch of s) {\n    if (ch === '(') depth++;\n    else if (ch === ')') depth--;\n    else if (ch === ',' && depth === 0) { result.push(current); current = ''; continue; }\n    current += ch;\n  }\n  if (current) result.push(current);\n  return result;\n}\n\nfunction parseGradientStop(token: string): GradientStop | null {\n  let color = '#000000', opacity = 100, position = 0;\n  let remaining = token.trim();\n  const colorFnMatch = remaining.match(/^(rgba?\\([^)]+\\)|hsla?\\([^)]+\\))/);\n  if (colorFnMatch) {\n    const fn = colorFnMatch[1];\n    remaining = remaining.slice(fn.length).trim();\n    const m = fn.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/);\n    if (m) {\n      color = rgbToHex(parseInt(m[1]), parseInt(m[2]), parseInt(m[3]));\n      opacity = m[4] != null ? Math.round(parseFloat(m[4]) * 100) : 100;\n    }\n  } else {\n    const hexMatch = remaining.match(/^(#[0-9a-fA-F]{3,8})/);\n    if (hexMatch) {\n      color = hexMatch[1].length === 4\n        ? '#' + hexMatch[1].slice(1).split('').map(c => c + c).join('')\n        : hexMatch[1].slice(0, 7);\n      remaining = remaining.slice(hexMatch[1].length).trim();\n    } else {\n      return null; // named color – skip\n    }\n  }\n  const posMatch = remaining.match(/([\\d.]+)%/);\n  if (posMatch) position = parseFloat(posMatch[1]);\n  return { color, opacity, position };\n}\n\nfunction looksLikeStop(token: string): boolean {\n  const t = token.trim();\n  return t.startsWith('#') || /^rgba?\\(/.test(t) || /^hsla?\\(/.test(t);\n}\n\nfunction parseGradientCss(value: string): GradientConfig {\n  const cfg = defaultConfig();\n  if (!value) return cfg;\n  const trimmed = value.trim();\n  cfg.repeating = trimmed.startsWith('repeating-');\n  let inner = cfg.repeating ? trimmed.slice('repeating-'.length) : trimmed;\n\n  if (inner.startsWith('linear-gradient')) cfg.type = 'linear';\n  else if (inner.startsWith('radial-gradient')) cfg.type = 'radial';\n  else if (inner.startsWith('conic-gradient')) cfg.type = 'conic';\n  else return cfg;\n\n  const parenStart = inner.indexOf('(');\n  const parenEnd = inner.lastIndexOf(')');\n  if (parenStart < 0 || parenEnd < 0) return cfg;\n  const content = inner.slice(parenStart + 1, parenEnd);\n  const tokens = splitTopLevel(content);\n\n  // Separate gradient-specific args from color stops\n  const argTokens: string[] = [];\n  let stopStart = 0;\n  for (let i = 0; i < tokens.length; i++) {\n    if (looksLikeStop(tokens[i])) { stopStart = i; break; }\n    argTokens.push(tokens[i]);\n    if (i === tokens.length - 1) stopStart = tokens.length;\n  }\n\n  // Parse type args\n  if (cfg.type === 'linear') {\n    const firstArg = argTokens[0]?.trim() ?? '';\n    const degMatch = firstArg.match(/^(-?[\\d.]+)deg$/);\n    if (degMatch) cfg.angle = parseFloat(degMatch[1]);\n    else if (firstArg.startsWith('to ')) {\n      const toMap: Record<string, number> = {\n        'to right': 90, 'to left': 270, 'to bottom': 180, 'to top': 0,\n        'to bottom right': 135, 'to bottom left': 225, 'to top right': 45, 'to top left': 315,\n      };\n      cfg.angle = toMap[firstArg] ?? 135;\n    }\n  } else if (cfg.type === 'radial') {\n    const firstArg = argTokens[0]?.trim() ?? '';\n    const atMatch = firstArg.match(/^(.*?)\\s+at\\s+([\\d.]+)%\\s+([\\d.]+)%$/);\n    if (atMatch) {\n      const shapePart = atMatch[1].trim();\n      cfg.posX = parseFloat(atMatch[2]);\n      cfg.posY = parseFloat(atMatch[3]);\n      cfg.radialShape = shapePart.includes('circle') ? 'circle' : 'ellipse';\n      for (const sz of ['closest-side', 'closest-corner', 'farthest-side', 'farthest-corner'] as RadialSize[]) {\n        if (shapePart.includes(sz)) { cfg.radialSize = sz; break; }\n      }\n    }\n  } else if (cfg.type === 'conic') {\n    const firstArg = argTokens[0]?.trim() ?? '';\n    const fromMatch = firstArg.match(/from\\s+(-?[\\d.]+)deg/);\n    if (fromMatch) cfg.angle = parseFloat(fromMatch[1]);\n    const atMatch = firstArg.match(/at\\s+([\\d.]+)%\\s+([\\d.]+)%/);\n    if (atMatch) { cfg.posX = parseFloat(atMatch[1]); cfg.posY = parseFloat(atMatch[2]); }\n  }\n\n  // Parse color stops\n  const stops: GradientStop[] = [];\n  for (let i = stopStart; i < tokens.length; i++) {\n    const s = parseGradientStop(tokens[i]);\n    if (s) stops.push(s);\n  }\n  if (stops.length >= 2) cfg.stops = stops;\n  return cfg;\n}\n\n// ─── Window class ──────────────────────────────────────────────────────────\n\nexport class GradientEditorWindow extends DraggableToolWindow {\n  private _designerCanvas: IDesignerCanvas;\n  private _config: GradientConfig = defaultConfig();\n  private _selectedStopIndex = 0;\n\n  private _previewStrip: HTMLDivElement;\n  private _stopTrack: HTMLDivElement;\n  private _typeSelect: HTMLSelectElement;\n  private _repeatingCheck: HTMLInputElement;\n  private _linearGroup: HTMLDivElement;\n  private _radialGroup: HTMLDivElement;\n  private _conicGroup: HTMLDivElement;\n  private _angleInput: HTMLInputElement;\n  private _angleVal: HTMLSpanElement;\n  private _conicAngleInput: HTMLInputElement;\n  private _conicAngleVal: HTMLSpanElement;\n  private _shapeSelect: HTMLSelectElement;\n  private _sizeSelect: HTMLSelectElement;\n  private _posXInput: HTMLInputElement;\n  private _posXVal: HTMLSpanElement;\n  private _posYInput: HTMLInputElement;\n  private _posYVal: HTMLSpanElement;\n  private _conicPosXInput: HTMLInputElement;\n  private _conicPosXVal: HTMLSpanElement;\n  private _conicPosYInput: HTMLInputElement;\n  private _conicPosYVal: HTMLSpanElement;\n  private _stopColorInput: HTMLInputElement;\n  private _stopOpacityInput: HTMLInputElement;\n  private _stopOpacityVal: HTMLSpanElement;\n  private _stopPositionInput: HTMLInputElement;\n  private _stopPositionVal: HTMLSpanElement;\n  private _removeStopBtn: HTMLButtonElement;\n  private _propertySelect: HTMLSelectElement;\n  private _cssOutput: HTMLTextAreaElement;\n  private _loadBtn: HTMLButtonElement;\n  private _copyBtn: HTMLButtonElement;\n  private _applyBtn: HTMLButtonElement;\n\n  protected override get windowTitle(): string { return 'Gradient Editor'; }\n\n  protected override get windowContentStyle(): CSSStyleSheet {\n    return css`\n      * { box-sizing: border-box; }\n      .ge-root {\n        display: flex;\n        flex-direction: column;\n        width: 380px;\n        color: #ddd;\n        font-family: sans-serif;\n        font-size: 12px;\n        gap: 6px;\n        padding: 8px;\n        background: #2a2a2a;\n      }\n      .preview-strip {\n        height: 50px;\n        border-radius: 4px 4px 0 0;\n        border: 1px solid #444;\n        border-bottom: none;\n      }\n      .stop-track {\n        height: 20px;\n        background: repeating-conic-gradient(#333 0% 25%, #444 0% 50%) 0 0 / 10px 10px;\n        border: 1px solid #444;\n        border-radius: 0 0 4px 4px;\n        position: relative;\n        cursor: crosshair;\n        margin-bottom: 2px;\n      }\n      .stop-marker {\n        position: absolute;\n        top: 2px;\n        width: 16px;\n        height: 16px;\n        border-radius: 50%;\n        border: 2px solid #aaa;\n        cursor: pointer;\n        transform: translateX(-50%);\n        transition: border-color 0.1s;\n      }\n      .stop-marker.selected {\n        border-color: #fff;\n        box-shadow: 0 0 0 2px #3a7ad5;\n        z-index: 1;\n      }\n      .row {\n        display: grid;\n        grid-template-columns: 70px 1fr 40px;\n        gap: 3px 6px;\n        align-items: center;\n      }\n      .row.two-col {\n        grid-template-columns: 70px 1fr;\n      }\n      .row.multi {\n        grid-template-columns: 70px 1fr 70px 1fr;\n      }\n      .row label { color: #aaa; }\n      .row input[type=range] { width: 100%; }\n      .row input[type=color] {\n        width: 100%;\n        height: 22px;\n        padding: 1px;\n        border: 1px solid #555;\n        border-radius: 3px;\n        background: #1e1e1e;\n        cursor: pointer;\n      }\n      select {\n        background: #2e2e2e;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        padding: 2px 4px;\n        font-size: 11px;\n        width: 100%;\n      }\n      .separator {\n        height: 1px;\n        background: #444;\n        margin: 2px 0;\n      }\n      .stop-header {\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        color: #888;\n        font-size: 11px;\n      }\n      .stop-header .stop-actions {\n        display: flex;\n        gap: 3px;\n      }\n      .stop-actions button {\n        padding: 1px 7px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        cursor: pointer;\n        font-size: 12px;\n      }\n      .stop-actions button:hover { background: #555; }\n      .val-label { text-align: right; color: #888; }\n      .css-out {\n        background: #1e1e1e;\n        border: 1px solid #333;\n        border-radius: 3px;\n        color: #7ec8e3;\n        font-family: monospace;\n        font-size: 11px;\n        padding: 6px;\n        resize: none;\n        width: 100%;\n        height: 48px;\n      }\n      .actions {\n        display: flex;\n        gap: 6px;\n        justify-content: flex-end;\n      }\n      .actions .load-btn { margin-right: auto; }\n      .actions button, .apply-btn {\n        padding: 4px 12px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        cursor: pointer;\n        font-size: 12px;\n      }\n      .actions button:hover, .apply-btn:hover { background: #555; }\n      .apply-btn {\n        background: #3a6a9a;\n        border-color: #2a5a8a;\n      }\n      .apply-btn:hover { background: #4a7aaa; }\n    `;\n  }\n\n  protected override get windowTemplate(): string {\n    return `\n      <div class=\"ge-root\">\n        <div class=\"preview-strip\" id=\"ge-preview-strip\"></div>\n        <div class=\"stop-track\" id=\"ge-stop-track\"></div>\n\n        <div class=\"row\">\n          <label>Type</label>\n          <select id=\"ge-type\">\n            <option value=\"linear\">Linear</option>\n            <option value=\"radial\">Radial</option>\n            <option value=\"conic\">Conic</option>\n          </select>\n          <span></span>\n        </div>\n        <div class=\"row two-col\" style=\"margin-top:-3px\">\n          <label></label>\n          <label style=\"color:#ddd\"><input type=\"checkbox\" id=\"ge-repeating\"> Repeating</label>\n        </div>\n\n        <!-- Linear controls -->\n        <div id=\"ge-linear-group\">\n          <div class=\"row\">\n            <label>Angle</label>\n            <input type=\"range\" id=\"ge-angle\" min=\"0\" max=\"360\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-angle-val\">135°</span>\n          </div>\n        </div>\n\n        <!-- Radial controls -->\n        <div id=\"ge-radial-group\" style=\"display:none\">\n          <div class=\"row multi\">\n            <label>Shape</label>\n            <select id=\"ge-shape\">\n              <option value=\"ellipse\">Ellipse</option>\n              <option value=\"circle\">Circle</option>\n            </select>\n            <label>Size</label>\n            <select id=\"ge-size\">\n              <option value=\"farthest-corner\">Farthest Corner</option>\n              <option value=\"farthest-side\">Farthest Side</option>\n              <option value=\"closest-corner\">Closest Corner</option>\n              <option value=\"closest-side\">Closest Side</option>\n            </select>\n          </div>\n          <div class=\"row\">\n            <label>Position X</label>\n            <input type=\"range\" id=\"ge-pos-x\" min=\"0\" max=\"100\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-pos-x-val\">50%</span>\n          </div>\n          <div class=\"row\">\n            <label>Position Y</label>\n            <input type=\"range\" id=\"ge-pos-y\" min=\"0\" max=\"100\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-pos-y-val\">50%</span>\n          </div>\n        </div>\n\n        <!-- Conic controls -->\n        <div id=\"ge-conic-group\" style=\"display:none\">\n          <div class=\"row\">\n            <label>Angle</label>\n            <input type=\"range\" id=\"ge-conic-angle\" min=\"0\" max=\"360\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-conic-angle-val\">0°</span>\n          </div>\n          <div class=\"row\">\n            <label>Position X</label>\n            <input type=\"range\" id=\"ge-conic-pos-x\" min=\"0\" max=\"100\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-conic-pos-x-val\">50%</span>\n          </div>\n          <div class=\"row\">\n            <label>Position Y</label>\n            <input type=\"range\" id=\"ge-conic-pos-y\" min=\"0\" max=\"100\" step=\"1\">\n            <span class=\"val-label\" id=\"ge-conic-pos-y-val\">50%</span>\n          </div>\n        </div>\n\n        <div class=\"separator\"></div>\n\n        <div class=\"stop-header\">\n          <span>Selected Stop</span>\n          <div class=\"stop-actions\">\n            <button id=\"ge-add-stop-btn\" title=\"Add stop at 50%\">+ Add</button>\n            <button id=\"ge-remove-stop-btn\" title=\"Remove selected stop\">− Remove</button>\n          </div>\n        </div>\n\n        <div class=\"row\">\n          <label>Color</label>\n          <input type=\"color\" id=\"ge-stop-color\" value=\"#667eea\">\n          <span></span>\n        </div>\n        <div class=\"row\">\n          <label>Opacity</label>\n          <input type=\"range\" id=\"ge-stop-opacity\" min=\"0\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"ge-stop-opacity-val\">100%</span>\n        </div>\n        <div class=\"row\">\n          <label>Position</label>\n          <input type=\"range\" id=\"ge-stop-pos\" min=\"0\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"ge-stop-pos-val\">0%</span>\n        </div>\n\n        <div class=\"separator\"></div>\n\n        <div class=\"row two-col\">\n          <label>Apply to</label>\n          <select id=\"ge-property\">\n            <option value=\"background-image\">background-image</option>\n            <option value=\"background\">background</option>\n          </select>\n        </div>\n\n        <textarea class=\"css-out\" id=\"ge-css-out\"></textarea>\n\n        <div class=\"actions\">\n          <button class=\"load-btn\" id=\"ge-load-btn\">Load</button>\n          <button id=\"ge-copy-btn\">Copy CSS</button>\n          <button class=\"apply-btn\" id=\"ge-apply-btn\">Apply to selection</button>\n        </div>\n      </div>`;\n  }\n\n  constructor(designerCanvas?: IDesignerCanvas) {\n    super();\n    this._designerCanvas = designerCanvas;\n\n    this._previewStrip = this._getDomElement<HTMLDivElement>('ge-preview-strip');\n    this._stopTrack = this._getDomElement<HTMLDivElement>('ge-stop-track');\n    this._typeSelect = this._getDomElement<HTMLSelectElement>('ge-type');\n    this._repeatingCheck = this._getDomElement<HTMLInputElement>('ge-repeating');\n    this._linearGroup = this._getDomElement<HTMLDivElement>('ge-linear-group');\n    this._radialGroup = this._getDomElement<HTMLDivElement>('ge-radial-group');\n    this._conicGroup = this._getDomElement<HTMLDivElement>('ge-conic-group');\n    this._angleInput = this._getDomElement<HTMLInputElement>('ge-angle');\n    this._angleVal = this._getDomElement<HTMLSpanElement>('ge-angle-val');\n    this._conicAngleInput = this._getDomElement<HTMLInputElement>('ge-conic-angle');\n    this._conicAngleVal = this._getDomElement<HTMLSpanElement>('ge-conic-angle-val');\n    this._shapeSelect = this._getDomElement<HTMLSelectElement>('ge-shape');\n    this._sizeSelect = this._getDomElement<HTMLSelectElement>('ge-size');\n    this._posXInput = this._getDomElement<HTMLInputElement>('ge-pos-x');\n    this._posXVal = this._getDomElement<HTMLSpanElement>('ge-pos-x-val');\n    this._posYInput = this._getDomElement<HTMLInputElement>('ge-pos-y');\n    this._posYVal = this._getDomElement<HTMLSpanElement>('ge-pos-y-val');\n    this._conicPosXInput = this._getDomElement<HTMLInputElement>('ge-conic-pos-x');\n    this._conicPosXVal = this._getDomElement<HTMLSpanElement>('ge-conic-pos-x-val');\n    this._conicPosYInput = this._getDomElement<HTMLInputElement>('ge-conic-pos-y');\n    this._conicPosYVal = this._getDomElement<HTMLSpanElement>('ge-conic-pos-y-val');\n    this._stopColorInput = this._getDomElement<HTMLInputElement>('ge-stop-color');\n    this._stopOpacityInput = this._getDomElement<HTMLInputElement>('ge-stop-opacity');\n    this._stopOpacityVal = this._getDomElement<HTMLSpanElement>('ge-stop-opacity-val');\n    this._stopPositionInput = this._getDomElement<HTMLInputElement>('ge-stop-pos');\n    this._stopPositionVal = this._getDomElement<HTMLSpanElement>('ge-stop-pos-val');\n    this._removeStopBtn = this._getDomElement<HTMLButtonElement>('ge-remove-stop-btn');\n    this._propertySelect = this._getDomElement<HTMLSelectElement>('ge-property');\n    this._cssOutput = this._getDomElement<HTMLTextAreaElement>('ge-css-out');\n    this._loadBtn = this._getDomElement<HTMLButtonElement>('ge-load-btn');\n    this._copyBtn = this._getDomElement<HTMLButtonElement>('ge-copy-btn');\n    this._applyBtn = this._getDomElement<HTMLButtonElement>('ge-apply-btn');\n\n    // Type / repeating\n    this._typeSelect.onchange = () => {\n      this._config.type = this._typeSelect.value as GradientType;\n      this._updateTypeVisibility();\n      this._refresh();\n    };\n    this._repeatingCheck.onchange = () => { this._config.repeating = this._repeatingCheck.checked; this._refresh(); };\n\n    // Linear angle\n    this._angleInput.oninput = () => {\n      this._config.angle = Number(this._angleInput.value);\n      this._angleVal.textContent = this._angleInput.value + '°';\n      this._refresh();\n    };\n\n    // Conic angle\n    this._conicAngleInput.oninput = () => {\n      this._config.angle = Number(this._conicAngleInput.value);\n      this._conicAngleVal.textContent = this._conicAngleInput.value + '°';\n      this._refresh();\n    };\n\n    // Radial controls\n    this._shapeSelect.onchange = () => { this._config.radialShape = this._shapeSelect.value as RadialShape; this._refresh(); };\n    this._sizeSelect.onchange = () => { this._config.radialSize = this._sizeSelect.value as RadialSize; this._refresh(); };\n    this._posXInput.oninput = () => { this._config.posX = Number(this._posXInput.value); this._posXVal.textContent = this._posXInput.value + '%'; this._refresh(); };\n    this._posYInput.oninput = () => { this._config.posY = Number(this._posYInput.value); this._posYVal.textContent = this._posYInput.value + '%'; this._refresh(); };\n\n    // Conic position\n    this._conicPosXInput.oninput = () => { this._config.posX = Number(this._conicPosXInput.value); this._conicPosXVal.textContent = this._conicPosXInput.value + '%'; this._refresh(); };\n    this._conicPosYInput.oninput = () => { this._config.posY = Number(this._conicPosYInput.value); this._conicPosYVal.textContent = this._conicPosYInput.value + '%'; this._refresh(); };\n\n    // Stop controls\n    this._stopColorInput.oninput = () => { this._currentStop().color = this._stopColorInput.value; this._refresh(); };\n    this._stopOpacityInput.oninput = () => {\n      this._currentStop().opacity = Number(this._stopOpacityInput.value);\n      this._stopOpacityVal.textContent = this._stopOpacityInput.value + '%';\n      this._refresh();\n    };\n    this._stopPositionInput.oninput = () => {\n      this._currentStop().position = Number(this._stopPositionInput.value);\n      this._stopPositionVal.textContent = this._stopPositionInput.value + '%';\n      this._refresh();\n    };\n\n    // Add / remove stop\n    this._getDomElement<HTMLButtonElement>('ge-add-stop-btn').onclick = () => this._addStop(50);\n    this._removeStopBtn.onclick = () => this._removeStop();\n\n    // Stop track: click to add stop\n    this._stopTrack.addEventListener('click', (e) => {\n      const rect = this._stopTrack.getBoundingClientRect();\n      const pos = Math.round(((e.clientX - rect.left) / rect.width) * 100);\n      this._addStop(Math.max(0, Math.min(100, pos)));\n    });\n\n    // Output / apply\n    this._copyBtn.onclick = () => { navigator.clipboard?.writeText(this._cssOutput.value).catch(() => {}); };\n    this._applyBtn.onclick = () => this._applyToSelection();\n    this._loadBtn.onclick = () => {\n      this._loadFromPrimarySelection();\n      this._selectedStopIndex = Math.max(0, Math.min(this._selectedStopIndex, this._config.stops.length - 1));\n      this._updateTypeVisibility();\n      this._syncControls();\n      this._renderStopTrack();\n      this._refreshOutput();\n    };\n\n    this._loadFromPrimarySelection();\n    this._updateTypeVisibility();\n    this._syncControls();\n    this._renderStopTrack();\n    this._refreshOutput();\n  }\n\n  private _loadFromPrimarySelection() {\n    const primary = this._designerCanvas?.instanceServiceContainer?.selectionService?.primarySelection;\n    if (!primary) return;\n    for (const prop of ['background-image', 'background']) {\n      const val = primary.getStyle(prop);\n      if (val && val.includes('gradient')) {\n        const extracted = extractFirstGradient(val);\n        if (extracted) {\n          this._config = parseGradientCss(extracted);\n          if (prop === 'background') this._propertySelect.value = 'background';\n        }\n        break;\n      }\n    }\n  }\n\n  private _currentStop(): GradientStop {\n    return this._config.stops[this._selectedStopIndex];\n  }\n\n  private _addStop(position: number) {\n    this._config.stops.push({ color: '#ffffff', opacity: 100, position });\n    this._selectedStopIndex = this._config.stops.length - 1;\n    this._syncControls();\n    this._renderStopTrack();\n    this._refreshOutput();\n  }\n\n  private _removeStop() {\n    if (this._config.stops.length <= 2) return;\n    this._config.stops.splice(this._selectedStopIndex, 1);\n    this._selectedStopIndex = Math.min(this._selectedStopIndex, this._config.stops.length - 1);\n    this._syncControls();\n    this._renderStopTrack();\n    this._refreshOutput();\n  }\n\n  private _updateTypeVisibility() {\n    this._linearGroup.style.display = this._config.type === 'linear' ? '' : 'none';\n    this._radialGroup.style.display = this._config.type === 'radial' ? '' : 'none';\n    this._conicGroup.style.display = this._config.type === 'conic' ? '' : 'none';\n  }\n\n  private _syncControls() {\n    // type\n    this._typeSelect.value = this._config.type;\n    this._repeatingCheck.checked = this._config.repeating;\n\n    // linear/conic angle\n    this._angleInput.value = String(this._config.angle);\n    this._angleVal.textContent = this._config.angle + '°';\n    this._conicAngleInput.value = String(this._config.angle);\n    this._conicAngleVal.textContent = this._config.angle + '°';\n\n    // radial\n    this._shapeSelect.value = this._config.radialShape;\n    this._sizeSelect.value = this._config.radialSize;\n    this._posXInput.value = String(this._config.posX);\n    this._posXVal.textContent = this._config.posX + '%';\n    this._posYInput.value = String(this._config.posY);\n    this._posYVal.textContent = this._config.posY + '%';\n\n    // conic position\n    this._conicPosXInput.value = String(this._config.posX);\n    this._conicPosXVal.textContent = this._config.posX + '%';\n    this._conicPosYInput.value = String(this._config.posY);\n    this._conicPosYVal.textContent = this._config.posY + '%';\n\n    // selected stop\n    const s = this._currentStop();\n    this._stopColorInput.value = s.color;\n    this._stopOpacityInput.value = String(s.opacity);\n    this._stopOpacityVal.textContent = s.opacity + '%';\n    this._stopPositionInput.value = String(s.position);\n    this._stopPositionVal.textContent = s.position + '%';\n  }\n\n  private _renderStopTrack() {\n    // Update gradient preview on strip\n    const gradCss = configToCss(this._config);\n    this._previewStrip.style.background = gradCss;\n\n    // Re-render stop markers\n    this._stopTrack.innerHTML = '';\n    this._config.stops.forEach((stop, i) => {\n      const marker = document.createElement('div');\n      marker.className = 'stop-marker' + (i === this._selectedStopIndex ? ' selected' : '');\n      marker.style.left = `${stop.position}%`;\n      marker.style.background = stop.color;\n      marker.style.opacity = String(stop.opacity / 100);\n\n      // Select on click (stop propagation so track click handler doesn't fire)\n      marker.addEventListener('click', (e) => {\n        e.stopPropagation();\n        this._selectedStopIndex = i;\n        this._syncControls();\n        this._renderStopTrack();\n      });\n\n      // Drag to reposition — update marker in-place to avoid destroying pointer capture\n      marker.addEventListener('pointerdown', (e) => {\n        e.preventDefault();\n        e.stopPropagation();\n        marker.setPointerCapture(e.pointerId);\n        const trackRect = this._stopTrack.getBoundingClientRect();\n        const onMove = (me: PointerEvent) => {\n          const x = Math.max(0, Math.min(1, (me.clientX - trackRect.left) / trackRect.width));\n          const pos = Math.round(x * 100);\n          this._config.stops[i].position = pos;\n          this._selectedStopIndex = i;\n          // Update this marker's position in-place (don't re-render — that would destroy the captured element)\n          marker.style.left = pos + '%';\n          // Update the selected state visually\n          this._stopTrack.querySelectorAll('.stop-marker').forEach((m, mi) => {\n            m.classList.toggle('selected', mi === i);\n          });\n          // Refresh preview and output without re-rendering markers\n          this._previewStrip.style.background = configToCss(this._config);\n          this._refreshOutput();\n        };\n        const onUp = () => {\n          marker.removeEventListener('pointermove', onMove);\n          marker.removeEventListener('pointerup', onUp);\n          // Full sync after drag ends\n          this._syncControls();\n          this._renderStopTrack();\n        };\n        marker.addEventListener('pointermove', onMove);\n        marker.addEventListener('pointerup', onUp);\n      });\n\n      this._stopTrack.appendChild(marker);\n    });\n  }\n\n  private _refreshOutput() {\n    this._cssOutput.value = configToCss(this._config);\n  }\n\n  private _refresh() {\n    this._renderStopTrack();\n    this._refreshOutput();\n  }\n\n  private _applyToSelection() {\n    const items = this._designerCanvas?.instanceServiceContainer?.selectionService?.selectedElements;\n    if (!items?.length) return;\n    const cssVal = configToCss(this._config);\n    const prop = this._propertySelect.value;\n    const group = items[0].openGroup(`set ${prop}`);\n    for (const item of items) {\n      item.setStyle(prop, cssVal);\n    }\n    group.commit();\n  }\n}\n\ncustomElements.define('node-projects-gradient-editor-window', GradientEditorWindow);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/PointerToolPopup.ts",
    "content": "import { html } from '@node-projects/base-custom-webcomponent';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { AbstractBaseToolPopup } from './AbstractBaseToolPopup.js';\r\n\r\nexport class PointerToolPopup extends AbstractBaseToolPopup {\r\n\r\n  static override template = html`\r\n        <div class=\"container\">\r\n          <header><h2 id=\"title\" style=\"margin: 0;\">Selection</h2></header>\r\n          <main id=\"content-area\">\r\n            <div class=\"tools\">\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"Pointer\" title=\"Pointer\" style=\"background-image: url('${assetsPath}images/tools/PointerTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"Pan\" title=\"Pan\" style=\"background-image: url('${assetsPath}images/tools/PanTool.svg');\"></div>\r\n            </div>\r\n          </main>\r\n        </div>`;\r\n}\r\n\r\ncustomElements.define('node-projects-designer-pointer-tool-popup', PointerToolPopup);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/SelectionToolPopup.ts",
    "content": "import { html } from '@node-projects/base-custom-webcomponent';\r\nimport { assetsPath } from \"../../../../../../Constants.js\";\r\nimport { AbstractBaseToolPopup } from './AbstractBaseToolPopup.js';\r\n\r\nexport class SelectionToolPopup extends AbstractBaseToolPopup {\r\n\r\n  static override template = html`\r\n        <div class=\"container\">\r\n          <header><h2 id=\"title\" style=\"margin: 0;\">Selection</h2></header>\r\n          <main id=\"content-area\">\r\n            <div class=\"tools\">\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"RectangleSelector\" title=\"Rectangle Selector\" style=\"background-image: url('${assetsPath}images/tools/SelectRectTool.svg');\"></div>\r\n              <div class=\"tool\" data-command=\"setTool\" data-command-parameter=\"MagicWandSelector\" title=\"Magic Wand Selector\" style=\"background-image: url('${assetsPath}images/tools/MagicWandTool.svg');\"></div>\r\n            </div>\r\n          </main>\r\n        </div>`;\r\n}\r\n\r\ncustomElements.define('node-projects-designer-selection-tool-popup', SelectionToolPopup);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/TextShadowEditorWindow.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { DraggableToolWindow } from './DraggableToolWindow.js';\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\n\ninterface TextShadowLayer {\n  offsetX: number;\n  offsetY: number;\n  blur: number;\n  color: string;\n  opacity: number;\n  enabled: boolean;\n}\n\nfunction defaultLayer(): TextShadowLayer {\n  return { offsetX: 2, offsetY: 2, blur: 4, color: '#000000', opacity: 50, enabled: true };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n  return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');\n}\n\nfunction hexToRgb(hex: string): string {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `${r},${g},${b}`;\n}\n\nfunction splitLayers(value: string): string[] {\n  const layers: string[] = [];\n  let depth = 0, current = '';\n  for (const ch of value) {\n    if (ch === '(') depth++;\n    else if (ch === ')') depth--;\n    else if (ch === ',' && depth === 0) {\n      const t = current.trim();\n      if (t) layers.push(t);\n      current = '';\n      continue;\n    }\n    current += ch;\n  }\n  const t = current.trim();\n  if (t) layers.push(t);\n  return layers;\n}\n\nfunction layerToCss(l: TextShadowLayer): string {\n  const a = (l.opacity / 100).toFixed(2);\n  return `${l.offsetX}px ${l.offsetY}px ${l.blur}px rgba(${hexToRgb(l.color)},${a})`;\n}\n\nfunction parseCssTextShadow(value: string): TextShadowLayer[] {\n  if (!value || value === 'none') return [defaultLayer()];\n  const layers: TextShadowLayer[] = [];\n  for (const token of splitLayers(value)) {\n    const layer = parseOneLayer(token.trim());\n    if (layer) layers.push(layer);\n  }\n  return layers.length ? layers : [defaultLayer()];\n}\n\nfunction parseOneLayer(token: string): TextShadowLayer | null {\n  let color = '#000000', opacity = 100;\n  let remaining = token;\n\n  const colorFnMatch = remaining.match(/(rgba?\\([^)]+\\)|hsla?\\([^)]+\\))/);\n  if (colorFnMatch) {\n    const fn = colorFnMatch[1];\n    remaining = remaining.replace(fn, '').trim();\n    const m = fn.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)(?:\\s*,\\s*([\\d.]+))?\\s*\\)/);\n    if (m) {\n      color = rgbToHex(parseInt(m[1]), parseInt(m[2]), parseInt(m[3]));\n      opacity = m[4] != null ? Math.round(parseFloat(m[4]) * 100) : 100;\n    }\n  } else {\n    const hexMatch = remaining.match(/(#[0-9a-fA-F]{3,8})/);\n    if (hexMatch) {\n      color = hexMatch[1].length === 4\n        ? '#' + hexMatch[1].slice(1).split('').map(c => c + c).join('')\n        : hexMatch[1].slice(0, 7);\n      remaining = remaining.replace(hexMatch[1], '').trim();\n    }\n  }\n\n  const numbers = remaining.split(/\\s+/).filter(Boolean).map(p => parseFloat(p)).filter(n => !isNaN(n));\n  if (numbers.length < 2) return null;\n  return { offsetX: numbers[0] ?? 0, offsetY: numbers[1] ?? 0, blur: numbers[2] ?? 0, color, opacity, enabled: true };\n}\n\nexport class TextShadowEditorWindow extends DraggableToolWindow {\n  private _designerCanvas: IDesignerCanvas;\n  private _layers: TextShadowLayer[] = [defaultLayer()];\n  private _selectedIndex = 0;\n\n  private _layerList: HTMLUListElement;\n  private _removeBtn: HTMLButtonElement;\n  private _enabledCheck: HTMLInputElement;\n  private _inputX: HTMLInputElement;\n  private _inputY: HTMLInputElement;\n  private _inputBlur: HTMLInputElement;\n  private _inputColor: HTMLInputElement;\n  private _inputOpacity: HTMLInputElement;\n  private _opacityVal: HTMLSpanElement;\n  private _previewText: HTMLDivElement;\n  private _cssOutput: HTMLTextAreaElement;\n  private _loadBtn: HTMLButtonElement;\n  private _copyBtn: HTMLButtonElement;\n  private _applyBtn: HTMLButtonElement;\n\n  protected override get windowTitle(): string { return 'Text Shadow Editor'; }\n\n  protected override get windowContentStyle(): CSSStyleSheet {\n    return css`\n      * { box-sizing: border-box; }\n      .tse-root {\n        display: flex;\n        flex-direction: column;\n        width: 360px;\n        color: #ddd;\n        font-family: sans-serif;\n        font-size: 12px;\n        gap: 6px;\n        padding: 8px;\n        background: #2a2a2a;\n      }\n      .preview-area {\n        background: #1a1a1a;\n        border: 1px solid #444;\n        border-radius: 4px;\n        height: 60px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n      }\n      .preview-text {\n        font-size: 28px;\n        font-weight: bold;\n        color: #fff;\n        user-select: none;\n      }\n      .layers-row {\n        display: flex;\n        gap: 4px;\n      }\n      .layer-list {\n        flex: 1;\n        list-style: none;\n        margin: 0;\n        padding: 0;\n        border: 1px solid #444;\n        border-radius: 3px;\n        max-height: 80px;\n        overflow-y: auto;\n        background: #1e1e1e;\n      }\n      .layer-list li {\n        display: flex;\n        align-items: center;\n        gap: 5px;\n        padding: 2px 5px;\n        cursor: pointer;\n        font-size: 11px;\n      }\n      .layer-list li:hover { background: #333; }\n      .layer-list li.selected { background: #2a4a7a; }\n      .swatch {\n        width: 12px; height: 12px;\n        border-radius: 2px;\n        border: 1px solid #555;\n        flex-shrink: 0;\n      }\n      .disabled-label { opacity: 0.4; }\n      .layer-buttons {\n        display: flex;\n        flex-direction: column;\n        gap: 2px;\n      }\n      .layer-buttons button {\n        width: 22px; height: 22px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        cursor: pointer;\n        font-size: 13px;\n        padding: 0;\n      }\n      .layer-buttons button:hover { background: #555; }\n      .controls {\n        display: grid;\n        grid-template-columns: 60px 1fr 38px;\n        gap: 3px 6px;\n        align-items: center;\n      }\n      .controls label { color: #aaa; }\n      .controls input[type=range] { width: 100%; }\n      .controls input[type=color] {\n        width: 100%;\n        height: 22px;\n        padding: 1px;\n        border: 1px solid #555;\n        border-radius: 3px;\n        background: #1e1e1e;\n        cursor: pointer;\n      }\n      .check-row {\n        display: flex;\n        gap: 12px;\n        align-items: center;\n      }\n      .check-row label {\n        display: flex;\n        align-items: center;\n        gap: 4px;\n        cursor: pointer;\n        user-select: none;\n      }\n      .css-out {\n        background: #1e1e1e;\n        border: 1px solid #333;\n        border-radius: 3px;\n        color: #7ec8e3;\n        font-family: monospace;\n        font-size: 11px;\n        padding: 6px;\n        resize: none;\n        width: 100%;\n        height: 40px;\n      }\n      .actions {\n        display: flex;\n        gap: 6px;\n        justify-content: flex-end;\n      }\n      .actions .load-btn { margin-right: auto; }\n      .actions button, .apply-btn {\n        padding: 4px 12px;\n        background: #3a3a3a;\n        color: #ddd;\n        border: 1px solid #555;\n        border-radius: 3px;\n        cursor: pointer;\n        font-size: 12px;\n      }\n      .actions button:hover, .apply-btn:hover { background: #555; }\n      .apply-btn {\n        background: #3a6a9a;\n        border-color: #2a5a8a;\n      }\n      .apply-btn:hover { background: #4a7aaa; }\n      .val-label { text-align: right; color: #888; }\n    `;\n  }\n\n  protected override get windowTemplate(): string {\n    return `\n      <div class=\"tse-root\">\n        <div class=\"preview-area\">\n          <div class=\"preview-text\" id=\"tse-preview\">Sample Text</div>\n        </div>\n\n        <div class=\"layers-row\">\n          <ul class=\"layer-list\" id=\"tse-layer-list\"></ul>\n          <div class=\"layer-buttons\">\n            <button id=\"tse-add-btn\" title=\"Add layer\">+</button>\n            <button id=\"tse-dup-btn\" title=\"Duplicate layer\">⧉</button>\n            <button id=\"tse-up-btn\" title=\"Move up\">↑</button>\n            <button id=\"tse-down-btn\" title=\"Move down\">↓</button>\n            <button id=\"tse-remove-btn\" title=\"Remove layer\">−</button>\n          </div>\n        </div>\n\n        <div class=\"check-row\">\n          <label><input type=\"checkbox\" id=\"tse-enabled\"> Enabled</label>\n        </div>\n\n        <div class=\"controls\">\n          <label>Offset X</label>\n          <input type=\"range\" id=\"tse-x\" min=\"-50\" max=\"50\" step=\"1\">\n          <span class=\"val-label\" id=\"tse-x-val\">0px</span>\n\n          <label>Offset Y</label>\n          <input type=\"range\" id=\"tse-y\" min=\"-50\" max=\"50\" step=\"1\">\n          <span class=\"val-label\" id=\"tse-y-val\">0px</span>\n\n          <label>Blur</label>\n          <input type=\"range\" id=\"tse-blur\" min=\"0\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"tse-blur-val\">0px</span>\n\n          <label>Color</label>\n          <input type=\"color\" id=\"tse-color\" value=\"#000000\">\n          <span></span>\n\n          <label>Opacity</label>\n          <input type=\"range\" id=\"tse-opacity\" min=\"0\" max=\"100\" step=\"1\">\n          <span class=\"val-label\" id=\"tse-opacity-val\">100%</span>\n        </div>\n\n        <textarea class=\"css-out\" id=\"tse-css-out\"></textarea>\n\n        <div class=\"actions\">\n          <button class=\"load-btn\" id=\"tse-load-btn\">Load</button>\n          <button id=\"tse-copy-btn\">Copy CSS</button>\n          <button class=\"apply-btn\" id=\"tse-apply-btn\">Apply to selection</button>\n        </div>\n      </div>`;\n  }\n\n  constructor(designerCanvas?: IDesignerCanvas) {\n    super();\n    this._designerCanvas = designerCanvas;\n\n    this._layerList = this._getDomElement<HTMLUListElement>('tse-layer-list');\n    this._removeBtn = this._getDomElement<HTMLButtonElement>('tse-remove-btn');\n    this._enabledCheck = this._getDomElement<HTMLInputElement>('tse-enabled');\n    this._inputX = this._getDomElement<HTMLInputElement>('tse-x');\n    this._inputY = this._getDomElement<HTMLInputElement>('tse-y');\n    this._inputBlur = this._getDomElement<HTMLInputElement>('tse-blur');\n    this._inputColor = this._getDomElement<HTMLInputElement>('tse-color');\n    this._inputOpacity = this._getDomElement<HTMLInputElement>('tse-opacity');\n    this._opacityVal = this._getDomElement<HTMLSpanElement>('tse-opacity-val');\n    this._previewText = this._getDomElement<HTMLDivElement>('tse-preview');\n    this._cssOutput = this._getDomElement<HTMLTextAreaElement>('tse-css-out');\n    this._loadBtn = this._getDomElement<HTMLButtonElement>('tse-load-btn');\n    this._copyBtn = this._getDomElement<HTMLButtonElement>('tse-copy-btn');\n    this._applyBtn = this._getDomElement<HTMLButtonElement>('tse-apply-btn');\n\n    this._getDomElement<HTMLButtonElement>('tse-add-btn').onclick = () => this._addLayer();\n    this._getDomElement<HTMLButtonElement>('tse-dup-btn').onclick = () => this._duplicateLayer();\n    this._getDomElement<HTMLButtonElement>('tse-up-btn').onclick = () => this._moveLayer(-1);\n    this._getDomElement<HTMLButtonElement>('tse-down-btn').onclick = () => this._moveLayer(1);\n    this._removeBtn.onclick = () => this._removeLayer();\n\n    this._enabledCheck.onchange = () => { this._currentLayer().enabled = this._enabledCheck.checked; this._refresh(); };\n\n    for (const [input, key, valId] of [\n      [this._inputX, 'offsetX', 'tse-x-val'],\n      [this._inputY, 'offsetY', 'tse-y-val'],\n      [this._inputBlur, 'blur', 'tse-blur-val'],\n    ] as [HTMLInputElement, keyof TextShadowLayer, string][]) {\n      input.oninput = () => {\n        (this._currentLayer() as any)[key] = Number(input.value);\n        this._getDomElement<HTMLSpanElement>(valId).textContent = input.value + 'px';\n        this._refresh();\n      };\n    }\n\n    this._inputColor.oninput = () => { this._currentLayer().color = this._inputColor.value; this._refresh(); };\n    this._inputOpacity.oninput = () => {\n      this._currentLayer().opacity = Number(this._inputOpacity.value);\n      this._opacityVal.textContent = this._inputOpacity.value + '%';\n      this._refresh();\n    };\n\n    this._copyBtn.onclick = () => { navigator.clipboard?.writeText(this._cssOutput.value).catch(() => {}); };\n    this._applyBtn.onclick = () => this._applyToSelection();\n    this._loadBtn.onclick = () => {\n      this._loadFromPrimarySelection();\n      this._renderList();\n      this._loadControls();\n      this._refresh();\n    };\n\n    this._loadFromPrimarySelection();\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _loadFromPrimarySelection() {\n    const primary = this._designerCanvas?.instanceServiceContainer?.selectionService?.primarySelection;\n    if (!primary) return;\n    const existing = primary.getStyle('text-shadow');\n    if (existing) {\n      this._layers = parseCssTextShadow(existing);\n      this._selectedIndex = 0;\n    }\n  }\n\n  private _currentLayer(): TextShadowLayer {\n    return this._layers[this._selectedIndex];\n  }\n\n  private _addLayer() {\n    this._layers.push(defaultLayer());\n    this._selectedIndex = this._layers.length - 1;\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _duplicateLayer() {\n    this._layers.splice(this._selectedIndex + 1, 0, { ...this._currentLayer() });\n    this._selectedIndex = this._selectedIndex + 1;\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _removeLayer() {\n    if (this._layers.length <= 1) return;\n    this._layers.splice(this._selectedIndex, 1);\n    this._selectedIndex = Math.min(this._selectedIndex, this._layers.length - 1);\n    this._renderList();\n    this._loadControls();\n    this._refresh();\n  }\n\n  private _moveLayer(dir: -1 | 1) {\n    const i = this._selectedIndex;\n    const j = i + dir;\n    if (j < 0 || j >= this._layers.length) return;\n    [this._layers[i], this._layers[j]] = [this._layers[j], this._layers[i]];\n    this._selectedIndex = j;\n    this._renderList();\n    this._refresh();\n  }\n\n  private _renderList() {\n    this._layerList.innerHTML = '';\n    this._layers.forEach((l, i) => {\n      const li = document.createElement('li');\n      if (i === this._selectedIndex) li.classList.add('selected');\n      const swatch = document.createElement('span');\n      swatch.className = 'swatch';\n      swatch.style.background = l.color;\n      li.appendChild(swatch);\n      const lbl = document.createElement('span');\n      lbl.textContent = `${l.offsetX}px ${l.offsetY}px ${l.blur}px`;\n      if (!l.enabled) lbl.classList.add('disabled-label');\n      li.appendChild(lbl);\n      li.onclick = () => { this._selectedIndex = i; this._renderList(); this._loadControls(); };\n      this._layerList.appendChild(li);\n    });\n  }\n\n  private _loadControls() {\n    const l = this._currentLayer();\n    this._enabledCheck.checked = l.enabled;\n    this._inputX.value = String(l.offsetX);\n    this._getDomElement<HTMLSpanElement>('tse-x-val').textContent = l.offsetX + 'px';\n    this._inputY.value = String(l.offsetY);\n    this._getDomElement<HTMLSpanElement>('tse-y-val').textContent = l.offsetY + 'px';\n    this._inputBlur.value = String(l.blur);\n    this._getDomElement<HTMLSpanElement>('tse-blur-val').textContent = l.blur + 'px';\n    this._inputColor.value = l.color;\n    this._inputOpacity.value = String(l.opacity);\n    this._opacityVal.textContent = l.opacity + '%';\n  }\n\n  private _buildCss(): string {\n    return this._layers.filter(l => l.enabled).map(layerToCss).join(', ') || 'none';\n  }\n\n  private _refresh() {\n    const css = this._buildCss();\n    this._previewText.style.textShadow = css;\n    this._cssOutput.value = css;\n    this._renderList();\n  }\n\n  private _applyToSelection() {\n    const items = this._designerCanvas?.instanceServiceContainer?.selectionService?.selectedElements;\n    if (!items?.length) return;\n    const cssVal = this._buildCss();\n    const group = items[0].openGroup('set text-shadow');\n    for (const item of items) {\n      if (cssVal === 'none') item.removeStyle('text-shadow');\n      else item.setStyle('text-shadow', cssVal);\n    }\n    group.commit();\n  }\n}\n\ncustomElements.define('node-projects-text-shadow-editor-window', TextShadowEditorWindow);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/designerView/tools/toolBar/popups/TransformToolPopup.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { DraggableToolWindow } from './DraggableToolWindow.js';\nimport { IDesignerCanvas } from '../../../IDesignerCanvas.js';\nimport { filterChildPlaceItems } from '../../../../../helper/LayoutHelper.js';\nimport { IRect } from '../../../../../../interfaces/IRect.js';\nimport { IPoint } from '../../../../../../interfaces/IPoint.js';\nimport { calculateOuterRect } from '../../../../../helper/ElementHelper.js';\nimport { IDesignItem } from '../../../../../item/IDesignItem.js';\n\nexport class TransformToolPopup extends DraggableToolWindow {\n  private _designerCanvas: IDesignerCanvas;\n  private _previousSelectionRect: IRect;\n  private _selectionChanged: boolean;\n\n  private _relativeButton: HTMLButtonElement;\n  private _absoluteButton: HTMLButtonElement;\n  private _applyButton: HTMLButtonElement;\n\n  private _inputX: HTMLInputElement;\n  private _inputY: HTMLInputElement;\n  private _inputR: HTMLInputElement;\n  private _inputSpacingX: HTMLInputElement;\n  private _inputSpacingY: HTMLInputElement;\n\n  private _originTopLeft: HTMLInputElement;\n  private _originTopMid: HTMLInputElement;\n  private _originTopRight: HTMLInputElement;\n  private _originMidLeft: HTMLInputElement;\n  private _originMidMid: HTMLInputElement;\n  private _originMidRight: HTMLInputElement;\n  private _originBotLeft: HTMLInputElement;\n  private _originBotMid: HTMLInputElement;\n  private _originBotRight: HTMLInputElement;\n\n  private _transformMode: \"relative\" | \"absolute\";\n  private _transformOrigin: \"topLeft\" | \"topMid\" | \"topRight\" | \"midLeft\" | \"midMid\" | \"midRight\" | \"botLeft\" | \"botMid\" | \"botRight\";\n\n  protected override get windowTitle(): string { return 'Transform'; }\n\n  protected override get windowContentStyle(): CSSStyleSheet {\n    return css`\n      .inputs{\n        float: left;\n        margin-top: 5px;\n        align-items: center;\n      }\n      .input {\n        display: flex;\n        align-items: center; \n        margin-top: 5px;\n      }\n      .text {\n        margin-left: 5px;\n        font-size: 14px;\n      }\n      #input-div{\n        display: grid;\n        grid-template-columns: 1fr 9fr;\n        grid-template-rows: 25px 25px 25px;\n        grid-row-gap: 2px;\n        font-size: small;\n        margin: 10px;\n        color: white;\n      }\n      #button-div{\n        display: grid;\n        grid-template-columns: 1fr 1fr;\n        grid-template-rows: 25px;\n        font-size: small;\n        margin: 10px;\n        grid-column-gap: 5px\n      }\n      #apply-div{\n        font-size: small;\n        justify-content: center;\n        width: 100%;\n        margin-top: 10px;\n        margin-bottom: 10px;\n        display: flex;\n      }\n      #cube{\n        display: grid;\n        grid-template-columns: 20px 20px 20px;\n        grid-template-rows: 20px 20px 20px;\n        grid-gap: 10px;\n        padding: 10px;\n        top: -80px;\n        position: relative;\n      }\n      #cube-background{\n        width: 60px;\n        height: 60px;\n        background: gray;\n        margin-top: 20px;\n        margin-left: 20px;\n      }\n      #spacing-div{\n        display: inline-grid;\n        grid-template-rows: 20px 20px;\n        grid-template-columns: 95px 95px;\n        gap: 5px;\n        justify-content: center;\n        align-items: center;\n        width: 100%;\n        color: white;\n      }\n    `;\n  }\n\n  protected override get windowTemplate(): string {\n    return `\n      <div id=\"input-div\">\n        <span>X:</span>\n        <input type=\"number\" id=\"transform-input-x\">\n        <span>Y:</span>\n        <input type=\"number\" id=\"transform-input-y\">\n        <span>R:</span>\n        <input type=\"number\" id=\"transform-input-r\">\n      </div>\n      <div id=\"button-div\">\n        <button id=\"transform-button-absolute\">absolute</button>\n        <button id=\"transform-button-relative\">relative</button>\n      </div>\n\n      <div id=\"spacing-div\">\n          <span>X spacing</span>\n          <span>Y spacing</span>\n          <input type=\"number\" id=\"spacing-input-x\">\n          <input type=\"number\" id=\"spacing-input-y\">\n      </div>\n\n      <div style=\"justify-content: center; display: grid; height: 100px\">\n        <div id=\"cube-background\"></div>\n          <div id=\"cube\">\n            <input id=\"origin-top-left\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-top-mid\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-top-right\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-mid-left\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-mid-mid\" type=\"radio\" name=\"origin-radio\" checked>\n            <input id=\"origin-mid-right\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-bot-left\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-bot-mid\" type=\"radio\" name=\"origin-radio\">\n            <input id=\"origin-bot-right\" type=\"radio\" name=\"origin-radio\">\n          </div>\n      </div>\n\n      <div id=\"apply-div\">\n        <button id=\"transform-button-apply\" style=\"width:100px;\">apply</button>\n      </div>`;\n  }\n\n  constructor(designerCanvas?: IDesignerCanvas) {\n    super();\n    this._designerCanvas = designerCanvas;\n\n    this._relativeButton = this._getDomElement<HTMLButtonElement>(\"transform-button-relative\");\n    this._absoluteButton = this._getDomElement<HTMLButtonElement>(\"transform-button-absolute\");\n    this._applyButton = this._getDomElement<HTMLButtonElement>(\"transform-button-apply\");\n\n    this._inputX = this._getDomElement<HTMLInputElement>(\"transform-input-x\");\n    this._inputY = this._getDomElement<HTMLInputElement>(\"transform-input-y\");\n    this._inputR = this._getDomElement<HTMLInputElement>(\"transform-input-r\");\n    this._inputSpacingX = this._getDomElement<HTMLInputElement>(\"spacing-input-x\");\n    this._inputSpacingY = this._getDomElement<HTMLInputElement>(\"spacing-input-y\");\n\n    this._originTopLeft = this._getDomElement<HTMLInputElement>(\"origin-top-left\");\n    this._originTopMid = this._getDomElement<HTMLInputElement>(\"origin-top-mid\");\n    this._originTopRight = this._getDomElement<HTMLInputElement>(\"origin-top-right\");\n    this._originMidLeft = this._getDomElement<HTMLInputElement>(\"origin-mid-left\");\n    this._originMidMid = this._getDomElement<HTMLInputElement>(\"origin-mid-mid\");\n    this._originMidRight = this._getDomElement<HTMLInputElement>(\"origin-mid-right\");\n    this._originBotLeft = this._getDomElement<HTMLInputElement>(\"origin-bot-left\");\n    this._originBotMid = this._getDomElement<HTMLInputElement>(\"origin-bot-mid\");\n    this._originBotRight = this._getDomElement<HTMLInputElement>(\"origin-bot-right\");\n\n    this._relativeButton.onclick = () => this._changePositionMode(\"relative\");\n    this._absoluteButton.onclick = () => this._changePositionMode(\"absolute\");\n    this._applyButton.onclick = () => this._applyTransform();\n\n    this._transformMode = \"relative\";\n    this._changePositionMode(this._transformMode);\n  }\n\n  private _changePositionMode(mode: \"relative\" | \"absolute\") {\n    if (mode == \"relative\") {\n      this._relativeButton.style.backgroundColor = \"#6F8A9D\";\n      this._relativeButton.style.color = \"black\"\n      this._absoluteButton.style.backgroundColor = \"#A4B5C1\";\n      this._absoluteButton.style.color = \"#77716E\"\n    }\n    else {\n      this._absoluteButton.style.backgroundColor = \"#6F8A9D\";\n      this._absoluteButton.style.color = \"black\"\n      this._relativeButton.style.backgroundColor = \"#A4B5C1\";\n      this._relativeButton.style.color = \"#77716E\"\n    }\n    this._transformMode = mode;\n  }\n\n  private _applyTransform() {\n    this._checkOrigin();\n    if (!this._designerCanvas) return;\n    let selection = this._designerCanvas.instanceServiceContainer.selectionService.selectedElements;\n    selection = filterChildPlaceItems(selection);\n\n    this._selectionChanged = false;\n    this._designerCanvas.instanceServiceContainer.selectionService.onSelectionChanged.once(() => {\n      this._selectionChanged = true;\n      this._previousSelectionRect = null;\n    });\n\n\n    if (selection.length != 0) {\n      let inputPos: IPoint = {\n        x: isNaN(this._inputX.valueAsNumber) ? null : this._inputX.valueAsNumber,\n        y: isNaN(this._inputY.valueAsNumber) ? null : this._inputY.valueAsNumber\n      }\n      let inputRotation = this._inputR.valueAsNumber ? this._inputR.valueAsNumber : 0;\n\n      let grp = selection[0].openGroup(\"Transform selection\")\n      if (!this._previousSelectionRect || this._selectionChanged)\n        this._previousSelectionRect = calculateOuterRect(selection, this._designerCanvas);\n      let origin = this._calculateTransformOriginPosition(this._previousSelectionRect);\n      for (let item of selection) {\n        let itemPos: IRect = {\n          x: parseFloat(item.getStyle(\"left\")),\n          y: parseFloat(item.getStyle(\"top\")),\n          width: parseFloat(item.getStyle(\"width\")),\n          height: parseFloat(item.getStyle(\"height\"))\n        }\n        let itemRotStyle = item.getStyle(\"transform\")\n        let itemRotation = 0;\n        if (itemRotStyle)\n          itemRotation = parseFloat(item.getStyle(\"transform\").replaceAll(\"rotate(\", \"\").replaceAll(\"deg)\", \"\"));\n        let newPos = this._calculateTransform(this._previousSelectionRect, origin, itemPos, inputRotation, inputPos, this._transformMode)\n\n        item.setStyle(\"left\", newPos.x.toString() + \"px\");\n        item.setStyle(\"top\", newPos.y.toString() + \"px\");\n\n        let rotation: number;\n        if (this._transformMode == 'relative')\n          rotation = itemRotation + inputRotation;\n        else\n          rotation = inputRotation;\n\n        while (rotation >= 360)\n          rotation -= 360;\n        if (rotation != 0)\n          item.setStyle(\"transform\", \"rotate(\" + rotation + \"deg)\");\n        else\n          item.removeStyle(\"transform\");\n      }\n\n      this._applySpacing(selection);\n\n      grp.commit();\n    }\n  }\n\n  private _calculateTransformOriginPosition(selectionRect: IRect): IPoint {\n    switch (this._transformOrigin) {\n      case \"topLeft\":\n        return { x: selectionRect.x, y: selectionRect.y }\n      case \"topMid\":\n        return { x: selectionRect.x + selectionRect.width / 2, y: selectionRect.y }\n      case \"topRight\":\n        return { x: selectionRect.x + selectionRect.width, y: selectionRect.y }\n      case \"midLeft\":\n        return { x: selectionRect.x, y: selectionRect.y + selectionRect.height / 2 }\n      case \"midMid\":\n        return { x: selectionRect.x + selectionRect.width / 2, y: selectionRect.y + selectionRect.height / 2 }\n      case \"midRight\":\n        return { x: selectionRect.x + selectionRect.width, y: selectionRect.y + selectionRect.height / 2 }\n      case \"botLeft\":\n        return { x: selectionRect.x, y: selectionRect.y + selectionRect.height }\n      case \"botMid\":\n        return { x: selectionRect.x + selectionRect.width / 2, y: selectionRect.y + selectionRect.height }\n      case \"botRight\":\n        return { x: selectionRect.x + selectionRect.width, y: selectionRect.y + selectionRect.height }\n    }\n  }\n\n  private _checkOrigin() {\n    if (this._originTopLeft.checked)\n      this._transformOrigin = \"topLeft\";\n    else if (this._originTopMid.checked)\n      this._transformOrigin = \"topMid\";\n    else if (this._originTopRight.checked)\n      this._transformOrigin = \"topRight\";\n    else if (this._originMidLeft.checked)\n      this._transformOrigin = \"midLeft\";\n    else if (this._originMidMid.checked)\n      this._transformOrigin = \"midMid\";\n    else if (this._originMidRight.checked)\n      this._transformOrigin = \"midRight\";\n    else if (this._originBotLeft.checked)\n      this._transformOrigin = \"botLeft\";\n    else if (this._originBotMid.checked)\n      this._transformOrigin = \"botMid\";\n    else if (this._originBotRight.checked)\n      this._transformOrigin = \"botRight\";\n  }\n\n\n  private _calculateTransform(selectionRect: IRect, origin: IPoint, itemRect: IRect, rotation: number, inputPos: IPoint, transformMode: 'relative' | 'absolute'): IPoint {\n    let newPoint: IPoint;\n    // convert deg in rad\n    rotation = rotation * (Math.PI / 180);\n    if (transformMode == 'absolute') {\n      if (inputPos.x)\n        inputPos.x = inputPos.x - selectionRect.x;\n      if (inputPos.y)\n        inputPos.y = inputPos.y - selectionRect.y;\n\n    }\n    origin = {\n      x: origin.x - itemRect.width / 2,\n      y: origin.y - itemRect.height / 2\n    }\n    let diffItemPosToOrigin: IPoint = {\n      x: itemRect.x - origin.x,\n      y: itemRect.y - origin.y\n    }\n\n    newPoint = {\n      x: Math.cos(rotation) * diffItemPosToOrigin.x - Math.sin(rotation) * diffItemPosToOrigin.y + origin.x + inputPos.x,\n      y: Math.sin(rotation) * diffItemPosToOrigin.x + Math.cos(rotation) * diffItemPosToOrigin.y + origin.y + inputPos.y\n    }\n\n    return newPoint;\n  }\n\n  private _applySpacing(selection: IDesignItem[]) {\n    let xSpacing = this._inputSpacingX.valueAsNumber;\n    let ySpacing = this._inputSpacingY.valueAsNumber;\n\n    if (!isNaN(xSpacing)) {\n      let sortedSelectionX = selection.sort((a, b) => {\n        return parseFloat(a.getStyle(\"left\")) - parseFloat(b.getStyle(\"left\"));\n      });\n      let xStartPos = parseFloat(sortedSelectionX[0].getStyle(\"left\"));\n      for (let i = 0; i < sortedSelectionX.length; i++) {\n        sortedSelectionX[i].setStyle(\"left\", (i * xSpacing + xStartPos) + \"px\");\n      }\n    }\n\n    if (!isNaN(ySpacing)) {\n      let sortedSelectionY = selection.sort((a, b) => {\n        return parseFloat(a.getStyle(\"top\")) - parseFloat(b.getStyle(\"top\"));\n      });\n      let yStartPos = parseFloat(sortedSelectionY[0].getStyle(\"top\"));\n      for (let i = 0; i < sortedSelectionY.length; i++) {\n        sortedSelectionY[i].setStyle(\"top\", (i * ySpacing + yStartPos) + \"px\");\n      }\n    }\n\n  }\n}\n\n\ncustomElements.define('node-projects-designer-transform-tool-popup', TransformToolPopup);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/layerDepthView/ILayerDepthView.ts",
    "content": "export interface ILayerDepthView {\n\n}\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/layerDepthView/layerDepthView.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, Disposable } from \"@node-projects/base-custom-webcomponent\";\nimport { InstanceServiceContainer } from \"../../services/InstanceServiceContainer.js\";\nimport { IDesignItem } from \"../../item/IDesignItem.js\";\nimport { NodeType } from \"../../item/NodeType.js\";\nimport { DesignerCanvas } from \"../designerView/designerCanvas.js\";\nimport { ILayerDepthView } from \"./ILayerDepthView.js\";\n\ninterface LayerDepthEntry {\n  element: HTMLElement | SVGElement;\n  depth: number;\n}\n\nexport class LayerDepthView extends BaseCustomWebComponentConstructorAppend implements ILayerDepthView {\n\n  static override readonly style = css`\n        :host {\n          overflow: hidden;\n          display: block;\n          width: 100%;\n          height: 100%;\n          position: relative;\n          isolation: isolate;\n          background: #f3f4f6;\n          color: #111827;\n          user-select: none;\n          -webkit-user-select: none;\n        }\n        #viewport {\n          position: absolute;\n          inset: 0;\n          overflow: hidden;\n          cursor: grab;\n          z-index: 0;\n          perspective: 9000px;\n        }\n        #viewport.dragging {\n          cursor: grabbing;\n        }\n        #scene {\n          position: absolute;\n          top: 50%;\n          left: 50%;\n          z-index: 0;\n          transform-style: preserve-3d;\n          transform-origin: center center;\n        }\n        #content {\n          position: absolute;\n          top: 0;\n          left: 0;\n          transform-style: preserve-3d;\n          transform-origin: top left;\n          pointer-events: none;\n        }\n        #content, #content * {\n          transform-style: preserve-3d;\n        }\n        #empty {\n          position: absolute;\n          inset: 0;\n          display: none;\n          align-items: center;\n          justify-content: center;\n          color: #4b5563;\n          font: 13px sans-serif;\n          pointer-events: none;\n        }\n        #controls {\n          position: absolute;\n          left: 8px;\n          right: 8px;\n          top: 8px;\n          z-index: 2147483647;\n          display: grid;\n          grid-template-columns: auto minmax(100px, 1fr) auto;\n          align-items: center;\n          gap: 8px;\n          box-sizing: border-box;\n          padding: 7px 9px;\n          background: rgba(255, 255, 255, 0.92);\n          border: 1px solid rgba(17, 24, 39, 0.14);\n          font: 12px sans-serif;\n          pointer-events: auto;\n          z-index: 1;\n          position: relative;\n        }\n        #depthSlider {\n          width: 100%;\n          min-width: 0;\n        }\n        #depthValue {\n          min-width: 42px;\n          text-align: right;\n          font-variant-numeric: tabular-nums;\n        }`;\n\n  static override readonly template = html`\n        <div id=\"controls\">\n          <span>Depth</span>\n          <input id=\"depthSlider\" type=\"range\" min=\"0\" max=\"120\" step=\"1\" value=\"36\">\n          <span id=\"depthValue\">36px</span>\n        </div>\n        <div id=\"viewport\">\n          <div id=\"scene\">\n            <div id=\"content\"></div>\n          </div>\n          <div id=\"empty\">No layers</div>\n        </div>`;\n\n  private _viewport: HTMLDivElement;\n  private _scene: HTMLDivElement;\n  private _content: HTMLDivElement;\n  private _empty: HTMLDivElement;\n  private _depthSlider: HTMLInputElement;\n  private _depthValue: HTMLSpanElement;\n  private _instanceServiceContainer: InstanceServiceContainer;\n  private _contentChangedHandler: Disposable;\n  private _zoomFactorChangedHandler: Disposable;\n  private _resizeObserver: ResizeObserver;\n  private _reRenderFlag = false;\n  private _maxX = 0;\n  private _maxY = 0;\n  private _depthSpacing = 36;\n  private _rotateX = 0;\n  private _rotateY = 0;\n  private _translateX = 0;\n  private _translateY = 0;\n  private _scale = 1;\n  private _layerDepthEntries: LayerDepthEntry[] = [];\n  private _dragStart: {\n    x: number;\n    y: number;\n    rotateX: number;\n    rotateY: number;\n    translateX: number;\n    translateY: number;\n    pan: boolean;\n  };\n  private _boundPointerMove: (event: PointerEvent) => void;\n  private _boundPointerUp: (event: PointerEvent) => void;\n\n  constructor() {\n    super();\n    this._restoreCachedInititalValues();\n\n    this._viewport = this._getDomElement<HTMLDivElement>('viewport');\n    this._scene = this._getDomElement<HTMLDivElement>('scene');\n    this._content = this._getDomElement<HTMLDivElement>('content');\n    this._empty = this._getDomElement<HTMLDivElement>('empty');\n    this._depthSlider = this._getDomElement<HTMLInputElement>('depthSlider');\n    this._depthValue = this._getDomElement<HTMLSpanElement>('depthValue');\n\n    this._boundPointerMove = this._onPointerMove.bind(this);\n    this._boundPointerUp = this._onPointerUp.bind(this);\n\n    this._depthSlider.addEventListener('input', () => {\n      this._depthSpacing = Number(this._depthSlider.value);\n      this._depthValue.textContent = this._depthSpacing + 'px';\n      this._updateLayerDepths();\n    });\n    this._viewport.addEventListener('pointerdown', event => this._onPointerDown(event));\n    this._viewport.addEventListener('wheel', event => this._onWheel(event), { passive: false });\n    this._viewport.addEventListener('contextmenu', event => event.preventDefault());\n\n    this._resizeObserver = new ResizeObserver(() => this._fitScene());\n  }\n\n  ready() {\n    this._resizeObserver.observe(this);\n    this._applySceneTransform();\n  }\n\n  private _scheduleRender() {\n    if (this._reRenderFlag === false) {\n      this._reRenderFlag = true;\n      setTimeout(() => this._reRender(), 50);\n    }\n  }\n\n  private async _reRender() {\n    if (!this._instanceServiceContainer) {\n      return;\n    }\n\n    const designerCanvas = this._instanceServiceContainer.designerCanvas;\n    this._copyAdoptedStyleSheets(designerCanvas.rootDesignItem);\n\n    const pixelSize = designerCanvas.designerPixelSize;\n    this._maxX = Math.max(pixelSize.width, 1);\n    this._maxY = Math.max(pixelSize.height, 1);\n\n    const layerTree = this._createLayerTree();\n    this._content.replaceChildren(layerTree.fragment);\n    this._scene.style.width = this._maxX + 'px';\n    this._scene.style.height = this._maxY + 'px';\n    this._content.style.width = this._maxX + 'px';\n    this._content.style.height = this._maxY + 'px';\n    this._empty.style.display = layerTree.count ? 'none' : 'flex';\n\n    this._updateLayerDepths();\n    this._fitScene();\n    this._reRenderFlag = false;\n  }\n\n  private _copyAdoptedStyleSheets(rootDesignItem: IDesignItem) {\n    const rootElement = rootDesignItem?.element;\n    const usableContainer = rootDesignItem?.usableContainer;\n    const root = rootElement?.shadowRoot ?? (usableContainer instanceof Document ? usableContainer : rootElement?.ownerDocument);\n    this.shadowRoot.adoptedStyleSheets = root?.adoptedStyleSheets ? [...root.adoptedStyleSheets] : [];\n  }\n\n  private _createLayerTree(): { fragment: DocumentFragment, count: number } {\n    const rootDesignItem = this._instanceServiceContainer.rootDesignItem;\n    const fragment = document.createDocumentFragment();\n    let count = 0;\n    this._layerDepthEntries = [];\n\n    for (const designItem of rootDesignItem.children()) {\n      if (designItem.nodeType !== NodeType.Element) {\n        continue;\n      }\n\n      const clone = designItem.element.cloneNode(true) as Element;\n      fragment.appendChild(clone);\n      count = this._prepareCloneTree(designItem, clone, count);\n    }\n\n    return { fragment, count };\n  }\n\n  private _prepareCloneTree(designItem: IDesignItem, clone: Element, layerIndex: number): number {\n    this._prepareCloneElement(clone, layerIndex);\n    layerIndex++;\n\n    const originalChildNodes = Array.from(designItem.element.childNodes);\n    const clonedChildNodes = Array.from(clone.childNodes);\n\n    for (const childDesignItem of designItem.children()) {\n      if (childDesignItem.nodeType !== NodeType.Element) {\n        continue;\n      }\n\n      const childIndex = originalChildNodes.findIndex(node => node === childDesignItem.node);\n      const childClone = clonedChildNodes[childIndex];\n      if (childClone instanceof Element) {\n        layerIndex = this._prepareCloneTree(childDesignItem, childClone, layerIndex);\n      }\n    }\n\n    return layerIndex;\n  }\n\n  private _prepareCloneElement(clone: Element, layerIndex: number) {\n    if (clone instanceof HTMLElement || clone instanceof SVGElement) {\n      clone.style.transformStyle = 'preserve-3d';\n      clone.style.willChange = 'translate';\n      this._layerDepthEntries.push({\n        element: clone,\n        depth: layerIndex\n      });\n    }\n  }\n\n  private _updateLayerDepths() {\n    this._depthValue.textContent = this._depthSpacing + 'px';\n    if (!this._layerDepthEntries.length) {\n      return;\n    }\n    const depths = this._layerDepthEntries.map(entry => entry.depth);\n    const minDepth = Math.min(...depths);\n    const maxDepth = Math.max(...depths);\n    const depthRange = Math.max(maxDepth - minDepth, 1);\n    const depthToZ = (depth: number) => ((depth - minDepth) / depthRange - 0.5) * this._depthSpacing * 2;\n    const depthByElement = new Map<HTMLElement | SVGElement, number>();\n    for (const entry of this._layerDepthEntries) {\n      depthByElement.set(entry.element, entry.depth);\n    }\n    for (const entry of this._layerDepthEntries) {\n      let parentElement = entry.element.parentElement;\n      let parentDepth: number;\n      while (parentElement) {\n        parentDepth = depthByElement.get(parentElement);\n        if (parentDepth !== undefined) {\n          break;\n        }\n        parentElement = parentElement.parentElement;\n      }\n      const parentZ = parentDepth === undefined ? 0 : depthToZ(parentDepth);\n      entry.element.style.translate = '0 0 ' + (depthToZ(entry.depth) - parentZ) + 'px';\n    }\n  }\n\n  private _fitScene() {\n    const viewportRect = this._viewport.getBoundingClientRect();\n    if (!viewportRect.width || !viewportRect.height || !this._maxX || !this._maxY) {\n      return;\n    }\n\n    const fitScale = Math.min(viewportRect.width / this._maxX, viewportRect.height / this._maxY) * 0.72;\n    this._scale = Math.min(Math.max(fitScale, 0.05), 4);\n    this._translateX = 0;\n    this._translateY = 0;\n    this._applySceneTransform();\n  }\n\n  private _applySceneTransform() {\n    this._scene.style.transformStyle = 'preserve-3d';\n    this._content.style.transformStyle = 'preserve-3d';\n    this._scene.style.transform =\n      'translate(-50%, -50%) ' +\n      'translate(' + this._translateX + 'px, ' + this._translateY + 'px) ' +\n      'scale(' + this._scale + ') ' +\n      'rotateX(' + this._rotateX + 'deg) ' +\n      'rotateY(' + this._rotateY + 'deg)';\n    this._content.style.transform = '';\n  }\n\n  private _onPointerDown(event: PointerEvent) {\n    if (event.target === this._depthSlider || event.button !== 0 && event.button !== 1 && event.button !== 2) {\n      return;\n    }\n\n    this._dragStart = {\n      x: event.clientX,\n      y: event.clientY,\n      rotateX: this._rotateX,\n      rotateY: this._rotateY,\n      translateX: this._translateX,\n      translateY: this._translateY,\n      pan: event.button === 1 || event.button === 2 || event.shiftKey\n    };\n\n    this._viewport.classList.add('dragging');\n    this._viewport.setPointerCapture(event.pointerId);\n    this._viewport.addEventListener('pointermove', this._boundPointerMove);\n    this._viewport.addEventListener('pointerup', this._boundPointerUp);\n    this._viewport.addEventListener('pointercancel', this._boundPointerUp);\n    event.preventDefault();\n  }\n\n  private _onPointerMove(event: PointerEvent) {\n    if (!this._dragStart) {\n      return;\n    }\n\n    const dx = event.clientX - this._dragStart.x;\n    const dy = event.clientY - this._dragStart.y;\n\n    if (this._dragStart.pan) {\n      this._translateX = this._dragStart.translateX + dx;\n      this._translateY = this._dragStart.translateY + dy;\n    } else {\n      this._rotateY = this._dragStart.rotateY + dx * 0.35;\n      this._rotateX = Math.min(85, Math.max(-85, this._dragStart.rotateX - dy * 0.35));\n    }\n\n    this._applySceneTransform();\n    event.preventDefault();\n  }\n\n  private _onPointerUp(event: PointerEvent) {\n    this._dragStart = null;\n    this._viewport.classList.remove('dragging');\n    this._viewport.releasePointerCapture(event.pointerId);\n    this._viewport.removeEventListener('pointermove', this._boundPointerMove);\n    this._viewport.removeEventListener('pointerup', this._boundPointerUp);\n    this._viewport.removeEventListener('pointercancel', this._boundPointerUp);\n  }\n\n  private _onWheel(event: WheelEvent) {\n    const scaleDelta = event.deltaY < 0 ? 1.08 : 0.92;\n    this._scale = Math.min(6, Math.max(0.03, this._scale * scaleDelta));\n    this._applySceneTransform();\n    event.preventDefault();\n  }\n\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\n    this._contentChangedHandler?.dispose();\n    this._zoomFactorChangedHandler?.dispose();\n    this._instanceServiceContainer = value;\n    if (this._instanceServiceContainer) {\n      this._zoomFactorChangedHandler = (<DesignerCanvas>this._instanceServiceContainer.designerCanvas).onZoomFactorChanged.on(() => {\n        this._scheduleRender();\n      });\n      this._contentChangedHandler = this._instanceServiceContainer.onContentChanged.on(() => {\n        this._scheduleRender();\n      });\n      this._scheduleRender();\n    } else {\n      this._content.replaceChildren();\n      this._layerDepthEntries = [];\n      this._empty.style.display = 'flex';\n    }\n  }\n}\n\ncustomElements.define('node-projects-web-component-designer-layer-depth-view', LayerDepthView);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/miniatureView/IMiniatureView.ts",
    "content": "export interface IMiniatureView  {\r\n  \r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/miniatureView/miniatureView.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, Disposable } from \"@node-projects/base-custom-webcomponent\";\nimport { IMiniatureView } from \"./IMiniatureView.js\";\nimport { InstanceServiceContainer } from \"../../services/InstanceServiceContainer.js\";\nimport { DesignerCanvas } from \"../designerView/designerCanvas.js\";\n\nexport class MiniatureView extends BaseCustomWebComponentConstructorAppend implements IMiniatureView {\n\n  static override readonly style = css`\n        :host {\n          overflow:hidden;\n          display: block;\n          width: 100%;\n          height: 100%;\n          position: relative;\n        }\n        #outerDiv {\n          width: 100%;\n          height: 100%;\n        }\n        #innerDiv {\n          transform-origin: top left;\n          height: 100%;\n          width: 100%;\n          image-rendering: pixelated;\n        }\n        #above {\n          position: absolute;\n          top: 0;\n          left: 0;\n          width: 100%;\n          height: 100%;\n          pointer-events: auto;\n          background: transparent;\n          cursor: pointer;\n        }\n        #viewRect {\n          position: absolute;\n          top: 0;\n          left: 0;\n          width: 100%;\n          height: 100%;\n          border: 1px solid black;\n          pointer-events: none;\n        }`;\n\n  static override readonly template = html`\n        <div id=\"outerDiv\">\n          <div id=\"innerDiv\"></div> \n        </div>\n        <div id=\"above\">\n          <div id=\"viewRect\"></div>\n        </div>`;\n\n  private _innerDiv: HTMLDivElement;\n  private _outerDiv: HTMLDivElement;\n  private _above: HTMLDivElement;\n  private _instanceServiceContainer: InstanceServiceContainer;\n  private _contentChangedHandler: Disposable;\n  private _maxX = 0;\n  private _maxY = 0;\n  private _resizeObserver: ResizeObserver;\n  private _innerShadow: ShadowRoot;\n  private _zoomFactorChangedHandler: Disposable;\n  private _viewRect: HTMLDivElement;\n  private _minatureScaleX = 1;\n  private _minatureScaleY = 1;\n  private _reRenderFlag = false;\n  private _isDragging = false;\n  private _boundMouseMove: (e: MouseEvent) => void;\n  private _boundMouseUp: (e: MouseEvent) => void;\n\n  constructor() {\n    super();\n    this._restoreCachedInititalValues();\n\n    this._outerDiv = this._getDomElement<HTMLDivElement>('outerDiv');\n    this._innerDiv = this._getDomElement<HTMLDivElement>('innerDiv');\n    this._viewRect = this._getDomElement<HTMLDivElement>('viewRect');\n    this._above = this._getDomElement<HTMLDivElement>('above');\n    this._innerShadow = this._innerDiv.attachShadow({ mode: 'open' });\n\n    this._boundMouseMove = this._onMouseMove.bind(this);\n    this._boundMouseUp = this._onMouseUp.bind(this);\n\n    this._above.addEventListener('mousedown', (e) => this._onMouseDown(e));\n\n    this._resizeObserver = new ResizeObserver(() => {\n      this._reSize();\n    });\n  }\n\n  ready() {\n    this._resizeObserver.observe(this);\n  }\n\n  private _reSize() {\n    const outerRect = this._outerDiv.getBoundingClientRect();\n    this._minatureScaleX = outerRect.width / this._maxX;\n    this._minatureScaleY = outerRect.height / this._maxY;\n    this._innerDiv.style.scale = this._minatureScaleX + ' ' + this._minatureScaleY;\n  }\n\n  private async _reRender() {\n    if (this._instanceServiceContainer) {\n      const designerCanvas = this._instanceServiceContainer?.designerCanvas;\n      this._innerShadow.adoptedStyleSheets = [...designerCanvas.rootDesignItem.element.shadowRoot.adoptedStyleSheets];\n\n      const pixelSize = designerCanvas.designerPixelSize;\n      this._maxX = pixelSize.width;\n      this._maxY = pixelSize.height;\n\n      const miniatureViewContent = await this._instanceServiceContainer.designerCanvas.serviceContainer.miniatureViewService.provideMiniatureView(designerCanvas);\n      this._innerShadow.replaceChildren(miniatureViewContent);\n      \n      this._reSize();\n      this._reDrawRect();\n      this._reRenderFlag = false;\n    }\n  }\n\n  private _reDrawRect() {\n    const designerCanvas = this._instanceServiceContainer?.designerCanvas;\n    const offset = designerCanvas.canvasOffset;\n    const zoom = designerCanvas.zoomFactor;\n\n    this._viewRect.style.left = (-offset.x / this._maxX * 100) + '%';\n    this._viewRect.style.top = (-offset.y / this._maxY * 100) + '%';\n    this._viewRect.style.width = (designerCanvas.clientWidth / zoom / this._maxX * 100) + '%';\n    this._viewRect.style.height = (designerCanvas.clientHeight / zoom / this._maxY * 100) + '%';\n  }\n\n  private _onMouseDown(e: MouseEvent) {\n    if (!this._instanceServiceContainer) return;\n    this._isDragging = true;\n    this._moveCanvasToMousePosition(e);\n    window.addEventListener('mousemove', this._boundMouseMove);\n    window.addEventListener('mouseup', this._boundMouseUp);\n    e.preventDefault();\n  }\n\n  private _onMouseMove(e: MouseEvent) {\n    if (!this._isDragging) return;\n    this._moveCanvasToMousePosition(e);\n    e.preventDefault();\n  }\n\n  private _onMouseUp(e: MouseEvent) {\n    this._isDragging = false;\n    window.removeEventListener('mousemove', this._boundMouseMove);\n    window.removeEventListener('mouseup', this._boundMouseUp);\n  }\n\n  private _moveCanvasToMousePosition(e: MouseEvent) {\n    const designerCanvas = this._instanceServiceContainer.designerCanvas;\n    const zoom = designerCanvas.zoomFactor;\n    const aboveRect = this._above.getBoundingClientRect();\n\n    const mouseX = e.clientX - aboveRect.left;\n    const mouseY = e.clientY - aboveRect.top;\n\n    const contentX = (mouseX / aboveRect.width) * this._maxX;\n    const contentY = (mouseY / aboveRect.height) * this._maxY;\n\n    const halfViewW = designerCanvas.clientWidth / zoom / 2;\n    const halfViewH = designerCanvas.clientHeight / zoom / 2;\n\n    designerCanvas.canvasOffset = {\n      x: -(contentX - halfViewW),\n      y: -(contentY - halfViewH)\n    };\n  }\n\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\n    this._contentChangedHandler?.dispose()\n    this._zoomFactorChangedHandler?.dispose();\n    this._instanceServiceContainer = value;\n    if (this._instanceServiceContainer) {\n      this._zoomFactorChangedHandler = (<DesignerCanvas>this._instanceServiceContainer.designerCanvas).onZoomFactorChanged.on(() => {\n        this._reDrawRect();\n      });\n      this._contentChangedHandler = this._instanceServiceContainer.onContentChanged.on(e => {\n        if (this._reRenderFlag === false) {\n          this._reRenderFlag = true;\n          setTimeout(() => this._reRender(), 50);\n        }\n      });\n      if (this._reRenderFlag === false) {\n        this._reRenderFlag = true;\n        setTimeout(() => this._reRender(), 50);\n      }\n    } else {\n      this._innerShadow.innerHTML = \"\";\n    }\n  }\n}\n\ncustomElements.define('node-projects-web-component-designer-miniature-view', MiniatureView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/paletteView/paletteElements.ts",
    "content": "import { IElementDefinition } from '../../services/elementsService/IElementDefinition.js';\r\nimport { dragDropFormatNameElementDefinition } from '../../../Constants.js';\r\nimport { BaseCustomWebComponentLazyAppend, css, html } from '@node-projects/base-custom-webcomponent';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { NamedTools } from '../designerView/tools/NamedTools.js';\r\n\r\nexport class PaletteElements extends BaseCustomWebComponentLazyAppend {\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      box-sizing: border-box;\r\n      height: 100%;\r\n      overflow: auto;        \r\n    }\r\n\r\n    button {\r\n      background-color: transparent;\r\n      color: white;\r\n      border: none;\r\n      font-size: 13px;\r\n      display: block;\r\n      cursor: pointer;\r\n      width: 100%;\r\n      text-align: left;\r\n      padding: 8px 14px;\r\n    }\r\n    button:hover {\r\n      background: var(--light-grey, #383f52);\r\n    }\r\n\r\n    table {\r\n      width: 100%\r\n    }\r\n\r\n    td {\r\n      color: white;\r\n      font-size: 13px;\r\n    }\r\n\r\n    div {\r\n      text-transform: uppercase;\r\n      font-size: 12px;\r\n      font-weight: bold;\r\n      padding: 4px 14px;\r\n    }\r\n    `;\r\n\r\n  static override readonly template = html`\r\n    <table id=\"table\">\r\n    </table>\r\n  `;\r\n\r\n  private _table: HTMLTableElement;\r\n\r\n  constructor() {\r\n    super();\r\n\r\n    this._table = this._getDomElement<HTMLTableElement>('table');\r\n  }\r\n\r\n  loadElements(serviceContainer: ServiceContainer, elementDefintions: IElementDefinition[], relativeImagePath?: string) {\n    this._table.replaceChildren();\n    for (const elementDefintion of elementDefintions) {\n      const tr = document.createElement(\"tr\");\r\n\r\n      const tdEl = document.createElement(\"td\");\r\n\r\n      const button = document.createElement(\"button\");\r\n      button.setAttribute(\"part\", \"button\");\r\n      if (elementDefintion.icon && !elementDefintion.displayHtml) {\r\n        let icon = elementDefintion.icon;\r\n        if (!elementDefintion.icon.startsWith('data:')) {\r\n          icon = relativeImagePath + elementDefintion.icon;\r\n        }\r\n        button.innerHTML =\r\n          '<table><tr>' +\r\n          '<td align=\"left\" valign=\"middle\" style=\"width:20px;\"><img style=\"width:16px;height:16px\" src=\"' + icon + '\"></td>' +\r\n          '<td align=\"left\" >' + elementDefintion.tag + '</td>' +\r\n          '</tr></table>\\n';\r\n      }\r\n      else if (elementDefintion.displayHtml)\r\n        button.innerHTML = elementDefintion.displayHtml;\r\n      else\r\n        button.innerText = elementDefintion.name ? elementDefintion.name : elementDefintion.tag;\r\n      button.draggable = true;\r\n      button.ondragstart = (e) => {\r\n        e.dataTransfer.setData(dragDropFormatNameElementDefinition, JSON.stringify(elementDefintion));\r\n        (<HTMLElement>e.currentTarget).style.outline = \"dashed\";\r\n\r\n        if (elementDefintion.ghostElement) {\r\n          if (typeof elementDefintion.ghostElement === 'string') {\r\n            const range = document.createRange();\r\n            range.selectNode(document.body);\r\n            const fragment = range.createContextualFragment(elementDefintion.ghostElement);\r\n            let elem = fragment.firstChild as HTMLElement;\r\n            elem.style.position = \"absolute\";\r\n            elem.style.top = \"-1000px\";\r\n            document.body.appendChild(elem);\r\n            e.dataTransfer.setDragImage(elem, 0, 0);\r\n            requestAnimationFrame(() => document.body.removeChild(elem));\r\n          } else {\r\n            e.dataTransfer.setDragImage(elementDefintion.ghostElement, 0, 0);\r\n          }\r\n        }\r\n        else if (!elementDefintion.import) {\r\n          try {\r\n            let elem = document.createElement(elementDefintion.tag);\r\n            if (elementDefintion.defaultContent)\r\n              elem.innerHTML = elementDefintion.defaultContent;\r\n            if (elementDefintion.defaultWidth)\r\n              elem.style.width = elementDefintion.defaultWidth;\r\n            if (elementDefintion.defaultHeight)\r\n              elem.style.height = elementDefintion.defaultHeight;\r\n            if (elementDefintion.defaultAttributes) {\r\n              for (let a in elementDefintion.defaultAttributes) {\r\n                elem.setAttribute(a, elementDefintion.defaultAttributes[a]);\r\n              }\r\n            }\r\n            if (elementDefintion.defaultStyles) {\r\n              for (let s in elementDefintion.defaultStyles) {\r\n                elem.style[s] = elementDefintion.defaultStyles[s];\r\n              }\r\n            }\r\n            elem.style.position = \"absolute\";\r\n            if (elementDefintion.defaultWidth)\r\n              elem.style.top = \"-\" + (parseInt(elementDefintion.defaultHeight) + 500) + 'px';\r\n            else\r\n              elem.style.top = \"-500px\";\r\n            if (elementDefintion.defaultWidth)\r\n              elem.style.left = \"-\" + (parseInt(elementDefintion.defaultWidth) + 500) + 'px';\r\n            else\r\n              elem.style.left = \"-500px\";\r\n            document.body.appendChild(elem);\r\n\r\n            let rect = elem.getBoundingClientRect();\r\n            if (rect.width > 0 && rect.height > 0)\r\n              e.dataTransfer.setDragImage(elem, 0, 0);\r\n            requestAnimationFrame(() => document.body.removeChild(elem));\r\n          }\r\n          catch (err) {\r\n            console.warn(\"error creating gost elment\", err);\r\n          }\r\n        }\r\n      }\r\n      button.ondragend = (e) => {\r\n        elementDefintion.import = null;\r\n        (<HTMLElement>e.currentTarget).style.outline = \"none\";\r\n      }\r\n      button.ontouchstart = (e) => {\r\n        e.preventDefault();\r\n      }\r\n      button.onclick = (x) => {\r\n        let tool = serviceContainer.designerTools.get(elementDefintion.tool ?? NamedTools.DrawElementTool);\r\n        if (typeof tool == 'function')\r\n          tool = new tool(elementDefintion)\r\n        serviceContainer.globalContext.tool = tool;\r\n      }\r\n      tdEl.appendChild(button);\r\n      tr.appendChild(tdEl);\r\n\r\n      const tdDesc = document.createElement(\"td\");\r\n      tdDesc.innerText = elementDefintion.description ?? \"\";\r\n      tr.appendChild(tdDesc);\r\n\r\n      this._table.appendChild(tr);\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-palette-elements', PaletteElements);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/paletteView/paletteView.ts",
    "content": "import '../../controls/DesignerTabControl.js';\r\nimport { IElementsService } from '../../services/elementsService/IElementsService.js';\r\nimport { PaletteElements } from './paletteElements.js';\r\nimport { DesignerTabControl } from '../../controls/DesignerTabControl.js';\r\nimport { BaseCustomWebComponentLazyAppend, css } from '@node-projects/base-custom-webcomponent';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\n\r\nexport class PaletteView extends BaseCustomWebComponentLazyAppend {\r\n\r\n  public selected = 'native';\r\n\r\n  private _designerTabControl: DesignerTabControl;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: flex;\r\n      flex-direction: column;\r\n      flex: 1;\r\n      height: 100%;\r\n    }`;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    \r\n    this._designerTabControl = new DesignerTabControl();\r\n    this._designerTabControl.selectedIndex = 0;\r\n    this.shadowRoot.appendChild(this._designerTabControl);\r\n  }\r\n\r\n  public async loadControls(serviceContainer: ServiceContainer, elementsServices: IElementsService[]) {\r\n    this._designerTabControl.innerHTML = '';\r\n    for (const s of elementsServices) {\r\n      try {\r\n        let elements = await s.getElements();\r\n        let paletteElement = new PaletteElements();\r\n        paletteElement.title = s.name;\n        this._designerTabControl.appendChild(paletteElement);\n        paletteElement.loadElements(serviceContainer, elements);\n        s.onElementsChanged?.on(async () => {\n          paletteElement.loadElements(serviceContainer, await s.getElements());\n        });\n      } catch (err) {\n        console.warn('Error loading elements', err);\r\n      }\r\n    }\r\n    this._designerTabControl.refreshItems();\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-palette-view', PaletteView);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/propertyGrid/PropertyGrid.ts",
    "content": "import { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { PropertyGridPropertyList } from './PropertyGridPropertyList.js';\r\nimport { DesignerTabControl } from '../../controls/DesignerTabControl.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { BaseCustomWebComponentLazyAppend, css, Disposable } from '@node-projects/base-custom-webcomponent';\r\nimport { IContentChanged, InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { RefreshMode } from '../../services/propertiesService/IPropertiesService.js';\r\nimport { IPropertyGroup } from '../../services/propertiesService/IPropertyGroup.js';\r\nimport { IProperty } from '../../services/propertiesService/IProperty.js';\r\nimport { IContextMenuItem } from '../../helper/contextMenu/IContextMenuItem.js';\r\n\r\nexport class PropertyGrid extends BaseCustomWebComponentLazyAppend {\r\n\r\n  private _serviceContainer: ServiceContainer;\r\n  private _designerTabControl: DesignerTabControl;\r\n  private _selectedItems: IDesignItem[];\r\n  private _propertyGridPropertyLists: PropertyGridPropertyList[];\r\n  private _propertyGridPropertyListsDict: Record<string, PropertyGridPropertyList>;\r\n  private _nodeReplacedCb: Disposable;\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _selectionChangedHandler: Disposable;\r\n  private _contentChangedHandler: Disposable;\r\n\r\n  public propertyGroupHover: (group: IPropertyGroup, part: 'name' | 'desc') => boolean;\r\n  public propertyGroupClick: (group: IPropertyGroup, part: 'name' | 'desc') => void;\r\n  public propertyContextMenuProvider: (designItems: IDesignItem[], property: IProperty) => IContextMenuItem[];\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n    }\r\n    button:hover {\r\n      box-shadow: inset 0 3px 0 var(--light-grey);\r\n    }\r\n    button:focus {\r\n      box-shadow: inset 0 3px 0 var(--highlight-pink, #e91e63);\r\n    }\r\n    `;\r\n\r\n  static readonly properties = {\r\n    serviceContainer: Object,\r\n    instanceServiceContainer: Object,\r\n    selectedItems: Array,\r\n    propertyGroupHover: Function,\r\n    propertyGroupClick: Function,\r\n    propertyContextMenuProvider: Function\r\n  }\r\n\r\n  constructor() {\r\n    super();\r\n    this._designerTabControl = new DesignerTabControl();\r\n    this.shadowRoot.appendChild(this._designerTabControl);\r\n    this._restoreCachedInititalValues();\r\n    this.addEventListener('contextmenu', (e) => {\r\n      if ((<HTMLElement>e.composedPath()[0]).localName != 'input')\r\n        e.preventDefault()\r\n    });\r\n  }\r\n\r\n  public set serviceContainer(value: ServiceContainer) {\r\n    this._serviceContainer = value;\r\n    this._propertyGridPropertyLists = [];\r\n    this._propertyGridPropertyListsDict = {}\r\n  }\r\n  public get serviceContainer(): ServiceContainer {\r\n    return this._serviceContainer;\r\n  }\r\n\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    this._instanceServiceContainer = value;\r\n    this._selectionChangedHandler?.dispose()\r\n    this._contentChangedHandler?.dispose()\r\n    if (this._instanceServiceContainer) {\r\n      this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(e => {\r\n        this.selectedItems = e.selectedElements;\r\n      });\r\n      this._contentChangedHandler = this._instanceServiceContainer.onContentChanged.on(e => {\r\n        this._changeOccured(null, false);\r\n      });\r\n      this.selectedItems = this._instanceServiceContainer.selectionService.selectedElements;\r\n    } else {\r\n      this.selectedItems = [];\r\n    }\r\n  }\r\n\r\n  get selectedItems() {\r\n    return this._selectedItems;\r\n  }\r\n  set selectedItems(items: IDesignItem[]) {\r\n    if (this._selectedItems != items) {\r\n      this._selectedItems = items;\r\n      this._selectedItemsSet();\r\n    }\r\n  }\r\n\r\n  async _selectedItemsSet() {\r\n    const pgGroups = this._serviceContainer.propertyGroupService.getPropertygroups(this._selectedItems);\r\n    const visibleDict = new Set<string>()\r\n    for (let p of pgGroups) {\r\n      let lst = this._propertyGridPropertyListsDict[p.name];\r\n      if (!lst) {\r\n        lst = new PropertyGridPropertyList(this.serviceContainer);\r\n        lst.title = p.name;\r\n        lst.propertyGroupHover = this.propertyGroupHover;\r\n        lst.propertyGroupClick = this.propertyGroupClick;\r\n        lst.propertyContextMenuProvider = this.propertyContextMenuProvider;\r\n        this._designerTabControl.appendChild(lst);\r\n        this._propertyGridPropertyLists.push(lst);\r\n        this._propertyGridPropertyListsDict[p.name] = lst;\r\n      }\r\n      lst.setPropertiesService(p.propertiesService);\r\n      if (await lst.createElements(this._selectedItems[0]))\r\n        visibleDict.add(p.name);\r\n    }\r\n\r\n    let parentEl: HTMLElement = this._designerTabControl;\r\n\r\n    for (const v of visibleDict) {\r\n      const el = this._propertyGridPropertyListsDict[v];\r\n      const scrollTop = el.scrollTop;\r\n      if (parentEl === this._designerTabControl)\r\n        parentEl.insertAdjacentElement('afterbegin', el);\r\n      else\r\n        parentEl.insertAdjacentElement('afterend', el);\r\n      parentEl = el;\r\n      el.scrollTo(0, scrollTop);\r\n    }\r\n\r\n    for (let p of this._propertyGridPropertyLists) {\r\n      if (visibleDict.has(p.title))\r\n        p.style.display = 'block';\r\n      else\r\n        p.style.display = 'none';\r\n    }\r\n\r\n    this._designerTabControl.refreshItems();\r\n    if (this._designerTabControl.selectedIndex < 0)\r\n      this._designerTabControl.selectedIndex = 0;\r\n\r\n    for (const a of this._propertyGridPropertyLists) {\r\n      if (visibleDict.has(a.title))\r\n        a.designItemsChanged(this._selectedItems);\r\n    }\r\n\r\n    if (this._selectedItems) {\r\n      if (this._selectedItems.length == 1) {\r\n        for (const a of this._propertyGridPropertyLists) {\r\n          if (visibleDict.has(a.title))\r\n            a.refreshForDesignItems(this._selectedItems);\r\n        }\r\n        this._observePrimarySelectionForChanges();\r\n      }\r\n    } else {\r\n      this._nodeReplacedCb?.dispose();\r\n      this._nodeReplacedCb = null;\r\n    }\r\n  }\r\n\r\n  _blockDoubleRun = false;\r\n  async _changeOccured(change: IContentChanged, forceRecreate = false) {\r\n    if (!this._blockDoubleRun) {\r\n      this._blockDoubleRun = true;\r\n      const selItem = this._selectedItems[0];\r\n      if (selItem) {\r\n        for (const a of this._propertyGridPropertyLists) {\r\n          if (a.propertiesService?.getRefreshMode(selItem) == RefreshMode.fullOnValueChange) {\r\n            await a.createElements(selItem);\r\n            a.designItemsChanged(this._selectedItems);\r\n          }\r\n          a.refreshForDesignItems(this._selectedItems);\r\n        }\r\n      }\r\n      this._blockDoubleRun = false;\r\n    }\r\n  }\r\n\r\n  private _observePrimarySelectionForChanges() {\r\n    this._nodeReplacedCb?.dispose();\r\n    this._nodeReplacedCb = this._selectedItems[0].nodeReplaced.on(() => {\r\n      this._observePrimarySelectionForChanges();\r\n      this._changeOccured(null, true);\r\n    });\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-web-component-designer-property-grid', PropertyGrid);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/propertyGrid/PropertyGridPropertyList.ts",
    "content": "import { IProperty } from '../../services/propertiesService/IProperty.js';\r\nimport { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { BaseCustomWebComponentLazyAppend, css, DomHelper } from '@node-projects/base-custom-webcomponent';\r\nimport { IPropertyEditor } from '../../services/propertiesService/IPropertyEditor.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IPropertiesService, RefreshMode } from '../../services/propertiesService/IPropertiesService.js';\r\nimport { ValueType } from '../../services/propertiesService/ValueType.js';\r\nimport { ContextMenu } from '../../helper/contextMenu/ContextMenu.js';\r\nimport { PropertyType } from '../../services/propertiesService/PropertyType.js';\r\nimport { IPropertyGroup } from '../../services/propertiesService/IPropertyGroup.js';\r\nimport { dragDropFormatNameBindingObject, dragDropFormatNamePropertyGrid } from '../../../Constants.js';\r\nimport { IContextMenuItem } from '../../helper/contextMenu/IContextMenuItem.js';\r\n\r\nexport class PropertyGridPropertyList extends BaseCustomWebComponentLazyAppend {\r\n\r\n  private _div: HTMLDivElement;\r\n  private _propertyMap: Map<IProperty, { isSetElement: HTMLElement, labelElement: HTMLElement, editor: IPropertyEditor }> = new Map();\r\n  private _serviceContainer: ServiceContainer;\r\n  private _propertiesService: IPropertiesService;\r\n  private _designItems: IDesignItem[];\r\n  private _lastClassType: any;\r\n  private _addCounter: number = 0;\r\n\r\n  public propertyGroupHover: (group: IPropertyGroup, part: 'name' | 'desc') => boolean;\r\n  public propertyGroupClick: (group: IPropertyGroup, part: 'name' | 'desc') => void;\r\n  public propertyContextMenuProvider: (designItems: IDesignItem[], property: IProperty) => IContextMenuItem[];\r\n\r\n  public get propertiesService() {\r\n    return this._propertiesService;\r\n  }\r\n\r\n  static override get style() {\r\n    return css`\r\n    :host{\r\n      display: block;\r\n      height: 100%;\r\n      overflow: auto;\r\n      box-sizing: border-box;\r\n    }\r\n    .content-wrapper {\r\n      padding: .5em;\r\n      display: grid;\r\n      grid-template-columns: 11px minmax(80px, auto) minmax(120px, 1fr);\r\n      align-items: center;\r\n      grid-auto-rows: minmax(24px, auto);\r\n      align-items: center;\r\n    }\r\n    label, input, select {\r\n      display: inline-block;\r\n      color: white;\r\n      background: transparent;\r\n      margin: 2px 0;\r\n      padding: 0 2px 0 4px;\r\n      width: 110px;\r\n      white-space: nowrap;\r\n    }\r\n    label, .style-label {\r\n      box-sizing: border-box;\r\n      display: inline-block;\r\n      font-size: 13px;\r\n      width: auto;\r\n      overflow: hidden;\r\n      text-overflow: ellipsis;\r\n      margin-right: 2px;\r\n    }\r\n    input, select {\r\n      padding:0;\r\n      padding-left: 3px;\r\n    }\r\n    input, select {\r\n      border: none;\r\n    }\r\n    .editor-control, .content-wrapper > input, .content-wrapper > select {\r\n      border: 1px solid var(--input-border-color, #596c7a);\r\n      border-radius: 0;\r\n    }\r\n    .editor-control, input, select {\r\n      height: 24px;\r\n      box-sizing: border-box;\r\n      font-size: 11px;\r\n      width: 100%;\r\n      margin: 0;\r\n      /*min-width: 0;*/\r\n    }\r\n    .editor-control:focus, input:focus, select:focus {\r\n      outline: none;\r\n      box-shadow: none;\r\n    }\r\n    /*input {\r\n      margin-left: 4px;\r\n    }*/\r\n    .editor-control[disabled], input[disabled], select[disabled] {\r\n      color: #BDBDBD;\r\n    }\r\n    select {\r\n      background: transparent;\r\n    }\r\n    select:focus option {\r\n      color: black;\r\n    }\r\n    .unset-value {\r\n      color: lightslategray\r\n    }\r\n    .unset-value > * {\r\n      color: lightslategray\r\n    }\r\n    .unset-value:focus {\r\n      color: white\r\n    }\r\n    .group-header {\r\n      grid-column: 1 / 3;\r\n      font-size: 10px;\r\n      font-family: monospace;\r\n    }\r\n    .group-header[clickable]:hover {\r\n      cursor:pointer;\r\n      color: orange;\r\n      text-decoration: underline;\r\n    }\r\n    .group-header::after{\r\n      content: \" ▾\";\r\n      font-size: 14px;\r\n    }\r\n    .group-header.expanded::after{\r\n      content: \" ▴\";\r\n      font-size: 14px;\r\n    }\r\n    .group-desc {\r\n      display: inline-flex;\r\n      flex-direction: row-reverse;\r\n      font-size: 10px;\r\n      text-decoration: underline;\r\n    }\r\n    .group-desc[clickable]:hover {\r\n      cursor:pointer;\r\n      color: orange;\r\n      text-decoration: underline;\r\n    }\r\n    .dragOverProperty {\r\n      outline: 2px dashed orange;\r\n      outline-offset: -2px;\r\n    }\r\n    `;\r\n  }\r\n\r\n  constructor(serviceContainer: ServiceContainer) {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this._serviceContainer = serviceContainer;\r\n\r\n    this._div = document.createElement(\"div\");\r\n    this._div.className = \"content-wrapper\";\r\n    this.shadowRoot.appendChild(this._div);\r\n  }\r\n\r\n  public setPropertiesService(propertiesService: IPropertiesService) {\r\n    if (this._propertiesService != propertiesService) {\r\n      this._propertiesService = propertiesService;\r\n      DomHelper.removeAllChildnodes(this._div);\r\n      this._propertyMap.clear();\r\n    }\r\n  }\r\n\r\n  public async createElements(designItem: IDesignItem): Promise<boolean> {\r\n    if (!this._shouldCreateElements(designItem))\r\n      return true;\r\n\r\n    const properties = await this._propertiesService?.getProperties(designItem);\r\n    return this._rebuildElements(designItem, properties);\r\n  }\r\n\r\n  private _shouldCreateElements(designItem: IDesignItem): boolean {\r\n    if (!this._propertiesService)\r\n      return false;\r\n    if (this._propertyMap.size == 0)\r\n      return true;\r\n\r\n    const refreshMode = this._propertiesService.getRefreshMode(designItem);\r\n    return refreshMode !== RefreshMode.none && (refreshMode !== RefreshMode.fullOnClassChange || this._lastClassType !== designItem.element.constructor);\r\n  }\r\n\r\n  private _rebuildElements(designItem: IDesignItem, properties: IProperty[] | IPropertyGroup[]): boolean {\r\n    this._lastClassType = designItem.element.constructor;\r\n    DomHelper.removeAllChildnodes(this._div);\r\n    this._propertyMap.clear();\r\n\r\n    if (properties?.length) {\r\n      for (let p of properties) {\r\n        if ('properties' in p)\r\n          this.createPropertyGroups(<IPropertyGroup>p);\r\n        else\r\n          this.createPropertyEditors(<IProperty>p);\r\n      }\r\n      return true;\r\n    }\r\n\r\n    return false;\r\n  }\r\n\r\n  private createPropertyGroups(group: IPropertyGroup) {\r\n    let header = document.createElement('span');\r\n    header.addEventListener('click', () => { this.expandOrCollapsePropertyGroups(group); header.classList.toggle('expanded') });\r\n    header.innerHTML = group.name.replaceAll(\"\\n\", \"<br>\");\r\n    header.className = 'group-header';\r\n    this._div.appendChild(header);\r\n    let desc = document.createElement('span');\r\n    desc.innerHTML = group.description ?? '';\r\n    desc.className = 'group-desc';\r\n    if (this.propertyGroupHover) {\r\n      header.onmouseenter = () => {\r\n        if (this.propertyGroupHover(group, 'name'))\r\n          header.setAttribute('clickable', '')\r\n        else\r\n          header.removeAttribute('clickable')\r\n      }\r\n      header.onclick = () => {\r\n        if (this.propertyGroupClick)\r\n          this.propertyGroupClick(group, 'name');\r\n      }\r\n      desc.onmouseenter = () => {\r\n        if (this.propertyGroupHover(group, 'desc'))\r\n          desc.setAttribute('clickable', '')\r\n        else\r\n          desc.removeAttribute('clickable')\r\n      }\r\n      desc.onclick = () => {\r\n        if (this.propertyGroupClick)\r\n          this.propertyGroupClick(group, 'desc');\r\n      }\r\n    }\r\n    this._div.appendChild(desc);\r\n    for (const p of group.properties)\r\n      this.createPropertyEditors(p, true);\r\n  }\r\n\r\n  private expandOrCollapsePropertyGroups(propertyGroup: IPropertyGroup) {\r\n    for (let p of propertyGroup.properties) {\r\n      const property = this._propertyMap.get(p);\r\n      const displayStyle = property.labelElement.style.display == 'none' ? 'flex' : 'none';\r\n      if (property.editor.element)\r\n        (<HTMLElement>property.editor.element).style.display = displayStyle;\r\n      if (property.labelElement)\r\n        property.labelElement.style.display = displayStyle;\r\n      if (property.isSetElement.parentElement)\r\n        property.isSetElement.parentElement.style.display = displayStyle;\r\n    }\r\n  }\r\n\r\n  private createPropertyEditors(property: IProperty, isInGroup?: boolean) {\r\n    let editor: IPropertyEditor;\r\n    let labelHolder: HTMLElement;\r\n    if (property.createEditor)\r\n      editor = property.createEditor(property);\r\n    else {\r\n      editor = this._serviceContainer.forSomeServicesTillResult(\"propertyEditorTypesService\", x => x.getEditorForProperty(property));\r\n    }\r\n    if (editor) {\r\n      let rectContainer = document.createElement(\"div\")\r\n      if (isInGroup)\r\n        rectContainer.style.marginLeft = '10px';\r\n      rectContainer.style.width = '20px';\r\n      rectContainer.style.height = '20px';\r\n      rectContainer.style.display = 'flex';\r\n      rectContainer.style.alignItems = 'center';\r\n      let rect = document.createElement(\"div\")\r\n      rect.style.width = '7px';\r\n      rect.style.height = '7px';\r\n      rect.style.border = '1px white solid';\r\n      rect.style.cursor = 'pointer';\r\n      if (property.propertyType != PropertyType.complex)\r\n        rectContainer.appendChild(rect);\r\n      this._div.appendChild(rectContainer);\r\n      if (property.readonly !== true) {\r\n        rect.oncontextmenu = (event) => {\r\n          event.preventDefault();\r\n          this.openContextMenu(event, property);\r\n        }\r\n        rect.onclick = (event) => {\r\n          event.preventDefault();\r\n          this.openContextMenu(event, property);\r\n        }\r\n      }\r\n      if (property.type == 'addNew') {\r\n        let input = <HTMLInputElement>editor.element;\r\n        input.disabled = true;\r\n        input.id = \"addNew_input_\" + (++this._addCounter);\r\n        let label = document.createElement(\"input\");\r\n        labelHolder = label;\r\n        if (isInGroup) {\r\n          label.style.marginLeft = '10px';\r\n          label.style.width = 'calc(100% - 10px)';\r\n        }\r\n        label.value = property.name;\r\n        label.type = \"text\";\r\n        label.id = \"addNew_label_\" + this._addCounter;\r\n        label.onkeyup = e => {\r\n          if (e.key == 'Enter' && label.value) {\r\n            property.name = label.value;\r\n            label.disabled = true;\r\n            input.disabled = false;\r\n            input.focus();\r\n          }\r\n        }\r\n        if (property.service.getPropertyNameSuggestions) {\r\n          const sug = property.service.getPropertyNameSuggestions(null); //TODO: design items?\r\n          const dl = document.createElement(\"datalist\");\r\n          dl.id = \"addNew_\" + this._addCounter + \"_datalist\";\r\n          for (let s of sug) {\r\n            const op = document.createElement(\"option\");\r\n            op.value = s;\r\n            dl.append(op);\r\n          }\r\n          this._div.appendChild(dl);\r\n          label.setAttribute('list', dl.id);\r\n        }\r\n        this._div.appendChild(label);\r\n      } else {\n        if (property.hideLabel) {\n          labelHolder = document.createElement(\"span\");\n        } else if (!property.renamable) {\n          let label = document.createElement(\"label\");\n          labelHolder = label;\n          if (isInGroup)\n            label.style.marginLeft = '10px';\r\n          label.htmlFor = property.name;\r\n          label.textContent = property.displayName ?? property.name;\r\n          label.title = property.description ?? ((property.displayName ?? property.name) + ' (type: ' + property.type + (property.defaultValue ? ', default: ' + property.defaultValue : '') + ', propertytype: ' + property.propertyType + ')');\r\n          label.ondragleave = (e) => this._onDragLeave(e, property, label);\r\n          label.ondragover = (e) => this._onDragOver(e, property, label);\r\n          label.ondrop = (e) => this._onDrop(e, property, label);\r\n          this._div.appendChild(label);\r\n        } else {\r\n          let label = document.createElement(\"input\");\r\n          labelHolder = label;\r\n          if (isInGroup) {\r\n            label.style.marginLeft = '10px';\r\n            label.style.width = 'calc(100% - 10px)';\r\n          }\r\n          label.id = 'label_' + property.name;\r\n          let input = <HTMLInputElement>editor.element;\r\n          label.value = property.name;\r\n          label.onkeyup = async e => {\r\n            if (e.key == 'Enter' && label.value) {\r\n              const pg = this._designItems[0].openGroup(\"rename property name from '\" + property.name + \"' to '\" + label.value + \"'\");\r\n              property.service.clearValue(this._designItems, property, 'all');\r\n              property.name = label.value;\r\n              await property.service.setValue(this._designItems, property, input.value);\r\n              pg.commit();\r\n              this._designItems[0].instanceServiceContainer.designerCanvas.extensionManager.refreshAllExtensions(this._designItems);\r\n            }\r\n          }\r\n          this._div.appendChild(label);\r\n        }\r\n      }\r\n      if (property.name)\r\n        editor.element.id = property.name;\r\n      if (editor.element instanceof HTMLElement)\n        editor.element.classList.add('editor-control');\n      if (property.hideLabel)\n        (<HTMLElement>editor.element).style.gridColumn = '2 / 4';\n      this._div.appendChild(editor.element);\n\r\n      this._propertyMap.set(property, { isSetElement: rect, labelElement: labelHolder, editor: editor });\r\n    }\r\n  }\r\n\r\n  private _onDragLeave(event: DragEvent, property: IProperty, label: HTMLLabelElement) {\r\n    event.preventDefault();\r\n    label.classList.remove('dragOverProperty');\r\n  }\r\n\r\n  private _onDragOver(event: DragEvent, property: IProperty, label: HTMLLabelElement) {\r\n    event.preventDefault();\r\n    const hasTransferDataBindingObject = event.dataTransfer.types.indexOf(dragDropFormatNameBindingObject) >= 0;\r\n    if (hasTransferDataBindingObject) {\r\n      const ddService = this._serviceContainer.bindableObjectDragDropService;\r\n      if (ddService) {\r\n        const effect = ddService.dragOverOnProperty(event, property, this._designItems);\r\n        if ((effect ?? 'none') != 'none') {\r\n          label.classList.add('dragOverProperty');\r\n          event.dataTransfer.dropEffect = effect;\r\n        } else {\r\n          label.classList.remove('dragOverProperty');\r\n        }\r\n      }\r\n    }\r\n\r\n    const hasPropertyGrid = event.dataTransfer.types.indexOf(dragDropFormatNamePropertyGrid) >= 0;\r\n    if (hasPropertyGrid) {\r\n      const ddService = this._serviceContainer.propertyGridDragDropService;\r\n      if (ddService) {\r\n        const effect = ddService.dragOverOnProperty(event, property, this._designItems);\r\n        if ((effect ?? 'none') != 'none') {\r\n          label.classList.add('dragOverProperty');\r\n          event.dataTransfer.dropEffect = effect;\r\n        } else {\r\n          label.classList.remove('dragOverProperty');\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  private _onDrop(event: DragEvent, property: IProperty, label: HTMLLabelElement) {\r\n    event.preventDefault();\r\n    label.classList.remove('dragOverProperty');\r\n    const transferDataBindingObject = event.dataTransfer.getData(dragDropFormatNameBindingObject)\r\n    if (transferDataBindingObject) {\r\n      const bo = JSON.parse(transferDataBindingObject);\r\n      const ddService = this._serviceContainer.bindableObjectDragDropService;\r\n      if (ddService) {\r\n        ddService.dropOnProperty(event, property, bo, this._designItems);\r\n      }\r\n    }\r\n\r\n    const transferDataPropertyGrid = event.dataTransfer.getData(dragDropFormatNamePropertyGrid)\r\n    if (transferDataPropertyGrid) {\r\n      const dropObj = JSON.parse(transferDataPropertyGrid);\r\n      const ddService = this._serviceContainer.propertyGridDragDropService;\r\n      if (ddService) {\r\n        ddService.dropOnProperty(event, property, dropObj, this._designItems);\r\n      }\r\n    }\r\n  }\r\n\r\n  public openContextMenu(event: MouseEvent, property: IProperty) {\r\n    let ctxMenuItems: IContextMenuItem[];\r\n    if (this.propertyContextMenuProvider)\r\n      ctxMenuItems = this.propertyContextMenuProvider(this._designItems, property)\r\n    if (!ctxMenuItems)\r\n      ctxMenuItems = property.service.getContextMenu(this._designItems, property);\r\n    ContextMenu.show(ctxMenuItems, event);\r\n  }\r\n\r\n  public designItemsChanged(designItems: IDesignItem[]) {\r\n    this._designItems = designItems;\r\n    for (let m of this._propertyMap) {\r\n      m[1].editor.designItemsChanged(designItems);\r\n    }\r\n  }\r\n\r\n  public refreshForDesignItems(items: IDesignItem[]) {\r\n    for (let m of this._propertyMap) {\r\n      PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(m[1].isSetElement, m[0], items, this._propertiesService, m[1].editor);\r\n    }\r\n  }\r\n\r\n  public static refreshIsSetElementAndEditorForDesignItems(isSetElement: HTMLElement, property: IProperty, items: IDesignItem[], propertiesService: IPropertiesService, editor?: IPropertyEditor) {\r\n    if (items && items.length) {\r\n      let s = propertiesService.isSet(items, property);\r\n      let v = propertiesService.getValue(items, property);\r\n      isSetElement.title = property.name + ': ' + s;\r\n      if (s == ValueType.none) {\r\n        isSetElement.style.background = '';\r\n        v = propertiesService.getUnsetValue(items, property);\r\n      }\r\n      else if (s == ValueType.all)\r\n        isSetElement.style.background = 'white';\r\n      else if (s == ValueType.some)\r\n        isSetElement.style.background = 'gray';\r\n      else if (s == ValueType.bound)\r\n        isSetElement.style.background = 'orange';\r\n      else if (s == ValueType.fromStylesheet) {\r\n        v = propertiesService.getUnsetValue(items, property);\r\n        isSetElement.style.background = 'yellow';\r\n      }\r\n      editor?.refreshValueWithoutNotification(s, v);\r\n    } else {\r\n      isSetElement.style.background = '';\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-property-grid-property-list', PropertyGridPropertyList);\n"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/propertyGrid/PropertyGridWithHeader.ts",
    "content": "import { ServiceContainer } from '../../services/ServiceContainer.js';\r\nimport { BaseCustomWebComponentLazyAppend, css, Disposable, html } from '@node-projects/base-custom-webcomponent';\r\nimport { PropertyGrid } from './PropertyGrid.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { sleep } from '../../helper/Helper.js';\r\nimport { NodeType } from '../../item/NodeType.js';\r\nimport { PropertyGridPropertyList } from './PropertyGridPropertyList.js';\r\nimport { ContentAndIdPropertiesService } from '../../services/propertiesService/services/ContentAndIdPropertiesService.js';\r\nimport { IProperty } from '../../services/propertiesService/IProperty.js';\r\nimport { IContextMenuItem } from '../../helper/contextMenu/IContextMenuItem.js';\r\nimport { ContextMenu } from '../../helper/contextMenu/ContextMenu.js';\r\n//import { doesContentChangeAffectDesignItems } from '../../services/contentService/IContentChanged.js';\r\n\r\nexport class PropertyGridWithHeader extends BaseCustomWebComponentLazyAppend {\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      user-select: none;\r\n      -webkit-user-select: none;\r\n      background: var(--medium-grey, #2f3545);\r\n      color: white;\r\n    }\r\n    div.root {\r\n      display: grid;\r\n      grid-template-columns: 11px 11px auto 1fr auto;\r\n      padding: 3px 6px;\r\n      font-family: monospace; \r\n      align-items: center;\r\n    }\r\n    .desc {\r\n      font-weight: 700;\r\n      font-size: 10px;\r\n      margin-right: 5px;\r\n    }\r\n    input {\r\n      background: var(--medium-grey, #2f3545);\r\n      border: solid 1px gray;\r\n      color: white;\r\n      width: calc(100% - 6px);\r\n    }\r\n    #type {\r\n      color: wheat;\r\n      white-space: nowrap;\r\n      overflow: hidden;\r\n      font-size: 12px;\r\n      height: 20px;\r\n      border: none;\r\n    }\r\n    #pg {\r\n      height: calc(100% - 64px);\r\n    }\r\n    `;\r\n\r\n  static override readonly template = html`\r\n  <div class=\"root\">\r\n    <span style=\"grid-column: span 3;\" class=\"desc\">Type:</span><input type=\"text\" readonly id=\"type\">\r\n    <button id=\"config\" style=\"display: none; grid-column: 5; grid-row: span 3; height: calc(100% - 10px); margin-left: 10px;\">config</button>\r\n    <div title=\"id\" id=\"idRect\" style=\"grid-column: 1; width: 7px; height: 7px; border: 1px solid white;\"></div>\r\n    <span id=\"idSpan\" style=\"grid-column: span 2;\" class=\"desc\">Id:</span><input type=\"text\" id=\"id\">\r\n    <div title=\"innerHTML\" id=\"innerRect\" style=\"grid-column: 1; width: 7px; height: 7px; border: 1px solid white;\"></div>\r\n    <div title=\"textContent\" id=\"contentRect\" style=\"width: 7px; height: 7px; border: 1px solid white;\"></div>\r\n    <span id=\"contentSpan\" class=\"desc\">Content:</span><input type=\"text\" id=\"content\">\r\n  </div>\r\n  <node-projects-web-component-designer-property-grid id=\"pg\"></node-projects-web-component-designer-property-grid>`\r\n\r\n  public propertyGrid: PropertyGrid;\r\n\r\n  private _type: HTMLInputElement;\r\n  private _id: HTMLInputElement;\r\n  private _content: HTMLInputElement;\r\n  private _selectionChangedHandler: Disposable;\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _idRect: HTMLDivElement;\r\n  private _contentRect: HTMLDivElement;\r\n  private _innerRect: HTMLDivElement;\r\n  private _propertiesService: ContentAndIdPropertiesService;\r\n  private _configButton: HTMLButtonElement;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this._type = this._getDomElement<HTMLInputElement>('type');\r\n    this._id = this._getDomElement<HTMLInputElement>('id');\r\n    this._content = this._getDomElement<HTMLInputElement>('content');\r\n    this.propertyGrid = this._getDomElement<PropertyGrid>('pg');\r\n    this._idRect = this._getDomElement<HTMLDivElement>('idRect');\r\n    this._contentRect = this._getDomElement<HTMLDivElement>('contentRect');\r\n    this._innerRect = this._getDomElement<HTMLDivElement>('innerRect');\r\n    this._configButton = this._getDomElement<HTMLButtonElement>('config');\r\n    this._configButton.onclick = async () => {\r\n      const srv = await this.serviceContainer?.getLastServiceWhereAsync('configUiService', async x => await x.hasConfigUi(this._instanceServiceContainer.selectionService.primarySelection));\r\n      const ui = await srv.getConfigUi(this._instanceServiceContainer.selectionService.primarySelection);\r\n      this.serviceContainer.globalContext.showConfigClicked.emit({ designItem: this._instanceServiceContainer.selectionService.primarySelection, configUi: ui });\r\n    }\r\n\r\n    this._propertiesService = new ContentAndIdPropertiesService();\r\n    this._idRect.oncontextmenu = (event) => {\r\n      event.preventDefault();\r\n      if (!this._instanceServiceContainer.selectionService.primarySelection?.isRootItem)\r\n        this._openContextMenu(event, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService.idProperty);\r\n    };\r\n    this._contentRect.oncontextmenu = (event) => {\r\n      event.preventDefault();\r\n      if (!this._instanceServiceContainer.selectionService.primarySelection?.isRootItem)\r\n        this._openContextMenu(event, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService.contentProperty);\r\n    };\r\n    this._innerRect.oncontextmenu = (event) => {\r\n      event.preventDefault();\r\n      if (!this._instanceServiceContainer.selectionService.primarySelection?.isRootItem)\r\n        this._openContextMenu(event, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService.innerHtmlProperty);\r\n    };\r\n\r\n    this._id.onkeydown = e => {\r\n      if (e.key == 'Enter')\r\n        this._instanceServiceContainer.selectionService.primarySelection.id = this._id.value;\r\n      else if (e.key == 'Escape') {\r\n        this._id.value = this._instanceServiceContainer.selectionService.primarySelection?.id ?? '';\r\n        e.preventDefault();\r\n        e.stopPropagation();\r\n      }\r\n      PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._idRect, this._propertiesService.idProperty, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService);\r\n    }\r\n    this._content.onkeydown = e => {\r\n      if (e.key == 'Enter') {\r\n        this._instanceServiceContainer.selectionService.primarySelection.content = this._content.value;\r\n        this._content.title = this._content.value;\r\n      } else if (e.key == 'Escape') {\r\n        this._content.value = this._instanceServiceContainer.selectionService.primarySelection?.element?.textContent ?? '';\r\n        e.preventDefault();\r\n        e.stopPropagation();\r\n      }\r\n      PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._contentRect, this._propertiesService.contentProperty, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService);\r\n      PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._innerRect, this._propertiesService.innerHtmlProperty, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService);\r\n    }\r\n\r\n    let pSel: IDesignItem\r\n    this._id.onfocus = e => {\r\n      pSel = this._instanceServiceContainer.selectionService.primarySelection;\r\n    }\r\n    this._id.onblur = e => {\r\n      if (pSel)\r\n        pSel.id = this._id.value;\r\n      pSel = null;\r\n      PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._idRect, this._propertiesService.idProperty, this._instanceServiceContainer.selectionService.selectedElements, this._propertiesService);\r\n    }\r\n  }\r\n\r\n  public set serviceContainer(value: ServiceContainer) {\r\n    this.propertyGrid.serviceContainer = value;\r\n  }\r\n\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    const currentContainer = value;\r\n    this._instanceServiceContainer = currentContainer;\r\n    this._selectionChangedHandler?.dispose();\r\n    if (currentContainer) {\r\n      this._selectionChangedHandler = currentContainer.selectionService.onSelectionChanged.on(async e => {\r\n        this.propertyGrid.instanceServiceContainer = currentContainer;\r\n        await sleep(20); // delay assignment a little bit, so onblur above could still set the value.\r\n        await this._refreshHeaderForCurrentSelection(currentContainer);\r\n      });\r\n      this._refreshHeaderForCurrentSelection(currentContainer);\r\n    } else {\r\n      this._configButton.style.display = 'none';\r\n      \r\n      this._id.value = '';\r\n      this._content.value = '';\r\n      this._id.disabled = true;\r\n      this._content.disabled = true;\r\n\r\n      this._idRect.style.background = '';\r\n      this._contentRect.style.background = '';\r\n      this._innerRect.style.background = '';\r\n      this._type.value = \":host\";\r\n    }\r\n    this.propertyGrid.instanceServiceContainer = currentContainer;\r\n  }\r\n\r\n  private async _refreshHeaderForCurrentSelection(currentContainer: InstanceServiceContainer) {\r\n    if (!currentContainer?.selectionService?.primarySelection || currentContainer.selectionService.primarySelection?.isRootItem) {\r\n      this._configButton.style.display = 'none';\r\n\r\n      this._id.value = '';\r\n      this._content.value = '';\r\n      this._id.disabled = true;\r\n      this._content.disabled = true;\r\n\r\n      this._idRect.style.background = '';\r\n      this._contentRect.style.background = '';\r\n      this._innerRect.style.background = '';\r\n      this._type.value = \":host\";\r\n      this._type.title = this._type.value;\r\n      return;\r\n    }\r\n\r\n    this._id.disabled = false;\r\n    this._content.disabled = false;\r\n\r\n    const srv = await this.serviceContainer?.getLastServiceWhereAsync('configUiService', x => x.hasConfigUi(currentContainer.selectionService.primarySelection));\r\n    if (srv) {\r\n      this._configButton.style.display = 'block';\r\n    } else {\r\n      this._configButton.style.display = 'none';\r\n    }\r\n\r\n    if (currentContainer.selectionService.primarySelection?.nodeType == NodeType.Element) {\r\n      this._type.value = currentContainer.selectionService.primarySelection?.name ?? '';\r\n    } else {\r\n      this._type.value = currentContainer.selectionService.primarySelection?.node?.nodeName ?? '';\r\n    }\r\n    this._type.title = this._type.value;\r\n    this._id.value = currentContainer.selectionService.primarySelection?.id ?? '';\r\n    if (currentContainer.selectionService.primarySelection?.element?.nodeType != NodeType.Element) {\r\n      this._content.value = currentContainer.selectionService.primarySelection?.content ?? '';\r\n    } else if (currentContainer.selectionService.primarySelection?.element?.children?.length <= 0)\r\n      this._content.value = currentContainer.selectionService.primarySelection?.content ?? '';\r\n    else\r\n      this._content.value = ''\r\n    this._content.title = this._content.value;\r\n\r\n    PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._idRect, this._propertiesService.idProperty, currentContainer.selectionService.selectedElements, this._propertiesService);\r\n    PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._contentRect, this._propertiesService.contentProperty, currentContainer.selectionService.selectedElements, this._propertiesService);\r\n    PropertyGridPropertyList.refreshIsSetElementAndEditorForDesignItems(this._innerRect, this._propertiesService.innerHtmlProperty, currentContainer.selectionService.selectedElements, this._propertiesService);\r\n  }\r\n\r\n  private _openContextMenu(event: MouseEvent, designItems: IDesignItem[], property: IProperty) {\r\n    let ctxMenuItems: IContextMenuItem[];\r\n    if (this.propertyGrid.propertyContextMenuProvider)\r\n      ctxMenuItems = this.propertyGrid.propertyContextMenuProvider(designItems, property)\r\n    if (!ctxMenuItems)\r\n      ctxMenuItems = property.service.getContextMenu(designItems, property);\r\n    ContextMenu.show(ctxMenuItems, event);\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-web-component-designer-property-grid-with-header', PropertyGridWithHeader);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/refactorView/refactor-view.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html, Disposable } from '@node-projects/base-custom-webcomponent';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { IRefactoring } from '../../services/refactorService/IRefactoring.js';\r\nimport { ChangeGroup } from '../../services/undoService/ChangeGroup.js';\r\n\r\nexport class RefactorView extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override readonly template = html`\r\n    <div id=\"root\">\r\n      <div class=\"search\">\r\n        <span>search</span>\r\n        <input style=\"flex-grow: 1; min-width: 0\" value=\"{{this.searchText}}\">\r\n        <span>replace</span>\r\n        <input style=\"flex-grow: 1; min-width: 0\"value=\"{{this.replaceText}}\">\r\n        <button @click=\"[[this.replace()]]\" style=\"grid-column: 2;\">replace</button>\r\n      </div>\r\n      <hr>\r\n      <template repeat:item=\"[[this.refactorings]]\">\r\n        <details open>\r\n          <summary>\r\n              [[item[1][0].itemType]]-name:<input value=\"[[item[1][0].name]]\" @keydown=\"[[this._refactor(item, event)]]\" style=\"flex-grow: 1; min-width: 0\">\r\n          </summary>\r\n          <ul>\r\n            <template repeat:reft=\"[[item[1]]]\">\r\n              <li>[[reft.type]]/[[reft.display]]</li>\r\n            </template>\r\n          </ul>\r\n        </details>\r\n      </template>\r\n    </div>`;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n        box-sizing: border-box;      \r\n        font-family: monospace;  \r\n        height: 100%;\r\n        width: 100%;\r\n        position: absolute;\r\n        overflow: hidden;\r\n    }\r\n\r\n    .search {\r\n      display: grid;\r\n      grid-template-columns: 40px 1fr;\r\n      align-items: center;\r\n    }\r\n\r\n    span {\r\n      font-size: 10px;\r\n    }\r\n\r\n    summary {\r\n      cursor: pointer;\r\n      font-size: 10px;\r\n      display: flex;\r\n      align-items: center;\r\n      white-space: nowrap;\r\n    }\r\n    \r\n    ul {\r\n      margin: 4px;\r\n      padding-left: 20px;\r\n      font-size: 10px;\r\n    }\r\n    \r\n    #root {\r\n      padding: 5px;\r\n      display: flex;\r\n      flex-direction: column;\r\n      gap: 4px;\r\n      overflow-y: auto;\r\n      height: calc(100% - 10px);\r\n    }`;\r\n\r\n  static readonly is = 'node-projects-refactor-view';\r\n\r\n  static readonly properties = {\r\n  }\r\n\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _selectionChangedHandler: Disposable;\r\n  private _selectedItems: IDesignItem[];\r\n\r\n  public searchText: string = \"(.*)\";\r\n  public replaceText: string = \"$1\";\r\n\r\n\r\n  public refactorings = new Map<string, IRefactoring[]>();;\r\n\r\n  ready() {\r\n    this._bindingsParse();\r\n  }\r\n\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    this._instanceServiceContainer = value;\r\n    this._selectionChangedHandler?.dispose()\r\n    this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(e => {\r\n      this.selectedItems = e.selectedElements;\r\n    });\r\n    this.selectedItems = this._instanceServiceContainer.selectionService.selectedElements;\r\n  }\r\n\r\n  get selectedItems() {\r\n    return this._selectedItems;\r\n  }\r\n  set selectedItems(items: IDesignItem[]) {\r\n    if (this._selectedItems != items) {\r\n      this._selectedItems = items;\r\n\r\n      this.updateRefactorlist(this._selectedItems);\r\n    }\r\n  }\r\n\r\n  public replace() {\r\n    let grp: ChangeGroup = null;\r\n    for (let r of this.refactorings) {\r\n      let n = r[1][0].name;\r\n      const regex = new RegExp(this.searchText);\r\n      const found = n.match(regex);\r\n      if (found) {\r\n        if (!grp)\r\n          grp = r[1][0].designItem.openGroup('refactor with regex ' + this.searchText + \" -> \" + this.replaceText);\r\n        const newText = n.replace(regex, this.replaceText);\r\n        if (newText != n) {\r\n          this.applyRefactoring(r, newText)\r\n        }\r\n      }\r\n    }\r\n    if (grp)\r\n      grp.commit();\r\n  }\r\n\r\n  public _refactor(refactoring: [string, IRefactoring[]], event: KeyboardEvent) {\r\n    const ip = event.target as HTMLInputElement;\r\n    if (event.key == 'Enter') {\r\n      this.applyRefactoring(refactoring, ip.value);\r\n    }\r\n  }\r\n\r\n  public applyRefactoring(refactoring: [string, IRefactoring[]], newValue: string) {\r\n    const grp = refactoring[1][0].designItem.openGroup('refactor ' + refactoring[1][0].name + ' to ' + newValue);\r\n    for (let r of refactoring[1]) {\r\n      r.service.refactor(r, r.name, newValue);\r\n    }\r\n    grp.commit();\r\n  }\r\n\r\n  updateRefactorlist(designItems: IDesignItem[]) {\r\n    this.refactorings.clear();\r\n\r\n    if (designItems && designItems.length) {\r\n      let refactorings: IRefactoring[] = []\r\n      const serviceContainer = designItems[0].serviceContainer;\r\n      for (let s of serviceContainer.refactorServices) {\r\n        let rfs = s.getRefactorings(designItems);\r\n        refactorings.push(...rfs);\r\n      }\r\n\r\n      //Group refactorings by name\r\n      //TODO: group also by itemType, cause different item types (for example screen and signal name) could have the same string\r\n      for (const r of refactorings) {\r\n        let thisList = this.refactorings.get(r.itemType + '|' + r.name);\r\n        if (thisList === undefined) {\r\n          thisList = [];\r\n          this.refactorings.set(r.itemType + '|' + r.name, thisList);\r\n        }\r\n        thisList.push(r);\r\n      }\r\n    }\r\n\r\n    this._bindingsRefresh();\r\n  }\r\n}\r\n\r\ncustomElements.define(RefactorView.is, RefactorView);"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/treeView/ITreeView.ts",
    "content": "import { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { ISelectionChangedEvent } from '../../services/selectionService/ISelectionChangedEvent.js';\r\n\r\n//TODO: buttons in treeview so keyboard events could be directed to designer\r\n\r\nexport interface ITreeView {\r\n  createTree(rootItem: IDesignItem);\r\n  selectionChanged(event: ISelectionChangedEvent);\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/elements/widgets/treeView/treeView.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, Disposable } from '@node-projects/base-custom-webcomponent';\r\nimport { ISelectionChangedEvent } from '../../services/selectionService/ISelectionChangedEvent.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\nimport { DesignItem } from '../../item/DesignItem.js';\r\nimport { ITreeView } from './ITreeView.js';\r\nimport { InstanceServiceContainer } from '../../services/InstanceServiceContainer.js';\r\n\r\nexport class TreeView extends BaseCustomWebComponentLazyAppend implements ITreeView {\r\n\r\n  private _items: any;\r\n  private _index: number;\r\n  private _previouslySelected: Element[];\r\n  private _treeDiv: HTMLDivElement;\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _selectionChangedHandler: Disposable;\r\n  private _contentChangedHandler: Disposable;\r\n\r\n  private _mapElementTreeitem: Map<Element, HTMLElement>;\r\n\r\n  private _rootItem: IDesignItem\r\n\r\n  //TODO: buton so key events can be transfered to designerCanvas (so you can move controls with keys)\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      --horz-margin: 20px;\r\n      --vert-margin: 0px;\r\n      --horz-shift: calc(var(--horz-margin) / 2); /* typically */\r\n      --vert-shift: 12px;\r\n\r\n      display: inline-block;\r\n      position: relative;\r\n      width: 100%;\r\n      height: 100%;\r\n      background: var(--dark-grey, #232733);\r\n      overflow-y: auto;\r\n    }\r\n    button {\r\n      border: none;\r\n      font-size: 13px;\r\n      display: block;\r\n      padding: 4px 0;\r\n      cursor: pointer;\r\n      width: 100%;\r\n      text-align: left;\r\n      display: inline-block;\r\n      margin: 0;\r\n      background: var(--dark-grey, #232733);\r\n      position: relative;\r\n      color: white;\r\n    }\r\n    button:hover, button:focus {\r\n      background: var(--light-grey, #383f52);\r\n    }\r\n    span {\r\n      margin: 4px;\r\n    }\r\n    .id {\r\n      font-style: italic;\r\n      color: var(--highlight-pink, #e91e63);\r\n    }\r\n    .selected {\r\n      background: var(--light-grey, #383f52);\r\n      outline: none;\r\n    }\r\n\r\n    li, ul {\r\n      margin: 0;\r\n      padding: 0;\r\n    }\r\n    .tree ul {\r\n      margin-left: var(--horz-margin);\r\n    }\r\n    .tree li {\r\n      list-style-type: none;\r\n      margin-top: var(--vert-margin);\r\n      margin-bottom: var(--vert-margin);\r\n      position: relative;\r\n    }\r\n\r\n    /* up connector */\r\n    .tree li::before {\r\n        content: \"\";\r\n        position: absolute;\r\n        top: calc(0px - var(--vert-margin));\r\n        left: calc(var(--horz-shift) - var(--horz-margin));\r\n        width: calc(var(--horz-margin) - var(--horz-shift));\r\n        height: calc(var(--vert-shift) + var(--vert-margin));\r\n        border-left: 1px solid #ccc;\r\n        border-bottom: 1px solid #ccc;\r\n        border-radius: 0;\r\n    }\r\n\r\n    /* down connector */\r\n    .tree li::after {\r\n        position: absolute;\r\n        content: \"\";\r\n        top: var(--vert-shift);\r\n        left: calc(var(--horz-shift) - var(--horz-margin));\r\n        width: calc(var(--horz-margin) - var(--horz-shift));\r\n        height: calc(100% - var(--vert-shift));\r\n        border-left: 1px solid #ccc;\r\n        border-top: 1px solid #ccc;\r\n        border-radius: 0;\r\n    }\r\n\r\n    /* do not draw: up connector of first root item */\r\n    ul.tree>li:first-child::before { display:none; }\r\n\r\n    /* do not draw: down connector of last item */\r\n    .tree li:last-child::after  { display:none; }\r\n\r\n    /* draw rounded: down connector of first root item */\r\n    ul.tree>li:first-child::after { border-radius: 5px 0 0 0; }\r\n\r\n    /* draw rounded: up connector of last item */\r\n    .tree li:last-child:before { border-radius: 0 0 0 5px; }\r\n    `;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this._treeDiv = document.createElement('div');\r\n    this._treeDiv.style.userSelect = 'none';\r\n    this.shadowRoot.appendChild(this._treeDiv);\r\n    this._treeDiv.addEventListener('click', this._clickElement.bind(this));\r\n  }\r\n\r\n  public createTree(rootItem: IDesignItem /*, activeElement: Element */) {\r\n    this._rootItem = rootItem;\r\n    if (rootItem != null)\r\n      this._recomputeTree(rootItem.element, null /*, activeElement */);\r\n  }\r\n\r\n  // this.instanceServiceContainer.selectionService.setSelectedElements(null);\r\n\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    this._instanceServiceContainer = value;\r\n    this._selectionChangedHandler?.dispose()\r\n    this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(e => {\r\n      this.selectionChanged(e);\r\n    });\r\n    this._contentChangedHandler?.dispose()\r\n    this._contentChangedHandler = this._instanceServiceContainer.onContentChanged.on(e => {\r\n      this.createTree(value.designerCanvas.rootDesignItem);\r\n    });\r\n    this.createTree(value.designerCanvas.rootDesignItem);\r\n  }\r\n\r\n  public selectionChanged(event: ISelectionChangedEvent) {\r\n    this._selectTreeElements(event.selectedElements.map(x => this._mapElementTreeitem.get(x.element)));\r\n  }\r\n\r\n  private _recomputeTree(parent, activeElement: Element) {\r\n    this._mapElementTreeitem = new Map<HTMLElement, HTMLElement>();\r\n    this._treeDiv.innerHTML = '';\r\n    let ul = document.createElement('ul');\r\n    ul.classList.add('tree');\r\n    this._treeDiv.appendChild(ul);\r\n\r\n    // Since we can't add a pojo to each button, generate a new index for\r\n    // each button in the this.items array of useful data.\r\n\r\n    this._index = 0;\r\n    this._items = this._getChildren(parent, ul);\r\n\r\n    this._highlight(activeElement);\r\n    return this._items;\r\n  }\r\n\r\n  private _makeButton(tag, id, index) {\r\n    let aButton = document.createElement('button');\r\n    aButton.dataset.index = index;\r\n    let aTag = document.createElement('span');\r\n    aTag.className = 'tag';\r\n    aTag.textContent = tag;\r\n    let aId = document.createElement('span');\r\n    aId.className = 'id';\r\n    aId.textContent = id;\r\n    aButton.appendChild(aTag);\r\n    aButton.appendChild(aId);\r\n    return aButton;\r\n  }\r\n\r\n  private _getChildren(item, list) {\r\n    // Add item and its children into nested ul list\r\n\r\n    let isViewContainer = item.id === 'viewContainer';\r\n    let data = {\r\n      tag: isViewContainer ? 'main-app' : item.tagName.toLowerCase(),\r\n      id: isViewContainer ? '' : (item.id ? '#' + item.id : ''),\r\n      text: isViewContainer ? '' : '\"' + item.textContent + '\"',\r\n      ref: item,\r\n      index: this._index\r\n    };\r\n\r\n    // Add item to list\r\n    let li = document.createElement('li');\r\n    let button = this._makeButton(data.tag, data.id, data.index);\r\n    li.appendChild(button);\r\n    list.appendChild(li);\r\n    this._mapElementTreeitem.set(data.ref, button);\r\n\r\n    this._index++;\r\n    let nodes = [data];\r\n\r\n    // Add children to subordinate list\r\n    let ul = null;\r\n    for (let i = 0; i < item.children.length; i++) {\r\n      let child = item.children[i];\r\n\r\n      // Skip <style> nodes;\r\n      if (child.localName === 'style') {\r\n        continue;\r\n      }\r\n\r\n      if (ul == null) {\r\n        ul = document.createElement('ul');\r\n        li.appendChild(ul);\r\n      }\r\n      nodes = nodes.concat(this._getChildren(child, ul));\r\n    }\r\n\r\n    return nodes;\r\n  }\r\n\r\n  private _clickElement(event) {\r\n    // If the target is a <span>, you clicked on the span inside the button\r\n    // so you need to use currentTarget.\r\n    let item = event.target;\r\n    if (item.localName === 'span') {\r\n      item = item.parentElement;\r\n    }\r\n    this._selectTreeElements([item]);\r\n    this._selectDesignerElement(item);\r\n  }\r\n\r\n  private _selectTreeElements(items: Element[]) {\r\n    if (this._previouslySelected) {\r\n      for (let e of this._previouslySelected)\r\n        if (e)\r\n          e.classList.remove('selected');\r\n    }\r\n    this._previouslySelected = items;\r\n    if (items)\r\n      for (let e of items)\r\n        if (e)\r\n          e.classList.add('selected');\r\n  }\r\n\r\n  private _selectDesignerElement(item: any) {\r\n    let index = item.dataset.index;\r\n    let el = this._items[index].ref;\r\n    let d = DesignItem.GetOrCreateDesignItem(el, el, this._rootItem.serviceContainer, this._rootItem.instanceServiceContainer);\r\n    d.instanceServiceContainer.selectionService.setSelectedElements([d]);\r\n  }\r\n\r\n  private _highlight(element: Element) {\r\n    if (!element)\r\n      return;\r\n    // Find it in the tree.\r\n    let buttons = this.shadowRoot.querySelectorAll('button');\r\n    if (buttons.length !== this._items.length) {\r\n      return;\r\n    }\r\n\r\n    for (let i = 0; i < this._items.length; i++) {\r\n      if (this._items[i].ref === element) {\r\n        this._selectTreeElements([buttons[i]]);\r\n        return;\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-tree-view', TreeView);"
  },
  {
    "path": "packages/web-component-designer/src/enums/EventNames.ts",
    "content": "export enum EventNames {\r\n  PointerDown = 'pointerdown',\r\n  PointerMove = 'pointermove',\r\n  PointerUp = 'pointerup',\r\n  DragEnter = 'dragenter',\r\n  DragEnd = 'dragend',\r\n  DragLeave = 'dragleave',\r\n  DragOver = 'dragover',\r\n  Drop = 'drop',\r\n  Scroll = 'scroll',\r\n  Wheel = 'wheel',\r\n  ContextMenu = 'contextmenu',\r\n  KeyDown = 'keydown',\r\n  KeyUp = 'keyup',\r\n  DblClick = 'dblclick'\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/enums/Orientation.ts",
    "content": "export enum Orientation {\n    TOP,\n    RIGHT,\n    BOTTOM,\n    LEFT,\n    VERTICAL_CENTER,\n    HORIZONTAL_CENTER,\n}"
  },
  {
    "path": "packages/web-component-designer/src/enums/PointerActionType.ts",
    "content": "export enum PointerActionType {\r\n    DragOrSelect = 'DragOrSelect',\r\n    Drag = 'Drag',\r\n    Resize = 'Resize',\r\n    Rotate  = 'Rotate',\r\n    DrawSelection = 'DrawSelection',\r\n    DrawingSelection = 'DrawingSelection'\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer/src/index-all.ts",
    "content": "export * from './index.js';\nexport { default as createDefaultServiceContainer } from './elements/services/DefaultServiceBootstrap.js';"
  },
  {
    "path": "packages/web-component-designer/src/index.ts",
    "content": "export * from \"./elements/controls/DesignerTabControl.js\";\nexport * from \"./elements/controls/ColorEditor.js\";\nexport * from \"./elements/controls/MetricsEditor.js\";\nexport * from \"./elements/controls/NumericStyleInput.js\";\nexport * from \"./elements/controls/PlainScrollbar.js\"\n\r\nexport * from \"./elements/services/DefaultServiceBootstrap.js\";\r\n\r\nexport * from \"./elements/helper/CssAttributeParser.js\";\nexport * from \"./elements/helper/CssImportant.js\";\nexport * from \"./elements/helper/KeyboardHelper.js\";\nexport * from \"./elements/helper/CssCombiner.js\";\r\nexport * from \"./elements/helper/ObservedCustomElementsRegistry.js\";\r\nexport * from \"./elements/helper/ElementHelper.js\";\r\nexport * from \"./elements/helper/IndentedTextWriter.js\";\r\nexport * from \"./elements/helper/PathDataPolyfill.js\";\r\nexport * from \"./elements/helper/PopupHelper.js\";\r\nexport * from \"./elements/helper/Screenshot.js\";\r\nexport * from \"./elements/helper/SvgHelper.js\";\r\nexport * from \"./elements/helper/ClipboardHelper.js\";\r\nexport * from \"./elements/helper/TouchGestureHelper.js\";\r\n\r\nexport type { ITextWriter } from \"./elements/helper/ITextWriter.js\";\r\nexport * from \"./elements/helper/w3color.js\";\r\nexport * from \"./elements/helper/contextMenu/ContextMenu.js\";\r\nexport * from \"./elements/helper/Helper.js\";\r\nexport * from \"./elements/helper/Browser.js\";\r\nexport * from \"./elements/helper/SwitchContainerHelper.js\";\r\nexport * from \"./elements/helper/NpmPackageLoader.js\";\r\nexport * from \"./elements/helper/LayoutHelper.js\";\r\nexport * from \"./elements/helper/SelectionHelper.js\";\r\nexport * from \"./elements/helper/QuadEdgeHandleHelper.js\";\r\nexport type { IContextMenuItem, IContextMenu } from \"./elements/helper/contextMenu/IContextMenuItem.js\";\r\n\r\nexport * from \"./elements/item/DesignItem.js\";\r\nexport type { IDesignItem } from \"./elements/item/IDesignItem.js\";\r\nexport type { IBinding } from \"./elements/item/IBinding.js\";\r\nexport * from \"./elements/item/BindingMode.js\";\r\nexport * from \"./elements/item/BindingTarget.js\";\r\nexport * from \"./elements/item/NodeType.js\";\r\n\r\nexport * from \"./elements/services/bindableObjectsService/BindableObjectType.js\";\r\nexport * from \"./elements/services/bindableObjectsService/BindableObjectsTarget.js\";\r\nexport type { IBindableObject } from \"./elements/services/bindableObjectsService/IBindableObject.js\";\r\nexport type { IBindableObjectsService } from \"./elements/services/bindableObjectsService/IBindableObjectsService.js\";\r\nexport type { IBindableObjectDragDropService } from \"./elements/services/bindableObjectsService/IBindableObjectDragDropService.js\";\r\n\r\nexport type { IBindingService } from \"./elements/services/bindingsService/IBindingService.js\";\r\nexport * from \"./elements/services/bindingsService/BaseCustomWebcomponentBindingsService.js\";\r\n\r\nexport * from \"./elements/services/placementService/AbsolutePlacementService.js\";\r\nexport * from \"./elements/services/placementService/AlwaysAbsolutePlacementService.js\";\r\nexport * from \"./elements/services/placementService/DefaultPlacementService.js\";\r\nexport * from \"./elements/services/placementService/FlexBoxPlacementService.js\";\r\nexport * from \"./elements/services/placementService/GridPlacementService.js\";\r\nexport type { IPlacementService } from \"./elements/services/placementService/IPlacementService.js\";\r\nexport * from \"./elements/services/placementService/SnaplinesProviderService.js\";\r\nexport type { ISnaplinesProviderService } from \"./elements/services/placementService/ISnaplinesProviderService.js\";\r\n\r\nexport * from \"./elements/services/pngCreatorService/DisplayMediaPngWriterService.js\";\r\nexport * from \"./elements/services/pngCreatorService/ElectronPngWriterService.js\";\r\nexport type { IPngCreatorService } from \"./elements/services/pngCreatorService/IPngCreatorService.js\";\r\n\r\nexport * from \"./elements/services/elementAtPointService/ElementAtPointService.js\";\r\nexport type { IElementAtPointService } from \"./elements/services/elementAtPointService/IElementAtPointService.js\";\r\n\r\nexport * from \"./elements/services/copyPasteService/CopyPasteService.js\";\r\nexport * from \"./elements/services/copyPasteService/CopyPasteAsJsonService.js\";\r\nexport type { ICopyPasteService } from \"./elements/services/copyPasteService/ICopyPasteService.js\";\r\nexport * from \"./elements/services/copyPasteService/PasteFormatSnapshot.js\";\r\n\r\nexport * from \"./elements/services/deletionService/DeletionService.js\";\r\nexport type { IDeletionService } from \"./elements/services/deletionService/IDeletionService.js\";\r\n\r\nexport * from \"./elements/services/demoProviderService/IframeDemoProviderService.js\";\r\nexport * from \"./elements/services/demoProviderService/SimpleDemoProviderService.js\";\r\nexport type { IDemoProviderService } from \"./elements/services/demoProviderService/IDemoProviderService.js\";\r\n\r\nexport * from \"./elements/services/designItemDocumentPositionService/DesignItemDocumentPositionService.js\";\nexport type { IDesignItemDocumentPositionService } from \"./elements/services/designItemDocumentPositionService/IDesignItemDocumentPositionService.js\";\nexport type { IRenderedDesignItemService } from \"./elements/services/renderedDesignItemService/IRenderedDesignItemService.js\";\nexport * from \"./elements/services/renderedDesignItemService/StyleElementRenderedDesignItemService.js\";\nexport type { ISourcePart, ISvgPathHandleSourcePartData, SourcePartKind } from \"./elements/services/sourceMapService/ISourcePart.js\";\nexport type { ISourceMapContext, ISourceMapProvider, SourceMapContextKind } from \"./elements/services/sourceMapService/ISourceMapProvider.js\";\nexport * from \"./elements/services/sourceMapService/SvgPathDataSourceMap.js\";\nexport * from \"./elements/services/sourceMapService/SvgPathSourceMapProvider.js\";\n\nexport type { IConfigUiService } from \"./elements/services/configUiService/IConfigUiService.js\";\n\r\nexport * from \"./elements/services/designItemService/DesignItemService.js\";\r\nexport * from \"./elements/services/designItemService/BaseCustomWebcomponentDesignItemService.js\";\r\nexport type { IDesignItemService } from \"./elements/services/designItemService/IDesignItemService.js\";\r\n\r\nexport * from \"./elements/services/dragDropService/ExternalDragDropService.js\";\r\nexport type { IExternalDragDropService } from \"./elements/services/dragDropService/IExternalDragDropService.js\";\r\n\r\nexport type { IPropertyGridDragDropService } from \"./elements/services/dragDropService/IPropertyGridDragDropService.js\";\r\n\r\nexport * from \"./elements/services/dragDropService/DragDropService.js\";\r\nexport type { IDragDropService } from \"./elements/services/dragDropService/IDragDropService.js\";\r\n\r\nexport type { IElementInteractionService } from \"./elements/services/elementInteractionService/IElementInteractionService.js\";\r\n\r\nexport type { IElementDefinition } from \"./elements/services/elementsService/IElementDefinition.js\";\r\nexport type { IElementsJson } from \"./elements/services/elementsService/IElementsJson.js\";\r\nexport type { IElementsService } from \"./elements/services/elementsService/IElementsService.js\";\r\nexport * from \"./elements/services/elementsService/JsonFileElementsService.js\";\r\nexport * from \"./elements/services/elementsService/PreDefinedElementsService.js\";\r\nexport * from \"./elements/services/elementsService/WebcomponentManifestElementsService.js\";\r\n\r\nexport type { IEvent } from \"./elements/services/eventsService/IEvent.js\";\r\nexport type { IEventsService } from \"./elements/services/eventsService/IEventsService.js\";\r\nexport * from \"./elements/services/eventsService/EventsService.js\";\r\nexport * from \"./elements/services/eventsService/WebcomponentManifestEventsService.js\";\r\n\r\nexport type { IHtmlWriterService } from \"./elements/services/htmlWriterService/IHtmlWriterService.js\";\r\nexport type { IHtmlWriterOptions } from \"./elements/services/htmlWriterService/IHtmlWriterOptions.js\";\r\nexport type { IStringPosition } from \"./elements/services/htmlWriterService/IStringPosition.js\";\r\nexport * from \"./elements/services/htmlWriterService/AbstractHtmlWriterService.js\";\r\nexport * from \"./elements/services/htmlWriterService/FormatingHtmlWriterService.js\";\r\nexport * from \"./elements/services/htmlWriterService/HtmlWriterService.js\";\r\n\r\nexport * from \"./elements/services/htmlParserService/DefaultHtmlParserService.js\";\r\nexport type { IHtmlParserService } from \"./elements/services/htmlParserService/IHtmlParserService.js\";\r\n\r\nexport type { IIntializationService } from \"./elements/services/initializationService/IIntializationService.js\";\r\n\r\nexport * from \"./elements/services/instanceService/DefaultInstanceService.js\";\r\nexport type { IInstanceService } from \"./elements/services/instanceService/IInstanceService.js\";\r\n\r\nexport * from \"./elements/services/manifestParsers/WebcomponentManifestParserService.js\";\r\n\r\nexport type { IMiniatureViewService } from \"./elements/services/miniatureViewService/IMiniatureViewService.js\";\r\nexport * from \"./elements/services/miniatureViewService/MiniatureViewService.js\";\r\n\r\nexport type { IModelCommandService } from \"./elements/services/modelCommandService/IModelCommandService.js\";\r\nexport * from \"./elements/services/modelCommandService/DefaultModelCommandService.js\";\r\n\r\nexport type { IMultiplayerService } from \"./elements/services/multiplayerService/IMultiplayerService.js\";\r\nexport * from \"./elements/services/multiplayerService/MultiplayerService.js\";\r\n\r\nexport * from \"./elements/services/propertiesService/DefaultPropertyEditorTypesService.js\";\r\nexport type { IPropertyEditorTypesService } from \"./elements/services/propertiesService/IPropertyEditorTypesService.js\";\r\nexport * from \"./elements/services/propertiesService/DefaultEditorTypeService.js\";\r\nexport type { IEditorTypeService } from \"./elements/services/propertiesService/IEditorTypeService.js\";\r\n\r\nexport type { IPropertiesService } from \"./elements/services/propertiesService/IPropertiesService.js\";\r\nexport type { IProperty } from \"./elements/services/propertiesService/IProperty.js\";\r\nexport type { IPropertyEditor } from \"./elements/services/propertiesService/IPropertyEditor.js\";\r\nexport type { IPropertyGroup } from \"./elements/services/propertiesService/IPropertyGroup.js\";\r\nexport type { IPropertyGroupsService } from './elements/services/propertiesService/IPropertyGroupsService.js';\r\nexport { RefreshMode } from \"./elements/services/propertiesService/IPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/PropertyGroupsService.js\";\r\n\r\nexport * from \"./elements/services/propertiesService/propertyEditors/BasePropertyEditor.js\";\nexport * from \"./elements/services/propertiesService/propertyEditors/BooleanPropertyEditor.js\";\nexport * from \"./elements/services/propertiesService/propertyEditors/ColorPropertyEditor.js\";\nexport * from \"./elements/services/propertiesService/propertyEditors/CssPropertyEditor.js\";\nexport * from \"./elements/services/propertiesService/propertyEditors/UnitPropertyEditor.js\";\nexport * from \"./elements/services/propertiesService/propertyEditors/DatePropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/ImageButtonListPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/JsonPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/JsonPropertyPopupEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/NumberPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/SelectPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/TextPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/ThicknessPropertyEditor.js\";\r\n\r\nexport * from \"./elements/services/propertiesService/propertyEditors/special/MetricsPropertyEditor.js\";\r\nexport * from \"./elements/services/propertiesService/propertyEditors/special/GridAssignedRowColumnPropertyEditor.js\";\r\n\r\nexport * from \"./elements/services/propertiesService/services/PropertiesHelper.js\";\r\nexport * from \"./elements/services/propertiesService/services/AbstractPolymerLikePropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/AttachedPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/AbstractCssPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/BaseCustomWebComponentPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/BasicWebcomponentPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/CommonPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/ContentAndIdPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/CssCurrentPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/CssPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/ListPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/LitElementPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/Lit2PropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/MathMLElementsPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/NativeElementsPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/SVGElementsPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/PolymerPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/AbstractPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/WebcomponentManifestPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/AttributesPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/CssCustomPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/services/UnkownElementsPropertiesService.js\";\r\nexport * from \"./elements/services/propertiesService/PropertyType.js\";\r\nexport * from \"./elements/services/propertiesService/ValueType.js\";\r\n\r\nexport * from \"./elements/services/refactorService/BindingsRefactorService.js\";\r\nexport * from \"./elements/services/refactorService/TextRefactorService.js\";\r\nexport type { IRefactorService } from \"./elements/services/refactorService/IRefactorService.js\";\r\nexport type { IRefactoring } from \"./elements/services/refactorService/IRefactoring.js\";\r\n\r\nexport type { IReferencesChangedService } from \"./elements/services/referencesChangedService/IReferencesChangedService.js\";\r\n\r\nexport * from \"./elements/services/collaborationService/CollaborationNodeIndex.js\";\r\nexport type {\r\n\tCollaborationConnectionState,\r\n\tICollaborationComment,\r\n\tICollaborationCommentsChangedEvent,\r\n\tICollaborationDocumentSnapshot,\r\n\tICollaborationPeerPresence,\r\n\tICollaborationPeersChangedEvent,\r\n\tICollaborationRemoteChange,\r\n\tICollaborationSelectionEvent,\r\n\tICollaborationService,\r\n\tICollaborationSession,\r\n\tICollaborationStateChangedEvent,\r\n\tICollaborationTransport\r\n} from \"./elements/services/collaborationService/ICollaborationService.js\";\r\n\r\nexport * from \"./elements/services/searchService/SearchService.js\";\r\nexport type { ISearchService } from \"./elements/services/searchService/ISearchService.js\";\r\nexport type { ISearchResult } from \"./elements/services/searchService/ISearchResult.js\";\r\n\r\nexport type { ISelectionChangedEvent } from \"./elements/services/selectionService/ISelectionChangedEvent.js\";\r\nexport type { ISelectionRefreshEvent } from \"./elements/services/selectionService/ISelectionRefreshEvent.js\";\r\nexport type { ISelectionService } from \"./elements/services/selectionService/ISelectionService.js\";\r\nexport * from \"./elements/services/selectionService/SelectionService.js\";\r\n\r\nexport type { IStyleRule, IStyleDeclaration, IStylesheet, IStylesheetService, IDocumentStylesheet } from \"./elements/services/stylesheetService/IStylesheetService.js\";\r\nexport * from \"./elements/services/stylesheetService/AbstractStylesheetService.js\";\r\nexport * from \"./elements/services/stylesheetService/SpecificityCalculator.js\";\r\n\r\nexport * from \"./elements/services/undoService/ChangeGroup.js\";\r\nexport type { IUndoChangeEvent, UndoChangeKind, UndoChangeSource } from \"./elements/services/undoService/IUndoChangeEvent.js\";\r\nexport type { ITransactionItem } from \"./elements/services/undoService/ITransactionItem.js\";\r\nexport type { IUndoService } from \"./elements/services/undoService/IUndoService.js\";\r\nexport * from \"./elements/services/undoService/UndoService.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/AttributeChangeAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/AttributeAndPropertyChangeAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/CssStyleChangeAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/DeleteAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/InsertAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/InsertChildAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/PropertyChangeAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/StylesheetChangedAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/SetDesignItemsAction.js\";\r\nexport * from \"./elements/services/undoService/transactionItems/TextContentChangeAction.js\";\r\n\r\nexport * from \"./elements/services/BaseServiceContainer.js\";\r\nexport * from \"./elements/services/InstanceServiceContainer.js\";\r\nexport type { IService } from \"./elements/services/IService.js\";\r\nexport type { IServiceContainer } from \"./elements/services/IServiceContainer.js\";\r\nexport * from \"./elements/services/ServiceContainer.js\";\r\n\r\nexport type { IBindableObjectsBrowser } from \"./elements/widgets/bindableObjectsBrowser/IBindableObjectsBrowser.js\";\r\n\r\nexport * from \"./elements/widgets/propertyGrid/PropertyGrid.js\";\r\nexport * from \"./elements/widgets/propertyGrid/PropertyGridPropertyList.js\";\r\nexport * from \"./elements/widgets/propertyGrid/PropertyGridWithHeader.js\";\r\n\r\nexport type { IDesignerCanvas } from \"./elements/widgets/designerView/IDesignerCanvas.js\";\r\nexport * from \"./elements/widgets/designerView/designerView.js\";\r\nexport * from \"./elements/widgets/designerView/designerCanvas.js\";\r\nexport * from \"./elements/widgets/designerView/overlayLayerView.js\";\r\nexport * from \"./elements/widgets/designerView/defaultConfiguredDesignerView.js\";\r\nexport * from \"./elements/widgets/designerView/DomConverter.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/DrawToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/PointerToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/SelectorToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/SeperatorToolProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/TextToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/ZoomToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/TransformToolButtonProvider.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/buttons/SimpleToolButtonProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/AbstractBaseToolPopup.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/DraggableToolWindow.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/BoxShadowEditorWindow.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/TextShadowEditorWindow.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/GradientEditorWindow.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/BorderRadiusEditorWindow.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/DrawToolPopup.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/PointerToolPopup.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/SelectionToolPopup.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/popups/TransformToolPopup.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/DesignerToolbar.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/DesignerToolbarButton.js\";\r\nexport type { IDesignViewToolbarButtonProvider } from \"./elements/widgets/designerView/tools/toolBar/IDesignViewToolbarButtonProvider.js\"\r\n\r\nexport type { ITool } from \"./elements/widgets/designerView/tools/ITool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/toolBar/DesignerToolbar.js\";\r\nexport * from \"./elements/widgets/designerView/tools/NamedTools.js\";\r\nexport * from \"./elements/widgets/designerView/tools/DrawElementTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/DrawPathTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/DrawRectTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/DrawEllipsisTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/DrawLineTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/MagicWandSelectorTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/PanTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/PickColorTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/PointerTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/RectangleSelectorTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/TextTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/ZoomTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/MarginTool.js\";\r\nexport * from \"./elements/widgets/designerView/tools/PaddingTool.js\";\r\n\r\nexport type { IDesignerExtension } from \"./elements/widgets/designerView/extensions/IDesignerExtension.js\";\r\nexport type { IDesignerExtensionProvider } from \"./elements/widgets/designerView/extensions/IDesignerExtensionProvider.js\";\r\nexport type { IExtensionManager } from \"./elements/widgets/designerView/extensions/IExtensionManger.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/BasicStackedToolbarExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/OverlayLayer.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ExtensionType.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/AbstractExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/AltToEnterContainerExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/AltToEnterContainerExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/MarginExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/MarginExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PaddingExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PaddingExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/ProjectiveTransformExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/ProjectiveTransformExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/logic/ConditionExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ExtensionManager.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/flex/FlexboxExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/flex/FlexboxExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/GrayOutExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/GrayOutExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/GrayOutDragOverContainerExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/GrayOutDragOverContainerExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/InvisibleElementExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/InvisibleElementExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/HighlightElementExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/HighlightElementExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/MultipleSelectionRectExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/MultipleSelectionRectExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/UnifiedGeometryExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/UnifiedGeometryExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/IGeometry.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/GeometryReaderFactory.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgPathGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgRectGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgLineGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgEllipseGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgCircleGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgPolygonGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/SvgPolylineGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/CssClipPathGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/CssShapeOutsideGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/svg/geometry/CssOffsetPathGeometryReader.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PositionExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PositionExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ElementDragTitleExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ElementDragTitleExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ResizeExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/ResizeExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/SkewExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/SkewExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/RotateExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/RotateExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/RotateGroupExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/RotateGroupExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/SelectionDefaultExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/SelectionDefaultExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/TransformOriginExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/transforms/TransformOriginExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PlacementExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PlacementExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PreviousElementSelectExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/PreviousElementSelectExtensionProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/block/BlockToolbarExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/block/BlockToolbarExtensionProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/flex/FlexToolbarExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/flex/FlexToolbarExtensionProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/grid/EditGridColumnRowSizesExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/EditGridColumnRowSizesExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/DisplayGridExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/DisplayGridExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridChildResizeExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridChildResizeExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridChildToolbarExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridChildToolbarExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridToolbarExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/grid/GridToolbarExtensionProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/logic/ApplyFirstMachingExtensionProvider.js\";\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/InvisibleElementExtensionDesignViewConfigButtons.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/FlexboxExtensionDesignViewConfigButtons.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/GridExtensionDesignViewConfigButtons.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/AbstractDesignViewConfigButton.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/ButtonSeperatorProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/StylesheetServiceDesignViewConfigButtons.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/ToolbarExtensionsDesignViewConfigButtons.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/RoundPixelsDesignViewConfigButton.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/buttons/OptionsContextMenuButton.js\";\r\nexport type { IDesignViewConfigButtonsProvider } from './elements/widgets/designerView/extensions/buttons/IDesignViewConfigButtonsProvider.js';\r\n\r\nexport * from \"./elements/widgets/designerView/extensions/EditText/EditTextExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/EditText/EditTextExtensionProvider.js\";\r\n\r\nexport type { IContextMenuExtension, ContextmenuInitiator } from \"./elements/widgets/designerView/extensions/contextMenu/IContextMenuExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/BasicContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ChildContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/CopyPasteContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ItemsBelowContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ChildrenContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/MultipleItemsSelectedContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/PathContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/RectContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/SeperatorContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/SelectAllChildrenContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ZMoveContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/RotateLeftAndRightContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ZoomToElementContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/JumpToElementContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/AlignItemsContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ForceCssContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/PasteFormatContextMenu.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/contextMenu/ToolWindowsContextMenu.js\";\r\n\r\nexport type { IDesignerPointerExtension } from \"./elements/widgets/designerView/extensions/pointerExtensions/IDesignerPointerExtension.js\";\r\nexport type { IDesignerPointerExtensionProvider } from \"./elements/widgets/designerView/extensions/pointerExtensions/IDesignerPointerExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/pointerExtensions/AbstractDesignerPointerExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/pointerExtensions/CursorLinePointerExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/pointerExtensions/CursorLinePointerExtensionProvider.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/pointerExtensions/LinePointerExtension.js\";\r\nexport * from \"./elements/widgets/designerView/extensions/pointerExtensions/LinePointerExtensionProvider.js\";\r\n\r\nexport type { IMiniatureView } from \"./elements/widgets/miniatureView/IMiniatureView.js\";\nexport * from \"./elements/widgets/miniatureView/miniatureView.js\";\nexport type { ILayerDepthView } from \"./elements/widgets/layerDepthView/ILayerDepthView.js\";\nexport * from \"./elements/widgets/layerDepthView/layerDepthView.js\";\n\nexport * from \"./elements/widgets/debugView/debug-view.js\";\n\r\nexport * from \"./elements/widgets/refactorView/refactor-view.js\";\r\n\r\nexport * from \"./elements/widgets/demoView/demoView.js\";\r\nexport * from \"./elements/widgets/paletteView/paletteElements.js\";\r\nexport * from \"./elements/widgets/paletteView/paletteView.js\";\r\n\r\nexport type { ITreeView } from \"./elements/widgets/treeView/ITreeView.js\";\r\nexport * from \"./elements/widgets/treeView/treeView.js\";\r\n\r\nexport type { ICodeView } from \"./elements/widgets/codeView/ICodeView.js\";\r\nexport * from \"./elements/widgets/codeView/code-view-simple.js\";\r\n\r\nexport * from \"./elements/documentContainer.js\";\r\n\r\nexport * from \"./enums/EventNames.js\";\r\nexport * from \"./enums/PointerActionType.js\";\r\n\r\nexport type { IActivateable } from \"./interfaces/IActivateable.js\";\r\nexport type { IDisposable } from \"./interfaces/IDisposable.js\";\r\nexport type { IPoint } from \"./interfaces/IPoint.js\";\r\nexport type { IRect } from \"./interfaces/IRect.js\";\r\nexport type { ISize } from \"./interfaces/ISize.js\";\r\n\r\nexport * from \"./commandHandling/CommandType.js\"\r\nexport type { IUiCommand } from \"./commandHandling/IUiCommand.js\"\r\nexport type { IUiCommandHandler } from \"./commandHandling/IUiCommandHandler.js\"\r\n\r\nexport * from './Constants.js'\r\n"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/IActivateable.ts",
    "content": "export interface IActivateable {\r\n  activated: () => void\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/IDisposable.ts",
    "content": "\r\nexport interface IDisposable {\r\n  dispose(): void;\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/IPoint.ts",
    "content": "export interface IPoint {\r\n    x: number\r\n    y: number\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/IPoint3D.ts",
    "content": "export interface IPoint3D {\r\n    x: number\r\n    y: number\r\n    z: number\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/IRect.ts",
    "content": "export interface IRect {\r\n    x: number\r\n    y: number\r\n    width: number\r\n    height: number\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/interfaces/ISize.ts",
    "content": "export interface ISize {\r\n    width: number\r\n    height: number\r\n}"
  },
  {
    "path": "packages/web-component-designer/src/polyfill/globals.ts",
    "content": "export { };\r\n\r\ndeclare global {\r\n  interface Window { }\r\n\r\n  interface Function {\r\n    style: CSSStyleSheet;\r\n    svgDefs: string;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer/tests/ContextMenu.test.ts",
    "content": "/** @jest-environment jsdom */\n\nimport { afterAll, afterEach, beforeAll, beforeEach, expect, test } from '@jest/globals';\n\nlet ContextMenu: typeof import('../src/elements/helper/contextMenu/ContextMenu').ContextMenu;\n\nconst originalCSSStyleSheet = globalThis.CSSStyleSheet;\nconst originalShowPopover = HTMLElement.prototype.showPopover;\nconst originalHidePopover = HTMLElement.prototype.hidePopover;\nconst originalMatches = HTMLElement.prototype.matches;\nconst popoverOpenAttribute = 'data-test-popover-open';\n\nfunction makeRect(left: number, top: number, width: number, height: number): DOMRect {\n  return {\n    x: left,\n    y: top,\n    width,\n    height,\n    top,\n    right: left + width,\n    bottom: top + height,\n    left,\n    toJSON() {\n      return {};\n    }\n  } as DOMRect;\n}\n\nbeforeAll(async () => {\n  Object.defineProperty(globalThis, 'CSSStyleSheet', {\n    configurable: true,\n    value: class {\n      replaceSync() {\n      }\n    }\n  });\n  Object.defineProperty(document, 'adoptedStyleSheets', {\n    configurable: true,\n    writable: true,\n    value: []\n  });\n\n  HTMLElement.prototype.showPopover = function () {\n    this.setAttribute(popoverOpenAttribute, '');\n  };\n  HTMLElement.prototype.hidePopover = function () {\n    this.removeAttribute(popoverOpenAttribute);\n  };\n  HTMLElement.prototype.matches = function (selectors: string) {\n    if (selectors === ':popover-open') {\n      return this.hasAttribute(popoverOpenAttribute);\n    }\n\n    return originalMatches.call(this, selectors);\n  };\n\n  ({ ContextMenu } = await import('../src/elements/helper/contextMenu/ContextMenu'));\n});\n\nbeforeEach(() => {\n  document.body.innerHTML = '';\n  document.adoptedStyleSheets = [];\n});\n\nafterEach(() => {\n  ContextMenu?.closeAll();\n  document.body.innerHTML = '';\n});\n\nafterAll(() => {\n  Object.defineProperty(globalThis, 'CSSStyleSheet', {\n    configurable: true,\n    value: originalCSSStyleSheet\n  });\n\n  if (originalShowPopover == null)\n    delete HTMLElement.prototype.showPopover;\n  else\n    HTMLElement.prototype.showPopover = originalShowPopover;\n\n  if (originalHidePopover == null)\n    delete HTMLElement.prototype.hidePopover;\n  else\n    HTMLElement.prototype.hidePopover = originalHidePopover;\n\n  HTMLElement.prototype.matches = originalMatches;\n});\n\ntest('displays the root menu as a manual popover', () => {\n  const menu = new ContextMenu([{ title: 'Item' }]);\n  menu.display(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 48, clientY: 64 }));\n\n  const menuElement = document.querySelector('.context_menu') as HTMLDivElement;\n\n  expect(menuElement.getAttribute('popover')).toBe('manual');\n  expect(menuElement.hasAttribute(popoverOpenAttribute)).toBe(true);\n  expect(menuElement.style.left).toBe('50px');\n  expect(menuElement.style.top).toBe('66px');\n});\n\ntest('opens submenus as manual popovers', () => {\n  new ContextMenu([{ title: 'Parent', children: [{ title: 'Child' }] }]);\n\n  const parentItem = document.querySelector('.context_menu li') as HTMLLIElement;\n  const childmenu = parentItem.querySelector('ul') as HTMLUListElement;\n  Object.defineProperty(parentItem, 'getBoundingClientRect', {\n    configurable: true,\n    value: () => makeRect(40, 20, 80, 24)\n  });\n  Object.defineProperty(childmenu, 'getBoundingClientRect', {\n    configurable: true,\n    value: () => makeRect(0, 0, 90, 100)\n  });\n  Object.defineProperty(childmenu, 'offsetWidth', { configurable: true, value: 90 });\n  Object.defineProperty(childmenu, 'offsetHeight', { configurable: true, value: 100 });\n\n  parentItem.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));\n\n  expect(childmenu.getAttribute('popover')).toBe('manual');\n  expect(childmenu.classList.contains('context_menu_submenu_popover')).toBe(true);\n  expect(childmenu.hasAttribute(popoverOpenAttribute)).toBe(true);\n  expect(childmenu.style.left).toBe('120px');\n  expect(childmenu.style.top).toBe('20px');\n});\n\ntest('closeAll removes the menu from the document', () => {\n  const menu = new ContextMenu([{ title: 'Item' }]);\n  menu.display(new MouseEvent('contextmenu', { bubbles: true, cancelable: true, clientX: 12, clientY: 24 }));\n\n  ContextMenu.closeAll();\n\n  expect(document.querySelector('.context_menu')).toBeNull();\n});"
  },
  {
    "path": "packages/web-component-designer/tests/CssCombiner.test.ts",
    "content": "import { afterAll, afterEach, beforeAll, beforeEach, expect, test } from '@jest/globals';\n\nconst camelToDashCase = (text: string) => text.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);\n\nconst compressBoxValues = (top: string, right: string, bottom: string, left: string) => {\n  if (top === right && top === bottom && top === left)\n    return top;\n  if (top === bottom && right === left)\n    return `${top} ${right}`;\n  if (right === left)\n    return `${top} ${right} ${bottom}`;\n  return `${top} ${right} ${bottom} ${left}`;\n};\n\nconst expandBoxValues = (value: string) => {\n  const values = value.trim().split(/\\s+/);\n  if (values.length === 1)\n    return [values[0], values[0], values[0], values[0]];\n  if (values.length === 2)\n    return [values[0], values[1], values[0], values[1]];\n  if (values.length === 3)\n    return [values[0], values[1], values[2], values[1]];\n  return [values[0], values[1], values[2], values[3]];\n};\n\nclass FakeStyleStore {\n  private declarations = new Map<string, string>();\n\n  reset() {\n    this.declarations.clear();\n  }\n\n  setProperty(name: string, value: string) {\n    this.declarations.set(name, value);\n  }\n\n  getPropertyValue(name: string) {\n    const directValue = this.declarations.get(name);\n    if (directValue != null)\n      return directValue;\n\n    const boxSideMap: Record<string, [string, number]> = {\n      'margin-top': ['margin', 0],\n      'margin-right': ['margin', 1],\n      'margin-bottom': ['margin', 2],\n      'margin-left': ['margin', 3],\n      'padding-top': ['padding', 0],\n      'padding-right': ['padding', 1],\n      'padding-bottom': ['padding', 2],\n      'padding-left': ['padding', 3],\n      'top': ['inset', 0],\n      'right': ['inset', 1],\n      'bottom': ['inset', 2],\n      'left': ['inset', 3],\n      'border-top-width': ['border-width', 0],\n      'border-right-width': ['border-width', 1],\n      'border-bottom-width': ['border-width', 2],\n      'border-left-width': ['border-width', 3],\n      'border-top-style': ['border-style', 0],\n      'border-right-style': ['border-style', 1],\n      'border-bottom-style': ['border-style', 2],\n      'border-left-style': ['border-style', 3],\n      'border-top-color': ['border-color', 0],\n      'border-right-color': ['border-color', 1],\n      'border-bottom-color': ['border-color', 2],\n      'border-left-color': ['border-color', 3]\n    };\n\n    const boxSide = boxSideMap[name];\n    if (boxSide) {\n      const boxValue = this.declarations.get(boxSide[0]);\n      if (boxValue != null)\n        return expandBoxValues(boxValue)[boxSide[1]];\n    }\n\n    const gap = this.declarations.get('gap');\n    if (gap && (name === 'row-gap' || name === 'column-gap')) {\n      const [rowGap, columnGap = rowGap] = gap.trim().split(/\\s+/, 2);\n      return name === 'row-gap' ? rowGap : columnGap;\n    }\n\n    const flexFlow = this.declarations.get('flex-flow');\n    if (flexFlow && (name === 'flex-direction' || name === 'flex-wrap')) {\n      const [flexDirection = '', flexWrap = ''] = flexFlow.trim().split(/\\s+/, 2);\n      return name === 'flex-direction' ? flexDirection : flexWrap;\n    }\n\n    const placeContent = this.declarations.get('place-content');\n    if (placeContent && (name === 'align-content' || name === 'justify-content')) {\n      const [alignContent = '', justifyContent = alignContent] = placeContent.trim().split(/\\s+/, 2);\n      return name === 'align-content' ? alignContent : justifyContent;\n    }\n\n    return '';\n  }\n\n  get cssText() {\n    const serialized: [string, string][] = [];\n    const consumed = new Set<string>();\n    const get = (name: string) => this.declarations.get(name);\n\n    const append = (name: string, value: string, consumedNames: string[] = [name]) => {\n      serialized.push([name, value]);\n      for (const consumedName of consumedNames)\n        consumed.add(consumedName);\n    };\n\n    const appendDirect = (name: string) => {\n      const value = get(name);\n      if (value)\n        append(name, value);\n    };\n\n    appendDirect('border');\n    appendDirect('border-width');\n    appendDirect('border-style');\n    appendDirect('border-color');\n    appendDirect('background');\n    appendDirect('font');\n\n    const marginTop = get('margin-top');\n    const marginRight = get('margin-right');\n    const marginBottom = get('margin-bottom');\n    const marginLeft = get('margin-left');\n    if (!consumed.has('margin') && marginTop && marginRight && marginBottom && marginLeft)\n      append('margin', compressBoxValues(marginTop, marginRight, marginBottom, marginLeft), ['margin-top', 'margin-right', 'margin-bottom', 'margin-left']);\n\n    const paddingTop = get('padding-top');\n    const paddingRight = get('padding-right');\n    const paddingBottom = get('padding-bottom');\n    const paddingLeft = get('padding-left');\n    if (!consumed.has('padding') && paddingTop && paddingRight && paddingBottom && paddingLeft)\n      append('padding', compressBoxValues(paddingTop, paddingRight, paddingBottom, paddingLeft), ['padding-top', 'padding-right', 'padding-bottom', 'padding-left']);\n\n    const top = get('top');\n    const right = get('right');\n    const bottom = get('bottom');\n    const left = get('left');\n    if (!consumed.has('inset') && top && right && bottom && left)\n      append('inset', compressBoxValues(top, right, bottom, left), ['top', 'right', 'bottom', 'left']);\n\n    const rowGap = get('row-gap');\n    const columnGap = get('column-gap');\n    if (rowGap && columnGap)\n      append('gap', `${rowGap} ${columnGap}`, ['row-gap', 'column-gap']);\n\n    const flexDirection = get('flex-direction');\n    const flexWrap = get('flex-wrap');\n    if (flexDirection && flexWrap)\n      append('flex-flow', `${flexDirection} ${flexWrap}`, ['flex-direction', 'flex-wrap']);\n\n    const alignContent = get('align-content');\n    const justifyContent = get('justify-content');\n    if (alignContent && justifyContent)\n      append('place-content', `${alignContent} ${justifyContent}`, ['align-content', 'justify-content']);\n\n    const borderTop = get('border-top');\n    const borderLeft = get('border-left');\n    const borderStyle = get('border-style');\n    if (borderStyle && (borderTop || borderLeft)) {\n      const [topStyle, rightStyle, bottomStyle, leftStyle] = expandBoxValues(borderStyle);\n      if (borderTop && topStyle)\n        append('border-top', `${borderTop} ${topStyle}`.trim(), ['border-top']);\n      if (borderLeft && leftStyle)\n        append('border-left', `${borderLeft} ${leftStyle}`.trim(), ['border-left']);\n      if (rightStyle)\n        append('border-right-style', rightStyle, ['border-style']);\n      if (bottomStyle)\n        append('border-bottom-style', bottomStyle, ['border-style']);\n    }\n\n    for (const [name, value] of this.declarations) {\n      if (!consumed.has(name) && !serialized.some(x => x[0] === name))\n        append(name, value);\n    }\n\n    return serialized.map(([name, value]) => `${name}: ${value};`).join(' ');\n  }\n}\n\nconst createFakeStyle = () => {\n  const store = new FakeStyleStore();\n  return new Proxy(store as any, {\n    get(target, prop) {\n      if (typeof prop === 'string' && !(prop in target))\n        return target.getPropertyValue(camelToDashCase(prop));\n\n      const value = target[prop];\n      return typeof value === 'function' ? value.bind(target) : value;\n    },\n    set(target, prop, value) {\n      if (typeof prop === 'string' && !(prop in target)) {\n        target.setProperty(camelToDashCase(prop), String(value));\n        return true;\n      }\n\n      target[prop] = value;\n      return true;\n    }\n  });\n};\n\nclass FakeHelperElement {\n  style = createFakeStyle();\n\n  setAttribute(name: string, value: string) {\n    if (name === 'style' && value === '')\n      this.style.reset();\n  }\n}\n\nlet CssCombiner: typeof import('../src/elements/helper/CssCombiner').CssCombiner;\nlet originalDocument: unknown;\nlet originalHelperElement: unknown;\n\nbeforeAll(async () => {\n  originalDocument = (<any>globalThis).document;\n  (<any>globalThis).document = {\n    createElement: () => new FakeHelperElement()\n  };\n\n  ({ CssCombiner } = await import('../src/elements/helper/CssCombiner'));\n});\n\nafterAll(() => {\n  (<any>globalThis).document = originalDocument;\n});\n\nbeforeEach(() => {\n  originalHelperElement = (<any>CssCombiner)._helperElement;\n  (<any>CssCombiner)._helperElement = new FakeHelperElement();\n});\n\nafterEach(() => {\n  (<any>CssCombiner)._helperElement = originalHelperElement;\n});\n\nconst combine = (styles: Record<string, string>, globalStyles?: Record<string, string>) =>\n  CssCombiner.combine(\n    new Map(Object.entries(styles)),\n    globalStyles ? new Map(Object.entries(globalStyles)) : undefined\n  );\n\ntest('combines border and font longhands into shorthand declarations', () => {\n  const result = combine({\n    'border-top-style': 'solid',\n    'border-right-style': 'solid',\n    'border-bottom-style': 'solid',\n    'border-left-style': 'solid',\n    'border-top-color': 'red',\n    'border-right-color': 'red',\n    'border-bottom-color': 'red',\n    'border-left-color': 'red',\n    'border-top-width': '1px',\n    'border-right-width': '1px',\n    'border-bottom-width': '1px',\n    'border-left-width': '1px',\n    'font-style': 'italic',\n    'font-weight': '700',\n    'font-size': '16px',\n    'line-height': '24px',\n    'font-family': '\"Fira Code\", monospace'\n  });\n\n  expect(result.get('border')).toBe('1px solid red');\n  expect(result.has('border-top-width')).toBe(false);\n  expect(result.has('border-right-width')).toBe(false);\n  expect(result.has('border-bottom-width')).toBe(false);\n  expect(result.has('border-left-width')).toBe(false);\n\n  const font = result.get('font');\n  expect(font).toContain('italic');\n  expect(font).toContain('700');\n  expect(font).toContain('16px');\n  expect(font).toContain('24px');\n  expect(font).toContain('Fira Code');\n  expect(result.has('font-style')).toBe(false);\n  expect(result.has('font-weight')).toBe(false);\n  expect(result.has('font-size')).toBe(false);\n  expect(result.has('line-height')).toBe(false);\n  expect(result.has('font-family')).toBe(false);\n});\n\ntest('combines additional browser-supported shorthand groups through CSSOM serialization', () => {\n  const result = combine({\n    'row-gap': '4px',\n    'column-gap': '8px',\n    'flex-direction': 'column',\n    'flex-wrap': 'wrap',\n    'align-content': 'center',\n    'justify-content': 'space-between'\n  });\n\n  expect(result.get('gap')).toBe('4px 8px');\n  expect(result.get('flex-flow')).toBe('column wrap');\n  expect(result.get('place-content')).toBe('center space-between');\n\n  expect(result.has('row-gap')).toBe(false);\n  expect(result.has('column-gap')).toBe(false);\n  expect(result.has('flex-direction')).toBe(false);\n  expect(result.has('flex-wrap')).toBe(false);\n  expect(result.has('align-content')).toBe(false);\n  expect(result.has('justify-content')).toBe(false);\n});\n\ntest('keeps custom properties and removes shorthand declarations that match global styles', () => {\n  const result = combine({\n    '--brand-color': '#c00',\n    'row-gap': '4px',\n    'column-gap': '8px'\n  }, {\n    'gap': '4px 8px'\n  });\n\n  expect(result.get('--brand-color')).toBe('#c00');\n  expect(result.has('gap')).toBe(false);\n});\n\ntest('parses cssText declarations with semicolons inside urls', () => {\n  const result = (<any>CssCombiner).parseStyleDeclarationList(\n    'background: url(\"data:image/svg+xml;utf8,<svg viewBox=\\\\\"0 0 1 1\\\\\"></svg>\") no-repeat center / cover; color: red;'\n  );\n\n  expect(result.get('background')).toBe('url(\"data:image/svg+xml;utf8,<svg viewBox=\\\\\"0 0 1 1\\\\\"></svg>\") no-repeat center / cover');\n  expect(result.get('color')).toBe('red');\n});\n\ntest('does not replace declarations with longer browser cssText expansions', () => {\n  const result = combine({\n    'border-top': '4px',\n    'border-left': '2px',\n    'border-style': 'solid'\n  });\n\n  expect(result.get('border-top')).toBe('4px');\n  expect(result.get('border-left')).toBe('2px');\n  expect(result.get('border-style')).toBe('solid');\n  expect(result.has('border-right-style')).toBe(false);\n  expect(result.has('border-bottom-style')).toBe(false);\n});"
  },
  {
    "path": "packages/web-component-designer/tests/CssImportant.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { CssAttributeParser } from '../src/elements/helper/CssAttributeParser';\nimport { appendCssImportant, splitCssImportant } from '../src/elements/helper/CssImportant';\nimport { CssPropertyEditor } from '../src/elements/services/propertiesService/propertyEditors/CssPropertyEditor';\nimport { CssStyleChangeAction } from '../src/elements/services/undoService/transactionItems/CssStyleChangeAction';\nimport { IDesignItem } from '../src/elements/item/IDesignItem';\nimport { PropertyType } from '../src/elements/services/propertiesService/PropertyType';\nimport { ValueType } from '../src/elements/services/propertiesService/ValueType';\nimport { IPropertiesService } from '../src/elements/services/propertiesService/IPropertiesService';\nimport { IPropertyEditor } from '../src/elements/services/propertiesService/IPropertyEditor';\n\nclass FakeStyleDeclaration {\n  values = new Map<string, { value: string, priority: string }>();\n\n  setProperty(name: string, value: string, priority: string = '') {\n    this.values.set(name, { value, priority });\n  }\n\n  removeProperty(name: string) {\n    this.values.delete(name);\n  }\n}\n\nconst createDesignItem = () => {\n  const styles = new Map<string, string>();\n  const priorities = new Map<string, boolean>();\n  const style = new FakeStyleDeclaration();\n\n  const designItem = {\n    name: 'div',\n    element: { style },\n    _withoutUndoSetStyle(name: string, value: string, important: boolean = false) {\n      styles.set(name, value);\n      if (important)\n        priorities.set(name, true);\n      else\n        priorities.delete(name);\n    },\n    _withoutUndoRemoveStyle(name: string) {\n      styles.delete(name);\n      priorities.delete(name);\n    },\n    getStyle(name: string) {\n      return styles.get(name);\n    },\n    isStyleImportant(name: string) {\n      return priorities.get(name) === true;\n    }\n  } as unknown as IDesignItem;\n\n  return { designItem, style };\n};\n\ntest('splits and appends css important markers', () => {\n  expect(splitCssImportant('red !important')).toEqual({ value: 'red', important: true });\n  expect(splitCssImportant('calc(100% - 2px) ! important  ')).toEqual({ value: 'calc(100% - 2px)', important: true });\n  expect(splitCssImportant('red')).toEqual({ value: 'red', important: false });\n  expect(appendCssImportant('red', true)).toBe('red !important');\n  expect(appendCssImportant('red', false)).toBe('red');\n});\n\ntest('parses inline css priority separately from the declaration value', () => {\n  const parser = new CssAttributeParser();\n  parser.parse('color: red !important; width: 10px;');\n\n  expect(parser.entries).toEqual([\n    { name: 'color', value: 'red', important: true },\n    { name: 'width', value: '10px', important: false }\n  ]);\n});\n\ntest('css style change action applies and restores priority', () => {\n  const { designItem, style } = createDesignItem();\n  designItem._withoutUndoSetStyle('color', 'blue', false);\n\n  const action = new CssStyleChangeAction(designItem, 'color', 'red', 'blue', true, false);\n\n  action.do();\n  expect(designItem.getStyle('color')).toBe('red');\n  expect(designItem.isStyleImportant('color')).toBe(true);\n  expect(style.values.get('color')).toEqual({ value: 'red', priority: 'important' });\n\n  action.undo();\n  expect(designItem.getStyle('color')).toBe('blue');\n  expect(designItem.isStyleImportant('color')).toBe(false);\n  expect(style.values.get('color')).toEqual({ value: 'blue', priority: '' });\n});\n\ntest('css property editor keeps important out of the wrapped editor value', async () => {\n  const originalDocument = (<any>globalThis).document;\n  const createElement = (tagName: string) => ({\n    tagName,\n    type: '',\n    title: '',\n    disabled: false,\n    dataset: {},\n    style: {},\n    children: [],\n    classList: { add() { }, remove() { } },\n    appendChild(child: unknown) {\n      this.children.push(child);\n    }\n  });\n  (<any>globalThis).document = { createElement };\n\n  try {\n    let setValue: unknown;\n    const service = {\n      setValue: async (_designItems, _property, value) => { setValue = value; }\n    } as IPropertiesService;\n\n    let innerValue: unknown;\n    let innerEditor: IPropertyEditor;\n    const editor = new CssPropertyEditor({\n      name: 'color',\n      type: 'css-color',\n      service,\n      propertyType: PropertyType.cssValue\n    }, property => {\n      innerEditor = {\n        element: createElement('input') as unknown as Element,\n        property,\n        designItems: [],\n        designItemsChanged(designItems) { this.designItems = designItems; },\n        refreshValue(_valueType, value) { innerValue = value; },\n        refreshValueWithoutNotification(_valueType, value) { innerValue = value; }\n      };\n      return innerEditor;\n    });\n\n    editor.designItemsChanged([{} as IDesignItem]);\n    editor.refreshValueWithoutNotification(ValueType.all, 'red !important');\n\n    expect(innerValue).toBe('red');\n    expect((<any>editor)._importantButton.dataset.checked).toBe('true');\n\n    await innerEditor.property.service.setValue(editor.designItems, innerEditor.property, 'blue');\n    expect(setValue).toBe('blue !important');\n\n    (<any>editor)._importantButton.dataset.checked = 'false';\n    await innerEditor.property.service.setValue(editor.designItems, innerEditor.property, 'green');\n    expect(setValue).toBe('green');\n  } finally {\n    (<any>globalThis).document = originalDocument;\n  }\n});\n"
  },
  {
    "path": "packages/web-component-designer/tests/DesignerStylesheetPatcher.test.ts",
    "content": "/**\n * @jest-environment jsdom\n */\nimport { expect, jest, test } from '@jest/globals';\nimport { patchStylesheetSelectorForDesigner } from '../src/elements/helper/DesignerStylesheetPatcher';\n\ntest('patches virtual root and forced pseudo classes for designer rendering', () => {\n  const css = [\n    ':root { --accent: red; }',\n    '.button:hover { color: blue; }',\n    '.field:focus-visible { outline: 1px solid red; }',\n    '.wrapper:focus-within { border-color: green; }'\n  ].join('\\n');\n\n  expect(patchStylesheetSelectorForDesigner(css, {\n    forceHoverAttributeName: 'node-projects-force-hover',\n    forceActiveAttributeName: 'node-projects-force-active',\n    forceVisitedAttributeName: 'node-projects-force-visited',\n    forceFocusAttributeName: 'node-projects-force-focus',\n    forceFocusWithinAttributeName: 'node-projects-force-focus-within',\n    forceFocusVisibleAttributeName: 'node-projects-force-focus-visible'\n  })).toBe([\n    ':host { --accent: red; }',\n    '.button[node-projects-force-hover] { color: blue; }',\n    '.field[node-projects-force-focus-visible] { outline: 1px solid red; }',\n    '.wrapper[node-projects-force-focus-within] { border-color: green; }'\n  ].join('\\n'));\n});\n\ntest('style design items patch rendered text without changing original content', async () => {\n  if (!CSSStyleSheet.prototype.replaceSync)\n    Object.defineProperty(CSSStyleSheet.prototype, 'replaceSync', { value() { } });\n  const { DesignItem } = await import('../src/elements/item/DesignItem');\n  const { StyleElementRenderedDesignItemService } = await import('../src/elements/services/renderedDesignItemService/StyleElementRenderedDesignItemService');\n\n  const originalCss = ':root { --accent: red; }\\n.button:hover { color: blue; }';\n  const host = document.createElement('test-host');\n  const shadow = host.attachShadow({ mode: 'open' });\n  shadow.innerHTML = `<style>${originalCss}</style><div class=\"button\"></div>`;\n  const template = document.createElement('template');\n  template.setAttribute('shadowrootmode', 'open');\n  const style = document.createElement('style');\n  style.textContent = originalCss;\n  template.content.appendChild(style);\n\n  const instanceServiceContainer = {\n    designerCanvas: {\n      lazyTriggerReparseDocumentStylesheets: jest.fn()\n    }\n  } as any;\n  const serviceContainer = {\n    renderedDesignItemServices: [new StyleElementRenderedDesignItemService()]\n  } as any;\n  const hostDesignItem = new DesignItem(host, host, serviceContainer, instanceServiceContainer);\n  const templateDesignItem = new DesignItem(template, template, serviceContainer, instanceServiceContainer);\n  (templateDesignItem as any)._attributes.set('shadowrootmode', 'open');\n  const styleDesignItem = new DesignItem(style, style, serviceContainer, instanceServiceContainer);\n  const textDesignItem = new DesignItem(style.firstChild, style.firstChild, serviceContainer, instanceServiceContainer);\n  (hostDesignItem as any)._childArray = [templateDesignItem];\n  (templateDesignItem as any)._parent = hostDesignItem;\n  (templateDesignItem as any)._childArray = [styleDesignItem];\n  (styleDesignItem as any)._parent = templateDesignItem;\n  (styleDesignItem as any)._childArray = [textDesignItem];\n  (textDesignItem as any)._parent = styleDesignItem;\n\n  styleDesignItem.refreshRenderedDesignItem();\n\n  expect(style.textContent).toBe(':host { --accent: red; }\\n.button[node-projects-force-hover] { color: blue; }');\n  expect(shadow.querySelector('style').textContent).toBe(':host { --accent: red; }\\n.button[node-projects-force-hover] { color: blue; }');\n  expect(styleDesignItem.content).toBe(originalCss);\n  expect(instanceServiceContainer.designerCanvas.lazyTriggerReparseDocumentStylesheets).toHaveBeenCalledTimes(1);\n});\n"
  },
  {
    "path": "packages/web-component-designer/tests/GridHelper.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { getGridColumnStartLineFromLocalX, getGridRowStartLineFromLocalY, type IGridInformation } from '../src/elements/helper/GridHelper';\n\nfunction createGridInformation(): IGridInformation {\n  return {\n    xGap: 0,\n    yGap: 0,\n    gaps: [],\n    cells: [\n      [\n        { x: 0, y: 0, width: 100, height: 50, name: 'a', localX: 0, localY: 0 },\n        { x: 100, y: 0, width: 100, height: 50, name: 'b', localX: 100, localY: 0 },\n        { x: 200, y: 0, width: 100, height: 50, name: 'c', localX: 200, localY: 0 }\n      ],\n      [\n        { x: 0, y: 50, width: 100, height: 50, name: 'd', localX: 0, localY: 50 },\n        { x: 100, y: 50, width: 100, height: 50, name: 'e', localX: 100, localY: 50 },\n        { x: 200, y: 50, width: 100, height: 50, name: 'f', localX: 200, localY: 50 }\n      ]\n    ]\n  };\n}\n\ntest('maps left resize snapping to the first grid line', () => {\n  const gridInformation = createGridInformation();\n\n  expect(getGridColumnStartLineFromLocalX(gridInformation, 10)).toBe(1);\n  expect(getGridColumnStartLineFromLocalX(gridInformation, 75)).toBe(2);\n  expect(getGridColumnStartLineFromLocalX(gridInformation, 175)).toBe(3);\n});\n\ntest('maps top resize snapping to the first grid line', () => {\n  const gridInformation = createGridInformation();\n\n  expect(getGridRowStartLineFromLocalY(gridInformation, 5)).toBe(1);\n  expect(getGridRowStartLineFromLocalY(gridInformation, 30)).toBe(2);\n  expect(getGridRowStartLineFromLocalY(gridInformation, 90)).toBe(3);\n});"
  },
  {
    "path": "packages/web-component-designer/tests/NumericStyleInput.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport type { IProperty } from '../src/elements/services/propertiesService/IProperty';\nimport { PropertyType } from '../src/elements/services/propertiesService/PropertyType';\nimport { combineNumericStyleInputValue, getNumericStyleInputUnitLabel, normalizeNumericStyleInputOptionValues, parseNumericStyleInputValue, resolveNumericStyleInputSelectedUnit, resolveNumericStyleInputStep } from '../src/elements/controls/NumericStyleInputValueHelpers';\nimport { applyCssNumericPropertyDefaults, convertNumericUnitValue, defaultCssNumericUnits, defaultCssNumericUnitSteps } from '../src/elements/services/propertiesService/propertyEditors/UnitPropertyEditorConfig';\n\ntest('parses numeric, fixed, and custom values', () => {\n  expect(parseNumericStyleInputValue('12px')).toEqual({ kind: 'numeric', numberText: '12', value: 12, unit: 'px' });\n  expect(parseNumericStyleInputValue('auto')).toEqual({ kind: 'text', text: 'auto' });\n  expect(parseNumericStyleInputValue('calc(100% - 4px)')).toEqual({ kind: 'text', text: 'calc(100% - 4px)' });\n  expect(parseNumericStyleInputValue('')).toEqual({ kind: 'empty' });\n  expect(combineNumericStyleInputValue('24', 'rem')).toBe('24rem');\n});\n\ntest('numeric style input helpers preserve the unitless option', () => {\n  expect(normalizeNumericStyleInputOptionValues(['', '%', ' ', '%'])).toEqual(['', '%']);\n  expect(getNumericStyleInputUnitLabel('')).toBe(' ');\n  expect(resolveNumericStyleInputSelectedUnit('', '%', ['', '%'])).toBe('');\n  expect(resolveNumericStyleInputSelectedUnit(undefined, '', ['', '%'])).toBe('');\n  expect(resolveNumericStyleInputSelectedUnit('rem', '', ['', '%'])).toBe('');\n  expect(resolveNumericStyleInputStep({ '': 0.1, '%': 10 }, 1, '')).toBe(0.1);\n  expect(resolveNumericStyleInputStep({ '': 0.1, '%': 10 }, 1, '%')).toBe(10);\n  expect(resolveNumericStyleInputStep({ '': 0.1, '%': 10 }, 1, 'px')).toBe(1);\n});\n\ntest('converts css numeric values outside the editor', () => {\n  const baseProperty = {\n    name: 'margin-left',\n    service: {} as any,\n    propertyType: PropertyType.cssValue\n  } satisfies Omit<IProperty, 'type'>;\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length' },\n    numericType: 'css-length',\n    value: 96,\n    numberText: '96',\n    rawValue: '96px',\n    fromUnit: 'px',\n    toUnit: 'in'\n  })).toBe('1in');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-angle' },\n    numericType: 'css-angle',\n    value: 180,\n    numberText: '180',\n    rawValue: '180deg',\n    fromUnit: 'deg',\n    toUnit: 'turn'\n  })).toBe('0.5turn');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-time' },\n    numericType: 'css-time',\n    value: 1500,\n    numberText: '1500',\n    rawValue: '1500ms',\n    fromUnit: 'ms',\n    toUnit: 's'\n  })).toBe('1.5s');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length' },\n    numericType: 'css-length',\n    value: 10,\n    numberText: '10',\n    rawValue: '10%',\n    fromUnit: '%',\n    toUnit: 'cm'\n  })).toBe('10cm');\n\n  const originalGetComputedStyle = globalThis.getComputedStyle;\n  const originalWindow = globalThis.window;\n  const parentElement = {\n    getBoundingClientRect: () => ({ width: 400, height: 200 }),\n  } as any;\n  const element = {\n    parentElement,\n    getBoundingClientRect: () => ({ width: 133.3333, height: 72 })\n  } as any;\n  const measuredElement = {\n    parentElement,\n    getBoundingClientRect: () => ({ width: 160, height: 72 })\n  } as any;\n  const fakeWindow = { innerWidth: 1200, innerHeight: 800 } as any;\n  Object.defineProperty(globalThis, 'window', { configurable: true, value: fakeWindow });\n  Object.defineProperty(globalThis, 'getComputedStyle', {\n    configurable: true,\n    value: (target: any) => {\n      if (target === parentElement)\n        return { fontSize: '16px' };\n      if (target === element)\n        return { fontSize: '20px' };\n      if (target === globalThis.document?.documentElement)\n        return { fontSize: '16px' };\n      return { fontSize: '16px' };\n    }\n  });\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length', name: 'width' },\n    numericType: 'css-length',\n    designItems: [{ element } as any],\n    value: 133.3333,\n    numberText: '133.3333',\n    rawValue: '133.3333px',\n    fromUnit: 'px',\n    toUnit: '%'\n  })).toBe('33.33%');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length', name: 'width' },\n    numericType: 'css-length',\n    designItems: [{ element } as any],\n    value: Number.NaN,\n    numberText: '',\n    rawValue: 'initial',\n    fromUnit: '',\n    toUnit: '%'\n  })).toBe('33.33%');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length', name: 'width' },\n    numericType: 'css-length',\n    designItems: [{ element } as any],\n    value: 33.33,\n    numberText: '33.33',\n    rawValue: '33.33%',\n    fromUnit: '%',\n    toUnit: 'px'\n  })).toBe('133.32px');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length', name: 'width', numericValueDecimalPlaces: 3 },\n    numericType: 'css-length',\n    designItems: [{ element } as any],\n    value: 133.3333,\n    numberText: '133.3333',\n    rawValue: '133.3333px',\n    fromUnit: 'px',\n    toUnit: '%'\n  })).toBe('33.333%');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-length', name: 'width' },\n    numericType: 'css-length',\n    designItems: [{ element: measuredElement } as any],\n    value: 100,\n    numberText: '100',\n    rawValue: '100px',\n    fromUnit: 'px',\n    toUnit: '%'\n  })).toBe('40%');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-scale', name: 'zoom' },\n    numericType: 'css-scale',\n    value: 2,\n    numberText: '2',\n    rawValue: '2',\n    fromUnit: '',\n    toUnit: '%'\n  })).toBe('200%');\n\n  expect(convertNumericUnitValue({\n    property: { ...baseProperty, type: 'css-scale', name: 'zoom' },\n    numericType: 'css-scale',\n    value: 200,\n    numberText: '200',\n    rawValue: '200%',\n    fromUnit: '%',\n    toUnit: ''\n  })).toBe('2');\n\n  if (originalGetComputedStyle == null)\n    delete (globalThis as any).getComputedStyle;\n  else\n    Object.defineProperty(globalThis, 'getComputedStyle', { configurable: true, value: originalGetComputedStyle });\n\n  if (originalWindow == null)\n    delete (globalThis as any).window;\n  else\n    Object.defineProperty(globalThis, 'window', { configurable: true, value: originalWindow });\n});\n\ntest('abstract css properties service fills css numeric metadata', () => {\n  const property = applyCssNumericPropertyDefaults({\n    name: 'width',\n    type: 'css-length',\n    values: ['auto'],\n    service: {} as any,\n    propertyType: PropertyType.cssValue\n  });\n\n  expect(property.units).toEqual(defaultCssNumericUnits['css-length']);\n  expect(property.unitSteps).toEqual(defaultCssNumericUnitSteps['css-length']);\n  expect(property.values).toEqual(['auto', 'initial', 'inherit', 'unset']);\n\n  const originalGetComputedStyle = globalThis.getComputedStyle;\n  const hostElement = {\n    getBoundingClientRect: () => ({ width: 400, height: 200 })\n  } as any;\n  const element = {\n    parentElement: null,\n    ownerDocument: { body: hostElement, documentElement: hostElement },\n    getRootNode: () => ({ host: hostElement }),\n    getBoundingClientRect: () => ({ width: 160, height: 80 })\n  } as any;\n\n  Object.defineProperty(globalThis, 'getComputedStyle', {\n    configurable: true,\n    value: () => ({ fontSize: '16px' })\n  });\n  \n  expect(property.numericValueConverter?.(100, 'px', '%', property, 'css-length', '100', '100px', [{ element } as any])).toBe('40%');\n\n  if (originalGetComputedStyle == null)\n    delete (globalThis as any).getComputedStyle;\n  else\n    Object.defineProperty(globalThis, 'getComputedStyle', { configurable: true, value: originalGetComputedStyle });\n});"
  },
  {
    "path": "packages/web-component-designer/tests/PasteFormatSnapshot.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { createPasteFormatSnapshot, createPasteFormatSnapshotFromEntries, getPasteFormatEntries } from '../src/elements/services/copyPasteService/PasteFormatSnapshot';\n\ntest('collects border, background, and text properties and skips unrelated styles', () => {\n  const values: Record<string, string> = {\n    'background-color': ' rgb(1, 2, 3) ',\n    'border-top-color': ' red ',\n    'border-top-style': ' solid ',\n    'font-family': ' Fira Code ',\n    'line-height': ' 24px ',\n    'color': ' white ',\n    'display': ' grid '\n  };\n\n  const fakeStyle = {\n    length: 7,\n    0: 'background-color',\n    1: 'border-top-color',\n    2: 'border-top-style',\n    3: 'font-family',\n    4: 'line-height',\n    5: 'color',\n    6: 'display',\n    getPropertyValue(name: string) {\n      return values[name] ?? '';\n    }\n  } as Pick<CSSStyleDeclaration, 'getPropertyValue' | 'length'> & ArrayLike<string>;\n\n  const snapshot = createPasteFormatSnapshot(fakeStyle);\n\n  expect(snapshot.background).toEqual([{ name: 'background-color', value: 'rgb(1, 2, 3)' }]);\n  expect(snapshot.border).toEqual([\n    { name: 'border-top-color', value: 'red' },\n    { name: 'border-top-style', value: 'solid' }\n  ]);\n  expect(snapshot.text).toEqual([\n    { name: 'font-family', value: 'Fira Code' },\n    { name: 'line-height', value: '24px' },\n    { name: 'color', value: 'white' }\n  ]);\n  expect(getPasteFormatEntries(snapshot, 'all')).toEqual([\n    { name: 'border-top-color', value: 'red' },\n    { name: 'border-top-style', value: 'solid' },\n    { name: 'background-color', value: 'rgb(1, 2, 3)' },\n    { name: 'font-family', value: 'Fira Code' },\n    { name: 'line-height', value: '24px' },\n    { name: 'color', value: 'white' }\n  ]);\n});\n\ntest('collects transform properties and includes them in the all group', () => {\n  const values: Record<string, string> = {\n    'transform': 'translateX(12px) rotate(12deg)',\n    'translate': '12px 0',\n    'rotate': '12deg',\n    'scale': '1.2',\n    'transform-origin': 'center center',\n    'display': 'grid'\n  };\n\n  const fakeStyle = {\n    length: 6,\n    0: 'transform',\n    1: 'translate',\n    2: 'rotate',\n    3: 'scale',\n    4: 'transform-origin',\n    5: 'display',\n    getPropertyValue(name: string) {\n      return values[name] ?? '';\n    }\n  } as Pick<CSSStyleDeclaration, 'getPropertyValue' | 'length'> & ArrayLike<string>;\n\n  const snapshot = createPasteFormatSnapshot(fakeStyle);\n\n  expect(snapshot.transform).toEqual([\n    { name: 'transform', value: 'translateX(12px) rotate(12deg)' },\n    { name: 'translate', value: '12px 0' },\n    { name: 'rotate', value: '12deg' },\n    { name: 'scale', value: '1.2' },\n    { name: 'transform-origin', value: 'center center' }\n  ]);\n  expect(getPasteFormatEntries(snapshot, 'all')).toEqual(snapshot.transform);\n});\n\ntest('keeps duplicate properties out of the all group', () => {\n  const values: Record<string, string> = {\n    'background-image': 'none',\n    'background-color': 'transparent',\n    'background-color-duplicate': ''\n  };\n\n  const fakeStyle = {\n    length: 4,\n    0: 'background-image',\n    1: 'background-color',\n    2: 'background-image',\n    3: 'background-color-duplicate',\n    getPropertyValue(name: string) {\n      return values[name] ?? '';\n    }\n  } as Pick<CSSStyleDeclaration, 'getPropertyValue' | 'length'> & ArrayLike<string>;\n\n  const snapshot = createPasteFormatSnapshot(fakeStyle);\n\n  expect(snapshot.background).toEqual([\n    { name: 'background-image', value: 'none' },\n    { name: 'background-color', value: 'transparent' }\n  ]);\n  expect(snapshot.all).toEqual(snapshot.background);\n});\n\ntest('creates a snapshot from design item style entries', () => {\n  const snapshot = createPasteFormatSnapshotFromEntries([\n    ['border-top-color', ' red '],\n    ['background-color', ' rgb(1, 2, 3) '],\n    ['transform', ' translateX(12px) '],\n    ['rotate', ' 12deg '],\n    ['font-family', ' Fira Code '],\n    ['color', ' white ']\n  ]);\n\n  expect(snapshot?.border).toEqual([{ name: 'border-top-color', value: 'red' }]);\n  expect(snapshot?.background).toEqual([{ name: 'background-color', value: 'rgb(1, 2, 3)' }]);\n  expect(snapshot?.transform).toEqual([\n    { name: 'transform', value: 'translateX(12px)' },\n    { name: 'rotate', value: '12deg' }\n  ]);\n  expect(snapshot?.text).toEqual([\n    { name: 'font-family', value: 'Fira Code' },\n    { name: 'color', value: 'white' }\n  ]);\n});"
  },
  {
    "path": "packages/web-component-designer/tests/PathDataPolyfill.test.ts",
    "content": "import { afterAll, beforeAll, expect, test } from '@jest/globals';\n\nconst svgElementGlobals = [\n  'SVGPathElement',\n  'SVGRectElement',\n  'SVGCircleElement',\n  'SVGEllipseElement',\n  'SVGLineElement',\n  'SVGPolylineElement',\n  'SVGPolygonElement'\n] as const;\n\ntype SvgElementGlobalName = (typeof svgElementGlobals)[number];\n\nlet straightenLine: typeof import('../src/elements/helper/PathDataPolyfill').straightenLine;\nlet interpolateLinePoints: typeof import('../src/elements/helper/PathDataPolyfill').interpolateLinePoints;\nconst originalGlobals = new Map<SvgElementGlobalName, unknown>();\n\nbeforeAll(async () => {\n  for (const globalName of svgElementGlobals) {\n    originalGlobals.set(globalName, (<any>globalThis)[globalName]);\n    (<any>globalThis)[globalName] = class { };\n  }\n\n  ({ straightenLine, interpolateLinePoints } = await import('../src/elements/helper/PathDataPolyfill'));\n});\n\nafterAll(() => {\n  for (const globalName of svgElementGlobals) {\n    const originalGlobal = originalGlobals.get(globalName);\n    if (originalGlobal === undefined)\n      delete (<any>globalThis)[globalName];\n    else\n      (<any>globalThis)[globalName] = originalGlobal;\n  }\n});\n\ntest('straightenLine keeps upward vertical snap upward', () => {\n  const result = straightenLine({ x: 10, y: 10 }, { x: 10, y: 0 }, 90);\n\n  expect(result.x).toBeCloseTo(10);\n  expect(result.y).toBeCloseTo(0);\n});\n\ntest('straightenLine keeps downward vertical snap downward', () => {\n  const result = straightenLine({ x: 10, y: 10 }, { x: 10, y: 25 }, 90);\n\n  expect(result.x).toBeCloseTo(10);\n  expect(result.y).toBeCloseTo(25);\n});\n\ntest('interpolateLinePoints fills long gaps with evenly spaced points', () => {\n  const result = interpolateLinePoints({ x: 0, y: 0 }, { x: 12, y: 0 }, 5);\n\n  expect(result).toHaveLength(3);\n  expect(result[0].x).toBeCloseTo(4);\n  expect(result[0].y).toBeCloseTo(0);\n  expect(result[1].x).toBeCloseTo(8);\n  expect(result[1].y).toBeCloseTo(0);\n  expect(result[2].x).toBeCloseTo(12);\n  expect(result[2].y).toBeCloseTo(0);\n});\n\ntest('interpolateLinePoints does not duplicate identical points', () => {\n  const result = interpolateLinePoints({ x: 3, y: 4 }, { x: 3, y: 4 }, 5);\n\n  expect(result).toEqual([]);\n});\n"
  },
  {
    "path": "packages/web-component-designer/tests/PropertyGridRefresh.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { didAttributePresenceChange, didCustomStyleDeclarationSetChange, didStyleDeclarationSetChange } from '../src/elements/services/propertiesService/PropertyMutationHandling';\n\ntest('does not recreate when an existing attribute value changes', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'style',\n    oldValue: 'width: 10px;',\n    target: {\n      getAttribute: () => 'width: 20px;'\n    }\n  } as const;\n\n  expect(didAttributePresenceChange(mutation)).toBe(false);\n  expect(didStyleDeclarationSetChange(mutation)).toBe(false);\n});\n\ntest('recreates when an attribute is added', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'style',\n    oldValue: null,\n    target: {\n      getAttribute: () => 'width: 20px;'\n    }\n  } as const;\n\n  expect(didAttributePresenceChange(mutation)).toBe(true);\n  expect(didStyleDeclarationSetChange(mutation)).toBe(true);\n});\n\ntest('recreates when an attribute is removed', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'title',\n    oldValue: 'hello',\n    target: {\n      getAttribute: () => null\n    }\n  } as const;\n\n  expect(didAttributePresenceChange(mutation)).toBe(true);\n  expect(didStyleDeclarationSetChange(mutation)).toBe(false);\n});\n\ntest('recreates styles when inline declaration names change', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'style',\n    oldValue: 'width: 10px; color: red;',\n    target: {\n      getAttribute: () => 'width: 20px; height: 30px; color: red;'\n    }\n  } as const;\n\n  expect(didStyleDeclarationSetChange(mutation)).toBe(true);\n});\n\ntest('does not recreate styles when only inline declaration values change', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'style',\n    oldValue: 'width: 10px; color: red;',\n    target: {\n      getAttribute: () => 'width: 20px; color: blue;'\n    }\n  } as const;\n\n  expect(didStyleDeclarationSetChange(mutation)).toBe(false);\n});\n\ntest('custom properties only recreate for custom declaration changes', () => {\n  const mutation = {\n    type: 'attributes',\n    attributeName: 'style',\n    oldValue: '--brand: red; width: 10px;',\n    target: {\n      getAttribute: () => '--brand: blue; --accent: orange; width: 20px;'\n    }\n  } as const;\n\n  expect(didCustomStyleDeclarationSetChange(mutation)).toBe(true);\n});"
  },
  {
    "path": "packages/web-component-designer/tests/SpecificityCalculator.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { calculateSpecificity } from '../dist/elements/services/stylesheetService/SpecificityCalculator';\n\ntest('test 1', () => {\n    const res = calculateSpecificity('#aa');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 2', () => {\n    const res = calculateSpecificity('#aa:is(bb,cc:hover)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(1);\n});\n\ntest('test 3', () => {\n    const res = calculateSpecificity(':host(#aa)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 4', () => {\n    const res = calculateSpecificity('div:hover:selected');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2);\n    expect(res.C).toBe(1);\n});\n\ntest('test 5', () => {\n    const res = calculateSpecificity(':host .grain ');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2);\n    expect(res.C).toBe(0);\n});\n\ntest('test 6', () => {\n    const res = calculateSpecificity(':host .center .outer_two__piece:nth-of-type(2)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(4);\n    expect(res.C).toBe(0);\n});\n\ntest('test 7', () => {\n    const res = calculateSpecificity('a[lang|=\"en\"]');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(1);\n});\n\ntest('test 8', () => {\n    const res = calculateSpecificity('div:not(.awesome)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(1);\n});\n\ntest('test 9', () => {\n    const res = calculateSpecificity('div:where(#aa)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 10', () => {\n    const res = calculateSpecificity('*.aa:is(button, button.cc#bb):hover');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(3);\n    expect(res.C).toBe(1);\n});\n\ntest('test 11', () => {\n    const res = calculateSpecificity('button:not(:nth-child(2))');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(1);\n});\n\ntest('test 12', () => {\n    const res = calculateSpecificity(':nth-child(2 of .a, #b)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 13', () => {\n    const res = calculateSpecificity(':where(#id, .class)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 14 - escaped class', () => {\n    const res = calculateSpecificity('.\\\\31 23');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 15 - escaped id', () => {\n    const res = calculateSpecificity('#\\\\#id');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 16 - attribute with closing bracket in string', () => {\n    const res = calculateSpecificity('[data=\"a]b\"]');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 17 - attribute with parentheses in string', () => {\n    const res = calculateSpecificity('[data=\"(test)\"]');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 18 - nth-child with of class', () => {\n    const res = calculateSpecificity(':nth-child(2 of .a)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // nth-child + .a\n    expect(res.C).toBe(0);\n});\n\ntest('test 19 - nth-child with of id', () => {\n    const res = calculateSpecificity(':nth-child(2 of #a)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 20 - nth-child with selector list', () => {\n    const res = calculateSpecificity(':nth-child(2 of .a, #b)');\n    expect(res.A).toBe(1); // max(#b)\n    expect(res.B).toBe(1); // nth-child\n    expect(res.C).toBe(0);\n});\n\ntest('test 21 - nth-child with whitespace', () => {\n    const res = calculateSpecificity(':nth-child(2    of    .a)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2);\n    expect(res.C).toBe(0);\n});\n\ntest('test 22 - nth-child without of', () => {\n    const res = calculateSpecificity(':nth-child(2)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 23 - nested strings and brackets', () => {\n    const res = calculateSpecificity('[data=\"a(b[c]d)e\"]');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1);\n    expect(res.C).toBe(0);\n});\n\ntest('test 24 - namespaced element', () => {\n    const res = calculateSpecificity('svg|rect');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 25 - universal with namespace', () => {\n    const res = calculateSpecificity('*|div');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 26 - empty namespace', () => {\n    const res = calculateSpecificity('|div');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 27 - pseudo-element with args ::part', () => {\n    const res = calculateSpecificity('::part(button)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 28 - pseudo-element with args ::slotted', () => {\n    const res = calculateSpecificity('::slotted(span)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1);\n});\n\ntest('test 29 - combined namespace + pseudo-element', () => {\n    const res = calculateSpecificity('svg|rect::part(foo)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // rect + ::part\n});\n\ntest('test 30 - multiple pseudo-elements', () => {\n    const res = calculateSpecificity('::slotted(span)::part(button)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2);\n});\n\ntest('test 31 - multiple pseudo-classes', () => {\n    const res = calculateSpecificity('div.class1.class2:hover:focus');\n    expect(res.A).toBe(0); // no ID\n    expect(res.B).toBe(4); // 2 classes + :hover + :focus\n    expect(res.C).toBe(1); // div type selector\n});\n\ntest('test 32 - pseudo-element and class', () => {\n    const res = calculateSpecificity('p::before.highlight');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .highlight\n    expect(res.C).toBe(2); // p + ::before\n});\n\ntest('test 33 - nested :is() and :not()', () => {\n    const res = calculateSpecificity(':is(.a, #b):not(.c)');\n    expect(res.A).toBe(1); // max of #b\n    expect(res.B).toBe(1); // .c\n    expect(res.C).toBe(0);\n});\n\ntest('test 34 - complex descendant combinators', () => {\n    const res = calculateSpecificity('ul li .item > a#link:hover');\n    expect(res.A).toBe(1); // #link\n    expect(res.B).toBe(2); // .item + :hover\n    expect(res.C).toBe(3); // ul + li + a\n});\n\ntest('test 35 - attribute selectors', () => {\n    const res = calculateSpecificity('[data-id=\"123\"].active');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // [attr] + .active\n    expect(res.C).toBe(0);\n});\n\ntest('test 36 - multiple pseudo-elements', () => {\n    const res = calculateSpecificity('div::first-line::after');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(3); // div + ::first-line + ::after\n});\n\ntest('test 37 - universal selector with class', () => {\n    const res = calculateSpecificity('*[role=\"button\"].btn');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // [role] + .btn\n    expect(res.C).toBe(0); // universal selector doesn't count\n});\n\ntest('test 38 - nested :has()', () => {\n    const res = calculateSpecificity('div:has(> span.highlight, a#link)');\n    expect(res.A).toBe(1); // #link (most specific argument: a#link)\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // div + a\n});\n\ntest('test 39 - :where() does not increase specificity', () => {\n    const res = calculateSpecificity(':where(.a, #b)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 40 - type selector and namespace', () => {\n    const res = calculateSpecificity('svg|circle.special');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .special\n    expect(res.C).toBe(1); // circle type\n});\n\ntest('test 41 - multiple combinators and pseudo-elements', () => {\n    const res = calculateSpecificity('header nav > ul li::after');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(5); // header + nav + ul + li + ::after\n});\n\ntest('test 42 - :nth-last-child with of selector list', () => {\n    const res = calculateSpecificity(':nth-last-child(3 of .x, #y, a)');\n    expect(res.A).toBe(1); // max from #y\n    expect(res.B).toBe(1); // nth-last-child itself\n    expect(res.C).toBe(0);\n});\n\ntest('test 43 - escaping in identifiers', () => {\n    const res = calculateSpecificity('.class\\\\#escaped #id\\\\:special');\n    expect(res.A).toBe(1); // #id\\:special\n    expect(res.B).toBe(1); // .class\\#escaped\n    expect(res.C).toBe(0);\n});\n\ntest('test 44 - nested functional pseudo-classes', () => {\n    const res = calculateSpecificity(':is(:not(.a), :has(#b))');\n    expect(res.A).toBe(1); // #b (most specific :is arg: :has(#b) = (1,0,0))\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 45 - complex all together', () => {\n    const res = calculateSpecificity('body > header.navbar :is(ul li:first-child, a#link.active):hover');\n    expect(res.A).toBe(1); // #link (most specific :is arg: a#link.active)\n    expect(res.B).toBe(3); // .navbar + .active + :hover\n    expect(res.C).toBe(3); // body + header + a\n});\n\ntest('test 46 - deeply nested :is() and :not()', () => {\n    const res = calculateSpecificity(':is(:not(.a, #b), .c)');\n    expect(res.A).toBe(1); // #b (most specific :is arg: :not(.a, #b) = (1,0,0))\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 47 - multiple combinators with pseudo-classes', () => {\n    const res = calculateSpecificity('div > ul li:first-child.active + a:hover');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(3); // .active + :first-child + :hover\n    expect(res.C).toBe(4); // div + ul + li + a\n});\n\ntest('test 48 - :slotted pseudo-class', () => {\n    const res = calculateSpecificity(':slotted(.item#id)');\n    expect(res.A).toBe(1); // #id\n    expect(res.B).toBe(1); // .item\n    expect(res.C).toBe(0);\n});\n\ntest('test 49 - :host() pseudo-class', () => {\n    const res = calculateSpecificity(':host(.container)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // :host pseudo-class + .container\n    expect(res.C).toBe(0);\n});\n\ntest('test 50 - :host-context() pseudo-class', () => {\n    const res = calculateSpecificity(':host-context(#parent) .child');\n    expect(res.A).toBe(1); // #parent\n    expect(res.B).toBe(2); // :host-context pseudo-class + .child\n    expect(res.C).toBe(0);\n});\n\ntest('test 51 - multiple pseudo-elements in chain', () => {\n    const res = calculateSpecificity('div::first-letter::after');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(3); // div + ::first-letter + ::after\n});\n\ntest('test 52 - universal + class + pseudo', () => {\n    const res = calculateSpecificity('*:hover.active');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // .active + :hover\n    expect(res.C).toBe(0); // * doesn't count\n});\n\ntest('test 53 - type + class + attribute', () => {\n    const res = calculateSpecificity('button.btn[type=\"submit\"]');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // .btn + [type]\n    expect(res.C).toBe(1); // button\n});\n\ntest('test 54 - deeply nested :has()', () => {\n    const res = calculateSpecificity('div:has(ul li:first-child, a#link)');\n    expect(res.A).toBe(1); // #link (most specific argument: a#link)\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // div + a\n});\n\ntest('test 55 - multiple :where()', () => {\n    const res = calculateSpecificity(':where(.a, #b):where(.c, div)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 56 - type selector + namespace', () => {\n    const res = calculateSpecificity('html|body main|article.section');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .section\n    expect(res.C).toBe(2); // body + article (namespaced type selectors)\n});\n\ntest('test 57 - multiple descendant combinators', () => {\n    const res = calculateSpecificity('header nav ul li a.link:hover');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(2); // .link + :hover\n    expect(res.C).toBe(5); // header + nav + ul + li + a\n});\n\ntest('test 58 - :not() with type + class', () => {\n    const res = calculateSpecificity(':not(div.item)');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .item\n    expect(res.C).toBe(1); // div\n});\n\ntest('test 59 - :is() inside :has()', () => {\n    const res = calculateSpecificity('section:has(:is(.a, #b))');\n    expect(res.A).toBe(1); // #b (most specific :is arg: #b = (1,0,0))\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1); // section\n});\n\ntest('test 60 - multiple functional pseudo-classes', () => {\n    const res = calculateSpecificity(':not(:is(.a, #b)):has(.c)');\n    expect(res.A).toBe(1); // #b (most specific :is arg: #b = (1,0,0))\n    expect(res.B).toBe(1); // .c from :has()\n    expect(res.C).toBe(0);\n});\n\n// --- Legacy single-colon pseudo-elements ---\n\ntest('test 61 - legacy :before pseudo-element', () => {\n    const res = calculateSpecificity('p:before');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // p + :before (pseudo-element)\n});\n\ntest('test 62 - legacy :after pseudo-element', () => {\n    const res = calculateSpecificity('div.item:after');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .item\n    expect(res.C).toBe(2); // div + :after\n});\n\ntest('test 63 - legacy :first-line pseudo-element', () => {\n    const res = calculateSpecificity('p:first-line');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // p + :first-line\n});\n\ntest('test 64 - legacy :first-letter pseudo-element', () => {\n    const res = calculateSpecificity('p:first-letter');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(2); // p + :first-letter\n});\n\n// --- :matches() and vendor-prefixed :any() ---\n\ntest('test 65 - :matches() behaves like :is()', () => {\n    const res = calculateSpecificity(':matches(.a, #b)');\n    expect(res.A).toBe(1); // most specific arg: #b\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 66 - :-webkit-any() behaves like :is()', () => {\n    const res = calculateSpecificity(':-webkit-any(.a, #b)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 67 - :-moz-any() behaves like :is()', () => {\n    const res = calculateSpecificity(':-moz-any(.a, #b)');\n    expect(res.A).toBe(1);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\n// --- Column combinator || ---\n\ntest('test 68 - column combinator ||', () => {\n    const res = calculateSpecificity('col.selected || td');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .selected\n    expect(res.C).toBe(2); // col + td\n});\n\n// --- Nesting selector & ---\n\ntest('test 69 - bare nesting selector & has zero specificity', () => {\n    const res = calculateSpecificity('&');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(0);\n});\n\ntest('test 70 - & with class', () => {\n    const res = calculateSpecificity('&.active');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .active\n    expect(res.C).toBe(0); // & has zero specificity on its own\n});\n\ntest('test 71 - & as descendant', () => {\n    const res = calculateSpecificity('& .child');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // .child\n    expect(res.C).toBe(0); // & has zero specificity on its own\n});\n\ntest('test 72 - & with pseudo-class', () => {\n    const res = calculateSpecificity('&:hover');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(1); // :hover\n    expect(res.C).toBe(0); // & has zero specificity on its own\n});\n\ntest('test 73 - & with pseudo-element', () => {\n    const res = calculateSpecificity('&::before');\n    expect(res.A).toBe(0);\n    expect(res.B).toBe(0);\n    expect(res.C).toBe(1); // ::before\n});\n\n// --- Performance test ---\n\ntest('performance - 100k iterations across selector categories', () => {\n    const selectors = {\n        simple: [\n            'div',\n            '.btn',\n            '#main',\n            'div.container',\n            'ul li a.link',\n            'body > header nav ul li',\n            '#app .sidebar .nav-item',\n            'main > section article p span',\n        ],\n        withAttributes: [\n            '[data-id]',\n            'input[type=\"text\"]',\n            'a[href^=\"https\"][target=\"_blank\"].external',\n            'div[class~=\"active\"][role=\"button\"]',\n        ],\n        withPseudoClasses: [\n            'a:hover',\n            'div:first-child',\n            'li:nth-child(2n+1)',\n            'input:focus:not(:disabled)',\n            'tr:nth-child(odd):hover',\n        ],\n        withPseudoElements: [\n            'p::before',\n            'div::after',\n            'p:before',\n            'h1::first-line',\n            'blockquote::first-letter',\n        ],\n        withIsNotHas: [\n            ':is(.a, .b, .c)',\n            ':not(#main)',\n            ':has(> .child)',\n            ':is(.nav, #sidebar):not(.hidden)',\n            'div:has(> span.highlight, a#link)',\n            ':is(:not(.a, #b), .c)',\n        ],\n        withWhere: [\n            ':where(.a, #b)',\n            ':where(div, span):is(.active)',\n        ],\n        withHostSlotted: [\n            ':host(.container)',\n            ':host-context(#parent) .child',\n            ':slotted(.item#id)',\n        ],\n        complex: [\n            'body > header.navbar :is(ul li:first-child, a#link.active):hover',\n            ':not(:is(.a, #b)):has(.c)',\n            'section:has(:is(.a, #b))',\n            'div > ul li:first-child.active + a:hover',\n            'col.selected || td',\n        ],\n    };\n\n    const allSelectors = Object.values(selectors).flat();\n    const iterations = 100_000;\n\n    // Warmup\n    for (let i = 0; i < 1000; i++) {\n        for (const sel of allSelectors) calculateSpecificity(sel);\n    }\n\n    const categoryResults: Record<string, { ops: number; nsPerOp: number }> = {};\n\n    // Benchmark per category\n    for (const [category, sels] of Object.entries(selectors)) {\n        const start = performance.now();\n        for (let i = 0; i < iterations; i++) {\n            for (const sel of sels) calculateSpecificity(sel);\n        }\n        const elapsed = performance.now() - start;\n        const totalOps = iterations * sels.length;\n        categoryResults[category] = {\n            ops: totalOps,\n            nsPerOp: (elapsed * 1_000_000) / totalOps,\n        };\n    }\n\n    // Overall benchmark\n    const overallStart = performance.now();\n    for (let i = 0; i < iterations; i++) {\n        for (const sel of allSelectors) calculateSpecificity(sel);\n    }\n    const overallElapsed = performance.now() - overallStart;\n    const totalOps = iterations * allSelectors.length;\n\n    // Print results\n    console.log('\\n── Specificity Calculator Performance ──');\n    console.log(`Total: ${totalOps.toLocaleString()} ops in ${overallElapsed.toFixed(1)}ms (${((overallElapsed * 1_000_000) / totalOps).toFixed(0)}ns/op)\\n`);\n    for (const [category, result] of Object.entries(categoryResults)) {\n        console.log(`  ${category.padEnd(22)} ${result.nsPerOp.toFixed(0).padStart(5)}ns/op  (${result.ops.toLocaleString()} ops)`);\n    }\n    console.log('');\n\n    // Sanity check: should complete in reasonable time (< 5 seconds total)\n    expect(overallElapsed).toBeLessThan(5000);\n});\n"
  },
  {
    "path": "packages/web-component-designer/tests/SvgGeometryPlacement.test.ts",
    "content": "import { afterAll, beforeAll, expect, test } from '@jest/globals';\n\nconst svgElementGlobals = [\n  'SVGPathElement',\n  'SVGRectElement',\n  'SVGCircleElement',\n  'SVGEllipseElement',\n  'SVGLineElement',\n  'SVGPolylineElement',\n  'SVGPolygonElement'\n] as const;\n\ntype SvgElementGlobalName = (typeof svgElementGlobals)[number];\n\nlet transformOffsetByInverseLinearMatrix: typeof import('../src/elements/helper/LayoutHelper').transformOffsetByInverseLinearMatrix;\nconst originalGlobals = new Map<SvgElementGlobalName, unknown>();\n\nbeforeAll(async () => {\n  for (const globalName of svgElementGlobals) {\n    originalGlobals.set(globalName, (<any>globalThis)[globalName]);\n    (<any>globalThis)[globalName] = class { };\n  }\n\n  ({ transformOffsetByInverseLinearMatrix } = await import('../src/elements/helper/LayoutHelper'));\n});\n\nafterAll(() => {\n  for (const globalName of svgElementGlobals) {\n    const originalGlobal = originalGlobals.get(globalName);\n    if (originalGlobal === undefined)\n      delete (<any>globalThis)[globalName];\n    else\n      (<any>globalThis)[globalName] = originalGlobal;\n  }\n});\n\ntest('maps visual svg placement offset through inverse rotation', () => {\n  const result = transformOffsetByInverseLinearMatrix(\n    { x: 10, y: 0 },\n    { a: 0, b: 1, c: -1, d: 0 }\n  );\n\n  expect(result.x).toBeCloseTo(0);\n  expect(result.y).toBeCloseTo(-10);\n});\n\ntest('maps visual svg placement offset through inverse scale and skew', () => {\n  const result = transformOffsetByInverseLinearMatrix(\n    { x: 14, y: 10 },\n    { a: 2, b: 0, c: 1, d: 4 }\n  );\n\n  expect(result.x).toBeCloseTo(5.75);\n  expect(result.y).toBeCloseTo(2.5);\n});\n\ntest('keeps svg placement offset unchanged for non-invertible transforms', () => {\n  const result = transformOffsetByInverseLinearMatrix(\n    { x: 14, y: 10 },\n    { a: 0, b: 0, c: 0, d: 0 }\n  );\n\n  expect(result).toEqual({ x: 14, y: 10 });\n});\n"
  },
  {
    "path": "packages/web-component-designer/tests/SvgPathDataSourceMap.test.ts",
    "content": "import { expect, test } from '@jest/globals';\nimport { parseSvgPathDataSourceMap } from '../dist/elements/services/sourceMapService/SvgPathDataSourceMap';\n\ntest('maps svg path coordinates to segment handles', () => {\n  const source = 'M 10 20 L 30 40 C 1 2 3 4 5 6';\n  const ranges = parseSvgPathDataSourceMap(source);\n\n  expect(ranges).toEqual([\n    { segmentIndex: 0, handleType: 'anchor', start: 2, length: 5 },\n    { segmentIndex: 1, handleType: 'anchor', start: 10, length: 5 },\n    { segmentIndex: 2, handleType: 'cp1', start: 18, length: 3 },\n    { segmentIndex: 2, handleType: 'cp2', start: 22, length: 3 },\n    { segmentIndex: 2, handleType: 'anchor', start: 26, length: 3 },\n  ]);\n});\n\ntest('maps implicit line segments after moveto', () => {\n  const source = 'M0 0 10 10 20 20';\n  const ranges = parseSvgPathDataSourceMap(source);\n\n  expect(ranges.map(x => ({ segmentIndex: x.segmentIndex, handleType: x.handleType }))).toEqual([\n    { segmentIndex: 0, handleType: 'anchor' },\n    { segmentIndex: 1, handleType: 'anchor' },\n    { segmentIndex: 2, handleType: 'anchor' },\n  ]);\n});\n"
  },
  {
    "path": "packages/web-component-designer/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false,\r\n    \"sourceMap\": true,\r\n    \"allowJs\": true,\r\n    \"noImplicitAny\": false,\r\n    \"noUncheckedSideEffectImports\": true,\r\n    \"moduleResolution\": \"bundler\",\r\n    \"strictNullChecks\": false\r\n  },\r\n  \"include\": [\r\n    \"./src/**/*.ts\",\r\n    \"./src/**/*.js\",\r\n    \"./src/**/*.json\"\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-ace/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-codeview-ace/README.md",
    "content": "# web-component-designer-codeview-ace\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-codeview-ace\r\n\r\n     npm i @node-projects/web-component-designer-codeview-ace\r\n\r\n## Description\r\n\r\nThis uses the ACE Codeeditor for the Webcomponent Designer Codeview\r\n\r\n## Usage\r\n\r\n    import { CodeViewAce } from '@node-projects/web-component-designer-codeview-ace';\r\n    serviceContainer.config.codeViewWidget = CodeViewAce;"
  },
  {
    "path": "packages/web-component-designer-codeview-ace/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Code Editor using ACE\",\n  \"name\": \"@node-projects/web-component-designer-codeview-ace\",\n  \"version\": \"0.1.3\",\n  \"type\": \"module\",\n  \"main\": \"./dist/widgets/codeView/code-view-ace.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"ace-builds\": \"^1.36.3\",\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\n    \"@node-projects/web-component-designer\": \">=0.1.224\"    \n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-codeview-ace/src/widgets/codeView/code-view-ace.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { ICodeView, IDisposable, IUiCommand, CommandType, IStringPosition } from '@node-projects/web-component-designer';\r\nimport type { Ace } from \"ace-builds\";\r\n\r\nclass CodeViewAceCompleter {\r\n  getCompletions(editor, session, pos, prefix, callback) {\r\n    if (prefix.length === 0) { callback(null, []); return }\r\n\r\n\r\n    let wordList = ['t-t', 'visu-conveyor']; //TODO: get word list from custom elements \r\n    {\r\n      callback(null, wordList.map((w) => {\r\n        return { name: w, value: w, score: 1, meta: \"tag\" }\r\n      }));\r\n    }\r\n  }\r\n}\r\n\r\nexport class CodeViewAce extends BaseCustomWebComponentLazyAppend implements ICodeView, IDisposable {\r\n  canvasElement: HTMLElement;\r\n  elementsToPackages: Map<string, string>;\r\n\r\n  public code: string;\r\n  public onTextChanged = new TypedEvent<string>();\r\n\r\n  private _aceEditor: Ace.Editor;\r\n  private _editor: HTMLDivElement;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      width: 100%;\r\n    }\r\n    `;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this.style.display = 'block';\r\n    this._editor = document.createElement(\"div\");\r\n    this._editor.style.height = '100%';\r\n    this._editor.style.width = '100%';\r\n\r\n    this.shadowRoot.appendChild(this._editor)\r\n  }\r\n  dispose(): void {\r\n    this._aceEditor?.destroy();\r\n  }\r\n\r\n  executeCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n        this._aceEditor.execCommand('undo');\r\n        break;\r\n      case CommandType.redo:\r\n        this._aceEditor.execCommand('redo');\r\n        break;\r\n      case CommandType.copy:\r\n        let text = this._aceEditor.getCopyText();\r\n        this._aceEditor.execCommand(\"copy\");\r\n        navigator.clipboard.writeText(text);\r\n        break;\r\n      case CommandType.paste:\r\n        navigator.clipboard.readText().then(text => {\r\n          this._aceEditor.execCommand(\"paste\", text)\r\n        });\r\n        break;\r\n      case CommandType.cut:\r\n        text = this._aceEditor.getCopyText();\r\n        this._aceEditor.execCommand(\"cut\");\r\n        navigator.clipboard.writeText(text);\r\n        break;\r\n      case CommandType.delete:\r\n        this._aceEditor.execCommand(\"delete\");\r\n        break;\r\n    }\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n      case CommandType.redo:\r\n      case CommandType.copy:\r\n      case CommandType.paste:\r\n      case CommandType.cut:\r\n      case CommandType.delete:\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  focusEditor() {\r\n    requestAnimationFrame(() => {\r\n      this.focus();\r\n      this._aceEditor.focus();\r\n    });\r\n  }\r\n\r\n  oneTimeSetup() {\r\n    //@ts-ignore\r\n    let langTools = ace.require(\"ace/ext/language_tools\");\r\n    langTools.addCompleter(new CodeViewAceCompleter());\r\n  }\r\n\r\n  ready() {\r\n    //@ts-ignore\r\n    this._aceEditor = ace.edit(this._editor, {\r\n      theme: \"ace/theme/chrome\",\r\n      mode: \"ace/mode/html\",\r\n      value: \"\",\r\n      autoScrollEditorIntoView: true,\r\n      fontSize: \"14px\",\r\n      showPrintMargin: false,\r\n      displayIndentGuides: true,\r\n      enableBasicAutocompletion: true,\r\n      enableSnippets: true,\r\n      enableLiveAutocompletion: true\r\n    });\r\n    //own snippet completer: http://plnkr.co/edit/6MVntVmXYUbjR0DI82Cr?p=preview\r\n    this._aceEditor.renderer.attachToShadowRoot();\r\n\r\n    let observer = new MutationObserver((m) => {\r\n      this._aceEditor.setAutoScrollEditorIntoView(false);\r\n      this._aceEditor.setAutoScrollEditorIntoView(true);\r\n    });\r\n    let config = { attributes: true, childList: true, characterData: true };\r\n    observer.observe(this.shadowRoot.querySelector('.ace_content'), config);\r\n\r\n    this._aceEditor.on('change', () => this.onTextChanged.emit(this._aceEditor.getValue()));\r\n  }\r\n\r\n  update(code) {\r\n    this._aceEditor.setValue(code);\r\n    this._aceEditor.clearSelection();\r\n  }\r\n  getText() {\r\n    return this._aceEditor.getValue();\r\n  }\r\n\r\n  setSelection(position: IStringPosition) {\r\n    let point1 = this._aceEditor.session.getDocument().indexToPosition(position.start, 0);\r\n    let point2 = this._aceEditor.session.getDocument().indexToPosition(position.start + position.length, 0);\r\n    //@ts-ignore\r\n    this._aceEditor.selection.setRange({ start: point1, end: point2 });\r\n    //@ts-ignore\r\n    this._aceEditor.scrollToLine(point1.row);\r\n  }\r\n  //TODO: reset undo stack, when and why?\r\n  //bind to global und and redo\r\n  //editor.getSession().setUndoManager(new ace.UndoManager())\r\n}\r\n\r\ncustomElements.define('node-projects-code-view-ace', CodeViewAce);"
  },
  {
    "path": "packages/web-component-designer-codeview-ace/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror/README.md",
    "content": "# web-component-designer-codeview-codemirror\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-codeview-codemirror\r\n\r\n     npm i @node-projects/web-component-designer-codeview-codemirror\r\n\r\n## Description\r\n\r\nThis uses the Codemirror Editor for the Webcomponent Designer Codeview\r\n\r\n## Usage\r\n\r\n    import { CodeViewCodeMirror } from '@node-projects/web-component-designer-codeview-codemirror';\r\n    serviceContainer.config.codeViewWidget = CodeViewCodeMirror;"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Code Editor using Codemirror 6 and newer\",\n  \"name\": \"@node-projects/web-component-designer-codeview-codemirror\",\n  \"version\": \"0.1.4\",\n  \"type\": \"module\",\n  \"main\": \"./dist/widgets/codeView/code-view-codemirror.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"codemirror\": \"^6.0.1\",\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\n    \"@node-projects/web-component-designer\": \">=0.1.224\"    \n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror/src/widgets/codeView/code-view-codemirror.ts",
    "content": "/*\r\nimport { BaseCustomWebComponentLazyAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { ICodeView, IDisposable, IUiCommand, CommandType, IStringPosition } from '@node-projects/web-component-designer';\r\nimport CodeMirror from 'codemirror';\r\n\r\nexport class CodeViewCodeMirror extends BaseCustomWebComponentLazyAppend implements ICodeView, IDisposable {\r\n  canvasElement: HTMLElement;\r\n  elementsToPackages: Map<string, string>;\r\n\r\n  public code: string;\r\n  public onTextChanged = new TypedEvent<string>();\r\n  public mode: string = 'xml';\r\n\r\n  private _codeMirrorEditor: CodeMirror.Editor;\r\n  private _editor: HTMLTextAreaElement;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      width: 100%;\r\n    }`;\r\n\r\n  static override readonly template = html`\r\n    <div  style=\"width: 100%; height: 100%; overflow: auto;\">\r\n      <div id=\"textarea\"></div>\r\n    </div>`;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    \r\n    this.style.display = 'block';\r\n    this._editor = this._getDomElement<HTMLTextAreaElement>('textarea');\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  executeCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n        this._codeMirrorEditor.undo();\r\n        break;\r\n      case CommandType.redo:\r\n        this._codeMirrorEditor.redo();\r\n        break;\r\n      case CommandType.copy:\r\n        const text = this._codeMirrorEditor.getSelection();\r\n        navigator.clipboard.writeText(text);\r\n        break;\r\n      case CommandType.paste:\r\n        navigator.clipboard.readText().then(text => {\r\n          this._codeMirrorEditor.replaceSelection(text);\r\n        });\r\n        break;\r\n      case CommandType.cut:\r\n        const textc = this._codeMirrorEditor.getSelection();\r\n        navigator.clipboard.writeText(textc);\r\n        this._codeMirrorEditor.replaceSelection('');\r\n        break;\r\n      case CommandType.delete:\r\n        this._codeMirrorEditor.replaceSelection('');\r\n        break;\r\n    }\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n      case CommandType.redo:\r\n      case CommandType.copy:\r\n      case CommandType.paste:\r\n      case CommandType.cut:\r\n      case CommandType.delete:\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  focusEditor() {\r\n    requestAnimationFrame(() => {\r\n      this.focus();\r\n      this._codeMirrorEditor.focus();\r\n    });\r\n  }\r\n\r\n  ready() {\r\n    const config: CodeMirror.EditorConfiguration = {\r\n      tabSize: 3,\r\n      lineNumbers: true,\r\n      mode: this.mode,\r\n      //@ts-ignore\r\n      htmlMode: true,\r\n      lineWrapping: true,\r\n      //@ts-ignore\r\n      extraKeys: { \"Ctrl-Q\": function (cm) { cm.foldCode(cm.getCursor()); } },\r\n      foldGutter: true,\r\n      gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\r\n    };\r\n\r\n    this._codeMirrorEditor = CodeMirror(this._editor, config);\r\n    this._codeMirrorEditor.setSize('100%', '100%');\r\n    this._codeMirrorEditor.on('change', () => this.onTextChanged.emit(this._codeMirrorEditor.getValue()))\r\n  }\r\n\r\n  update(code) {\r\n    this._codeMirrorEditor.setValue(code);\r\n  }\r\n  getText() {\r\n    return this._codeMirrorEditor.getValue();\r\n  }\r\n\r\n  setSelection(position: IStringPosition) {\r\n\r\n    let point1 = this._codeMirrorEditor.posFromIndex(position.start);\r\n    let point2 = this._codeMirrorEditor.posFromIndex(position.start + position.length);\r\n    this._codeMirrorEditor.setSelection(point1, point2);\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-code-view-codemirror', CodeViewCodeMirror);\r\n*/"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror5/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror5/README.md",
    "content": "# web-component-designer-codeview-codemirror5\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-codeview-codemirror5\r\n\r\n     npm i @node-projects/web-component-designer-codeview-codemirror5\r\n\r\n## Description\r\n\r\nThis uses the Codemirror Version 5 Editor for the Webcomponent Designer Codeview.\r\n\r\nThe NPM Package is linked via alias from \"codemirror5\" directory not \"codemirror\", look at package.json on how to do.\r\n\r\n## Usage\r\n\r\n    import { CodeViewCodeMirror5 } from '@node-projects/web-component-designer-codeview-codemirror5';\r\n    serviceContainer.config.codeViewWidget = CodeViewCodeMirror5;"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror5/package.json",
    "content": "{\r\n  \"description\": \"web-component-designer addon: Code Editor using Codemirror 5\",\r\n  \"name\": \"@node-projects/web-component-designer-codeview-codemirror5\",\r\n  \"version\": \"0.1.5\",\r\n  \"type\": \"module\",\r\n  \"main\": \"./dist/widgets/codeView/code-view-codemirror5.js\",\r\n  \"author\": \"jochen.kuehner@gmx.de\",\r\n  \"license\": \"MIT\",\r\n  \"scripts\": {\r\n    \"tsc\": \"tsc\",\r\n    \"build\": \"tsc\",\r\n    \"link\": \"npm link\",\r\n    \"prepublishOnly\": \"npm run build\"\r\n  },\r\n  \"dependencies\": {\r\n    \"codemirror5\": \"npm:codemirror@^5.0.0\",\r\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\r\n    \"@node-projects/web-component-designer\": \">=0.1.224\"    \r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror5/src/widgets/codeView/code-view-codemirror5.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, cssFromString, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { ICodeView, IDisposable, IUiCommand, CommandType, IStringPosition } from '@node-projects/web-component-designer';\r\nimport CodeMirror from 'codemirror5';\r\n\r\nexport class CodeViewCodeMirror5 extends BaseCustomWebComponentLazyAppend implements ICodeView, IDisposable {\r\n  canvasElement: HTMLElement;\r\n  elementsToPackages: Map<string, string>;\r\n\r\n  public code: string;\r\n  public onTextChanged = new TypedEvent<string>();\r\n  public mode: string = 'xml';\r\n\r\n  private _codeMirrorEditor: CodeMirror.Editor;\r\n  private _editor: HTMLTextAreaElement;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      width: 100%;\r\n    }`;\r\n\r\n  static override readonly template = html`\r\n    <div  style=\"width: 100%; height: 100%; overflow: auto;\">\r\n      <div id=\"textarea\"></div>\r\n    </div>`;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    //@ts-ignore\r\n    import(\"codemirror5/lib/codemirror.css\", { with: { type: 'css' } }).then(x => this.shadowRoot.adoptedStyleSheets = [cssFromString(x), ...this.shadowRoot.adoptedStyleSheets]);\r\n    //@ts-ignore\r\n    import(\"codemirror5/addon/fold/foldgutter.css\", { with: { type: 'css' } }).then(x => this.shadowRoot.adoptedStyleSheets = [cssFromString(x), ...this.shadowRoot.adoptedStyleSheets]);\r\n\r\n    this.style.display = 'block';\r\n    this._editor = this._getDomElement<HTMLTextAreaElement>('textarea');\r\n  }\r\n\r\n  dispose(): void {\r\n  }\r\n\r\n  executeCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n        this._codeMirrorEditor.undo();\r\n        break;\r\n      case CommandType.redo:\r\n        this._codeMirrorEditor.redo();\r\n        break;\r\n      case CommandType.copy:\r\n        const text = this._codeMirrorEditor.getSelection();\r\n        navigator.clipboard.writeText(text);\r\n        break;\r\n      case CommandType.paste:\r\n        navigator.clipboard.readText().then(text => {\r\n          this._codeMirrorEditor.replaceSelection(text);\r\n        });\r\n        break;\r\n      case CommandType.cut:\r\n        const textc = this._codeMirrorEditor.getSelection();\r\n        navigator.clipboard.writeText(textc);\r\n        this._codeMirrorEditor.replaceSelection('');\r\n        break;\r\n      case CommandType.delete:\r\n        this._codeMirrorEditor.replaceSelection('');\r\n        break;\r\n    }\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n      case CommandType.redo:\r\n      case CommandType.copy:\r\n      case CommandType.paste:\r\n      case CommandType.cut:\r\n      case CommandType.delete:\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  focusEditor() {\r\n    requestAnimationFrame(() => {\r\n      this.focus();\r\n      this._codeMirrorEditor.focus();\r\n    });\r\n  }\r\n\r\n  ready() {\r\n    const config: CodeMirror.EditorConfiguration = {\r\n      tabSize: 3,\r\n      lineNumbers: true,\r\n      mode: this.mode,\r\n      //@ts-ignore\r\n      htmlMode: true,\r\n      lineWrapping: true,\r\n      //@ts-ignore\r\n      extraKeys: { \"Ctrl-Q\": function (cm) { cm.foldCode(cm.getCursor()); } },\r\n      foldGutter: true,\r\n      gutters: [\"CodeMirror-linenumbers\", \"CodeMirror-foldgutter\"]\r\n    };\r\n\r\n    this._codeMirrorEditor = CodeMirror(this._editor, config);\r\n    this._codeMirrorEditor.setSize('100%', '100%');\r\n    this._codeMirrorEditor.on('change', () => this.onTextChanged.emit(this._codeMirrorEditor.getValue()))\r\n  }\r\n\r\n  update(code) {\r\n    this._codeMirrorEditor.setValue(code);\r\n  }\r\n  getText() {\r\n    return this._codeMirrorEditor.getValue();\r\n  }\r\n\r\n  setSelection(position: IStringPosition) {\r\n    let point1 = this._codeMirrorEditor.posFromIndex(position.start);\r\n    let point2 = this._codeMirrorEditor.posFromIndex(position.start + position.length);\r\n    this._codeMirrorEditor.setSelection(point1, point2);\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-code-view-codemirror5', CodeViewCodeMirror5);"
  },
  {
    "path": "packages/web-component-designer-codeview-codemirror5/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-monaco/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-codeview-monaco/README.md",
    "content": "# web-component-designer-codeview-monaco\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-codeview-monaco\r\n\r\n     npm i @node-projects/web-component-designer-codeview-monaco\r\n\r\n## Description\r\n\r\nThis uses the Monaco Editor for the Webcomponent Designer Codeview\r\n\r\n## Usage\r\n\r\n    import { CodeViewMonaco } from '@node-projects/web-component-designer-codeview-monaco';\r\n    serviceContainer.config.codeViewWidget = CodeViewMonaco;\r\n\r\nYou need to load the monaco manualy and call\r\n\r\n     await CodeViewMonaco.setMonacoLibrary(...); \r\n     \r\nor via\r\n\r\n     await CodeViewMonaco.loadMonacoEditorViaRequire(); \r\n\r\nor\r\n\r\n     await CodeViewMonaco.loadMonacoEditorViaImport();\r\n\r\nor\r\n\r\n     <script src=\"./node_modules/monaco-editor/min/vs/loader.js\"></script>\r\n     <script>\r\n          require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs', 'vs/css': { disabled: true } } });\r\n          require(['vs/editor/editor.main'], () => { });    \r\n     </script>"
  },
  {
    "path": "packages/web-component-designer-codeview-monaco/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Code Editor using Monaco\",\n  \"name\": \"@node-projects/web-component-designer-codeview-monaco\",\n  \"version\": \"0.2.1\",\n  \"type\": \"module\",\n  \"main\": \"./dist/widgets/codeView/code-view-monaco.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"monaco-editor\": \">=0.50.0\",\n    \"@node-projects/base-custom-webcomponent\": \">=0.19.0\",\n    \"@node-projects/web-component-designer\": \">=0.1.224\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-codeview-monaco/src/widgets/codeView/code-view-monaco.ts",
    "content": "import { BaseCustomWebComponentLazyAppend, css, html, TypedEvent } from '@node-projects/base-custom-webcomponent';\r\nimport { CommandType, IActivateable, ICodeView, InstanceServiceContainer, IStringPosition, IUiCommand, IUiCommandHandler } from '@node-projects/web-component-designer';\r\nimport type * as monacoType from 'monaco-editor';\r\n\r\nexport class CodeViewMonaco extends BaseCustomWebComponentLazyAppend implements ICodeView, IActivateable, IUiCommandHandler {\r\n  private static _monaco: { editor: typeof monacoType.editor, Range: typeof monacoType.Range };\r\n  private static _monacoStyle: CSSStyleSheet;\r\n  public static async getMonacoLib() {\r\n    if (CodeViewMonaco._monaco) {\r\n      return CodeViewMonaco._monaco;\r\n    }\r\n    const monaco = await import('monaco-editor');\r\n    CodeViewMonaco._monaco = monaco;\r\n    //@ts-ignore\r\n    CodeViewMonaco._monacoStyle = (await import(\"monaco-editor/min/vs/editor/editor.main.css\", { with: { type: 'css' } })).default;\r\n    return monaco;\r\n  }\r\n\r\n  private _disableSelectionAfterSel: boolean;\r\n  private _setSelectionTimeout: ReturnType<typeof setTimeout>;\r\n  private _pendingSelectionKey: string;\r\n\r\n  dispose(): void {\r\n    this._monacoEditor?.dispose();\r\n  }\r\n\r\n  canvasElement: HTMLElement;\r\n  elementsToPackages: Map<string, string>;\r\n\r\n  public onTextChanged = new TypedEvent<string>();\r\n  public language: string = 'html';\r\n  public singleRow: boolean = false;\r\n\r\n  private _theme: string = 'vs';\r\n  public get theme(): string {\r\n    return this._theme;\r\n  }\r\n  public set theme(value: string) {\r\n    this._theme = value;\r\n    CodeViewMonaco.getMonacoLib().then(monaco => monaco.editor.setTheme(value));\r\n  }\r\n\r\n  #code: string = null;\r\n  get code() {\r\n    if (this._monacoEditor)\r\n      return this._monacoEditor.getModel().getValue();\r\n    return null;\r\n  }\r\n  set code(v) {\r\n    this.#code = v;\r\n    if (this._monacoEditor)\r\n      this._monacoEditor.getModel().setValue(v);\r\n  }\r\n\r\n  #readOnly = false;\r\n  get readOnly() {\r\n    return this.#readOnly;\r\n  }\r\n  set readOnly(v) {\r\n    this.#readOnly = v;\r\n    if (this._monacoEditor)\r\n      this._monacoEditor.updateOptions({ readOnly: v })\r\n  }\r\n\r\n  static readonly properties = {\r\n    code: String,\r\n    language: String,\r\n    theme: String,\r\n    singleRow: Boolean,\r\n    readOnly: Boolean\r\n  }\r\n\r\n  private _monacoEditor: monacoType.editor.IStandaloneCodeEditor;\r\n  private _editor: HTMLDivElement;\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _disableSelection: boolean;\r\n  private _disableSelectionAfterUpd: boolean;\r\n\r\n  static override readonly style = css`\r\n    :host {\r\n      display: block;\r\n      height: 100%;\r\n      width: 100%;\r\n    }\r\n    `;\r\n\r\n  static override readonly template = html`<div id=\"container\" style=\"overflow: hidden; width: 100%; height: 100%; position: absolute;\"></div>`;\r\n\r\n  executeCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n        this._monacoEditor.trigger('source', 'undo', null);\r\n        break;\r\n      case CommandType.redo:\r\n        this._monacoEditor.trigger('source', 'redo', null);\r\n        break;\r\n      case CommandType.copy:\r\n        this._monacoEditor.trigger('source', 'editor.action.clipboardCopyAction', null);\r\n        break;\r\n      case CommandType.paste:\r\n        this._monacoEditor.trigger('source', 'editor.action.clipboardPasteAction', null);\r\n        break;\r\n      case CommandType.cut:\r\n        break;\r\n      case CommandType.delete:\r\n        break;\r\n    }\r\n  }\r\n\r\n  canExecuteCommand(command: IUiCommand) {\r\n    switch (command.type) {\r\n      case CommandType.undo:\r\n      case CommandType.redo:\r\n      case CommandType.copy:\r\n      case CommandType.paste:\r\n      case CommandType.cut:\r\n      case CommandType.delete:\r\n        return true;\r\n    }\r\n    return false;\r\n  }\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    this.addEventListener(\"keydown\", e => e.stopPropagation());\r\n  }\r\n\r\n  async ready() {\r\n    this._parseAttributesToProperties();\r\n\r\n    const monaco = await CodeViewMonaco.getMonacoLib();\r\n    this.shadowRoot.adoptedStyleSheets = [CodeViewMonaco._monacoStyle, (<any>this.constructor).style];\r\n\r\n    this._editor = this._getDomElement<HTMLDivElement>('container');\r\n\r\n    const resizeObserver = new ResizeObserver(() => {\r\n      if (this._editor.offsetWidth > 0) {\r\n\r\n        let options: monacoType.editor.IStandaloneEditorConstructionOptions = {\r\n          automaticLayout: true,\r\n          theme: this._theme,\r\n          language: this.language,\r\n          value: this.#code,\r\n          fixedOverflowWidgets: true,\r\n          minimap: {\r\n            size: 'fill'\r\n          },\r\n          readOnly: this.#readOnly\r\n        }\r\n        if (this.singleRow) {\r\n          options.minimap.enabled = false;\r\n          options.lineNumbers = 'off';\r\n          options.glyphMargin = false;\r\n          options.folding = false;\r\n          options.lineDecorationsWidth = 0;\r\n          options.lineNumbersMinChars = 0;\r\n        } else {\r\n          options.scrollbar = {};\r\n          options.scrollbar.useShadows = false;\r\n          options.scrollbar.verticalHasArrows = true;\r\n          options.scrollbar.horizontalHasArrows = true;\r\n          options.scrollbar.vertical = 'visible';\r\n          options.scrollbar.horizontal = 'visible';\r\n        }\r\n\r\n        this._monacoEditor = monaco.editor.create(this._editor, options);\r\n\r\n        let selectionTimeout;\r\n        let disableCursorChange;\r\n        let changeContentListener = this._monacoEditor.getModel().onDidChangeContent(e => {\r\n          if (selectionTimeout) {\r\n            clearTimeout(selectionTimeout);\r\n            selectionTimeout = null;\r\n            this._disableSelection = false;\r\n          }\r\n          disableCursorChange = true;\r\n          setTimeout(() => {\r\n            disableCursorChange = false;\r\n          }, 50);\r\n          this.onTextChanged.emit(this._monacoEditor.getValue());\r\n          this.dispatchEvent(new CustomEvent('code-changed'));\r\n        });\r\n        this._monacoEditor.onDidChangeModel(e => {\r\n          changeContentListener.dispose();\r\n          changeContentListener = this._monacoEditor.getModel().onDidChangeContent(e => {\r\n            if (selectionTimeout) {\r\n              clearTimeout(selectionTimeout);\r\n              selectionTimeout = null;\r\n              this._disableSelection = false;\r\n            }\r\n            disableCursorChange = true;\r\n            setTimeout(() => {\r\n              disableCursorChange = false;\r\n            }, 50);\r\n            this.onTextChanged.emit(this._monacoEditor.getValue());\r\n            this.dispatchEvent(new CustomEvent('code-changed'));\r\n          });\r\n        });\r\n        this._monacoEditor.onDidChangeCursorPosition(e => {\r\n          const sel = this._monacoEditor.getSelection();\r\n          const offsetStart = this._monacoEditor.getModel().getOffsetAt(sel.getStartPosition());\r\n          const offsetEnd = this._monacoEditor.getModel().getOffsetAt(sel.getEndPosition());\r\n          if (this._instanceServiceContainer && !this._disableSelectionAfterUpd && !this._disableSelectionAfterSel && !disableCursorChange) {\r\n            this._disableSelection = true;\r\n            if (selectionTimeout)\r\n              clearTimeout(selectionTimeout);\r\n            selectionTimeout = setTimeout(() => {\r\n              selectionTimeout = null;\r\n              this._instanceServiceContainer.selectionService.setSelectionByTextRange(offsetStart, offsetEnd);\r\n              this._disableSelection = false;\r\n            }, 50);\r\n          }\r\n        });\r\n\r\n        this._monacoEditor.focus();\r\n\r\n        resizeObserver.disconnect();\r\n      };\r\n    });\r\n\r\n    resizeObserver.observe(this._editor);\r\n  }\r\n\r\n  focusEditor() {\r\n    requestAnimationFrame(() => {\r\n      this.focus();\r\n      if (this._monacoEditor)\r\n        this._monacoEditor.focus();\r\n    });\r\n  }\r\n\r\n  activated() {\r\n    if (this._monacoEditor)\r\n      if (this._monacoEditor)\r\n        this._monacoEditor.layout();\r\n  }\r\n\r\n  update(code: string, instanceServiceContainer?: InstanceServiceContainer) {\r\n    this.#code = code;\r\n    this._instanceServiceContainer = instanceServiceContainer;\r\n    if (this._monacoEditor) {\r\n      this.clearPendingSetSelection();\r\n      this._disableSelectionAfterUpd = true;\r\n      if (this._monacoEditor)\r\n        this._monacoEditor.setValue(code);\r\n      this._disableSelectionAfterUpd = false;\r\n      CodeViewMonaco.getMonacoLib().then(monaco => {\r\n        monaco.editor.setTheme(this._theme);\r\n        monaco.editor.setModelLanguage(this._monacoEditor.getModel(), this.language);\r\n      });\r\n    }\r\n  }\r\n\r\n  getText() {\r\n    return this._monacoEditor.getValue();\r\n  }\r\n\r\n  setSelection(position: IStringPosition) {\r\n    if (this._monacoEditor && !this._disableSelection && !this._disableSelectionAfterSel && position) {\r\n      const selectionKey = `${position.start}:${position.length}`;\r\n      if (this._pendingSelectionKey === selectionKey)\r\n        return;\r\n\r\n      this._disableSelectionAfterSel = true;\r\n      this._pendingSelectionKey = selectionKey;\r\n      let model = this._monacoEditor.getModel();\r\n      let point1 = model.getPositionAt(position.start);\r\n      let point2 = model.getPositionAt(position.start + position.length);\r\n      this._setSelectionTimeout = setTimeout(async () => {\r\n        this._setSelectionTimeout = null;\r\n        this._pendingSelectionKey = null;\r\n        this._monacoEditor.setSelection({ startLineNumber: point1.lineNumber, startColumn: point1.column, endLineNumber: point2.lineNumber, endColumn: point2.column });\r\n        CodeViewMonaco.getMonacoLib().then(monaco => {\r\n          this._monacoEditor.revealRangeInCenterIfOutsideViewport(new monaco.Range(point1.lineNumber, point1.column, point2.lineNumber, point2.column), 1);\r\n        });\r\n        setTimeout(() => {\r\n          this._disableSelectionAfterSel = false;\r\n        }, 50);\r\n      }, 50);\r\n    }\r\n  }\r\n\r\n  private clearPendingSetSelection() {\r\n    if (this._setSelectionTimeout) {\r\n      clearTimeout(this._setSelectionTimeout);\r\n      this._setSelectionTimeout = null;\r\n    }\r\n    this._pendingSelectionKey = null;\r\n    this._disableSelectionAfterSel = false;\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-code-view-monaco', CodeViewMonaco);\r\n"
  },
  {
    "path": "packages/web-component-designer-codeview-monaco/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/README.md",
    "content": "# @node-projects/web-component-designer-collaboration-service\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-collaboration-service\r\n\r\n```bash\r\nnpm i @node-projects/web-component-designer-collaboration-service\r\n```\r\n\r\n## Description\r\n\r\nThis package adds collaboration features to `@node-projects/web-component-designer`.\r\n\r\nIt provides:\r\n\r\n- a per-document collaboration service\r\n- remote selection and presence overlays\r\n- remote cursor overlays\r\n- comment context menu integration\r\n- a WebRTC transport with `broadcast-channel` and manual copy/paste signaling\r\n\r\nThe package is an addon. It does not patch the default designer bootstrap automatically. You enable it explicitly on your `ServiceContainer`.\r\n\r\n## Quick Start\r\n\r\n```ts\r\nimport { createDefaultServiceContainer } from '@node-projects/web-component-designer';\r\nimport {\r\n     setupCollaborationService,\r\n     WebRtcTabCollaborationTransport,\r\n} from '@node-projects/web-component-designer-collaboration-service';\r\n\r\nconst serviceContainer = createDefaultServiceContainer();\r\nsetupCollaborationService(serviceContainer);\r\n```\r\n\r\nAfter that, each `DocumentContainer` gets a collaboration service instance on its `instanceServiceContainer`.\r\n\r\n```ts\r\nconst collaborationService = documentContainer.instanceServiceContainer.collaborationService;\r\n\r\ncollaborationService.attachTransport(new WebRtcTabCollaborationTransport({\r\n     enabledSignalingChannels: ['broadcast-channel', 'manual'],\r\n     rtcConfiguration: {\r\n          iceServers: [\r\n               { urls: 'stun:stun.l.google.com:19302' },\r\n               { urls: 'turns:turn.example.com:5349?transport=tcp', username: 'demo', credential: 'secret' }\r\n          ]\r\n     }\r\n}));\r\n\r\ncollaborationService.connect(\r\n     'my-session-id',\r\n     'peer-a',\r\n     'Browser A'\r\n);\r\n```\r\n\r\n## What `setupCollaborationService()` Adds\r\n\r\n`setupCollaborationService(serviceContainer)` registers:\r\n\r\n- the `collaborationService` instance factory\r\n- the collaboration node/comment overlay extension\r\n- the collaboration cursor overlay extension\r\n- the collaboration comments context menu entry\r\n\r\nIf you want more control, the package also exports the individual classes:\r\n\r\n- `DefaultCollaborationService`\r\n- `WebRtcTabCollaborationTransport`\r\n- `CollaborationOverlayExtensionProvider`\r\n- `CollaborationCursorOverlayExtensionProvider`\r\n- `CollaborationCommentsContextMenu`\r\n\r\n## Signaling Modes\r\n\r\n`WebRtcTabCollaborationTransport` supports two signaling channels:\r\n\r\n- `broadcast-channel`\r\n     Use this for same-origin tabs in the same browser.\r\n- `manual`\r\n     Use this for different browsers or when you want to move signaling data yourself.\r\n\r\nBoth channels are enabled by default.\r\n\r\n```ts\r\nconst transport = new WebRtcTabCollaborationTransport({\r\n     enabledSignalingChannels: ['manual']\r\n});\r\n```\r\n\r\n## Manual Copy/Paste Signaling\r\n\r\nThe manual signaling API is useful for connecting different browsers without a backend signaling server.\r\n\r\nFor same-browser tabs you usually do not need extra RTC configuration. For different machines, browsers, subnets, VPNs, or internet connections, configure `rtcConfiguration` with suitable STUN or TURN servers. Without that, the browser only has local host candidates available, which often works on one computer but is unreliable across machines.\r\n\r\nIf your TURN credentials rotate, update them before reconnecting. `WebRtcTabCollaborationTransport` also exposes `setRtcConfiguration()` for that use case.\r\n\r\n```ts\r\nconst transport = new WebRtcTabCollaborationTransport({\r\n     enabledSignalingChannels: ['manual'],\r\n     rtcConfiguration: {\r\n          iceServers: [\r\n               { urls: 'stun:stun.l.google.com:19302' },\r\n               { urls: ['turn:turn.example.com:3478?transport=udp', 'turns:turn.example.com:5349?transport=tcp'], username: 'demo', credential: 'secret' }\r\n          ]\r\n     }\r\n});\r\n\r\ntransport.setRtcConfiguration({\r\n     iceServers: [\r\n          { urls: 'stun:stun.l.google.com:19302' },\r\n          { urls: 'turns:turn.example.com:5349?transport=tcp', username: 'demo', credential: 'next-secret' }\r\n     ]\r\n});\r\n\r\nconst bundle = transport.exportManualSignalingData();\r\nawait transport.importManualSignalingData(bundleFromOtherClient);\r\n```\r\n\r\nTypical flow:\r\n\r\n1. Use the same collaboration session id in both clients.\r\n2. Copy the signaling bundle from client A and import it in client B.\r\n3. Copy the updated signaling bundle from client B and import it in client A.\r\n4. If one client still has a newer signaling bundle because of trickled ICE candidates, copy that bundle back once more.\r\n\r\n## Demo Notes\r\n\r\nThe demo toolbar contains a `collab` menu with:\r\n\r\n- session id selection\r\n- signaling-channel toggles\r\n- copy/paste signaling bundle actions\r\n- a help popup with the connection steps\r\n\r\nIn the demo:\r\n\r\n1. Use the same session id in both clients.\r\n2. For same-browser tabs, keep `broadcast signaling` enabled.\r\n3. For different browsers, keep `manual copy/paste signaling` enabled and exchange bundles through the menu.\r\n\r\n## Package Exports\r\n\r\nMain exports:\r\n\r\n- `setupCollaborationService`\r\n- `DefaultCollaborationService`\r\n- `WebRtcTabCollaborationTransport`\r\n- `CollaborationCommentsContextMenu`\r\n- `CollaborationOverlayExtensionProvider`\r\n- `CollaborationCursorOverlayExtensionProvider`\r\n"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/package.json",
    "content": "{\r\n  \"description\": \"web-component-designer addon for collaboration features\",\r\n  \"name\": \"@node-projects/web-component-designer-collaboration-service\",\r\n  \"version\": \"0.2.0\",\r\n  \"type\": \"module\",\r\n  \"main\": \"./dist/index.js\",\r\n  \"author\": \"jochen.kuehner@gmx.de\",\r\n  \"license\": \"MIT\",\r\n  \"scripts\": {\r\n    \"tsc\": \"tsc\",\r\n    \"build\": \"tsc\",\r\n    \"link\": \"npm link\",\r\n    \"prepublishOnly\": \"npm run build && npm run bundle\",\r\n    \"bundle\": \"esbuild ./dist/index.js --format=esm --minify --external:@node-projects/* --platform=neutral --bundle --outfile=./dist/index-min.js\"\r\n  },\r\n  \"dependencies\": {\r\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\r\n    \"@node-projects/web-component-designer\": \">=0.2.0\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"esbuild\": \"^0.25.10\"\r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/extensions/CollaborationCommentsContextMenu.ts",
    "content": "import { ContextmenuInitiator, getCollaborationNodeIndex, ICollaborationComment, ICollaborationService, IContextMenuExtension, IContextMenuItem, IDesignerCanvas, IDesignItem } from '@node-projects/web-component-designer';\n\nfunction truncateText(text: string, maxLength: number) {\n  if (!text || text.length <= maxLength)\n    return text;\n  return text.substring(0, Math.max(0, maxLength - 1)) + '…';\n}\n\nfunction createCommentId() {\n  return globalThis.crypto?.randomUUID?.() ?? `comment-${Date.now()}-${Math.floor(Math.random() * 100000)}`;\n}\n\nexport class CollaborationCommentsContextMenu implements IContextMenuExtension {\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n    return !!designerView.instanceServiceContainer.collaborationService && !!designItem && !designItem.isRootItem;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerCanvas: IDesignerCanvas, designItem: IDesignItem): IContextMenuItem[] {\n    const collaborationService = designerCanvas.instanceServiceContainer.collaborationService;\n    const targetNodeIndex = getCollaborationNodeIndex(designItem);\n    if (targetNodeIndex == null)\n      return [];\n\n    const comments = collaborationService!.comments.filter(x => x.targetNodeIndex === targetNodeIndex);\n    const localPeerId = collaborationService!.session?.peerId ?? 'local';\n\n    const items: IContextMenuItem[] = [\n      {\n        title: 'add comment',\n        action: () => {\n          const value = globalThis.prompt?.('Add comment', '')?.trim();\n          if (!value)\n            return;\n\n          const now = Date.now();\n          collaborationService!.upsertComment({\n            id: createCommentId(),\n            text: value,\n            authorPeerId: localPeerId,\n            targetNodeIndex,\n            targetDesignItemId: designItem.id,\n            createdAt: now,\n            updatedAt: now,\n            resolved: false\n          });\n        }\n      }\n    ];\n\n    if (comments.length) {\n      items.push({ title: '-' });\n      items.push({\n        title: `comments (${comments.length})`,\n        children: comments.map(comment => this.createCommentMenuItem(comment, collaborationService!, designItem))\n      });\n    }\n\n    return items;\n  }\n\n  private createCommentMenuItem(comment: ICollaborationComment, collaborationService: ICollaborationService, designItem: IDesignItem): IContextMenuItem {\n    return {\n      title: `${comment.resolved ? 'resolved' : 'open'}: ${truncateText(comment.text, 36)}`,\n      children: [\n        {\n          title: comment.resolved ? 'reopen' : 'resolve',\n          action: () => collaborationService.upsertComment({\n            ...comment,\n            targetDesignItemId: designItem.id,\n            resolved: !comment.resolved,\n            updatedAt: Date.now()\n          })\n        },\n        {\n          title: 'remove',\n          action: () => collaborationService.removeComment(comment.id)\n        }\n      ]\n    };\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/extensions/CollaborationCursorOverlayExtension.ts",
    "content": "import { AbstractExtension, IExtensionManager, IDesignerCanvas, IDesignItem, ICollaborationPeerPresence, OverlayLayer } from \"@node-projects/web-component-designer\";\n\nfunction truncateText(text: string, maxLength: number) {\n  if (!text || text.length <= maxLength)\n    return text;\n  return text.substring(0, Math.max(0, maxLength - 1)) + '…';\n}\n\nfunction createCursorPath(x: number, y: number) {\n  return [\n    `M ${x} ${y}`,\n    `L ${x} ${y + 18}`,\n    `L ${x + 4.5} ${y + 13.5}`,\n    `L ${x + 8.5} ${y + 23}`,\n    `L ${x + 12} ${y + 21.5}`,\n    `L ${x + 8} ${y + 12.5}`,\n    `L ${x + 15} ${y + 12.5}`,\n    'Z'\n  ].join(' ');\n}\n\nexport class CollaborationCursorOverlayExtension extends AbstractExtension {\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n  }\n\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\n    this.refresh(cache, event);\n  }\n\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\n    const collaborationService = this.extendedItem.instanceServiceContainer.collaborationService;\n    if (!collaborationService || !this.extendedItem.isRootItem) {\n      if (this.overlays.length)\n        this._removeAllOverlays();\n      return;\n    }\n\n    const localPeerId = collaborationService.session?.peerId;\n    const peers = collaborationService.peers\n      .filter(peer => peer.peerId !== localPeerId && !!peer.cursorPosition)\n      .sort((a, b) => a.peerId.localeCompare(b.peerId));\n\n    const overlayState = JSON.stringify(peers.map(peer => ({\n      peerId: peer.peerId,\n      displayName: peer.displayName,\n      color: peer.color,\n      cursorPosition: peer.cursorPosition\n    })));\n\n    if (!peers.length) {\n      if (this.overlays.length)\n        this._removeAllOverlays();\n      this._valuesHaveChanges(overlayState);\n      return;\n    }\n\n    if (!this._valuesHaveChanges(overlayState))\n      return;\n\n    this._removeAllOverlays();\n\n    peers.forEach(peer => this.drawPeerCursor(peer));\n  }\n\n  override dispose() {\n    this._removeAllOverlays();\n  }\n\n  private drawPeerCursor(peer: ICollaborationPeerPresence) {\n    if (!peer.cursorPosition)\n      return;\n\n    const color = peer.color ?? '#1f7ae0';\n    const x = peer.cursorPosition.x;\n    const y = peer.cursorPosition.y;\n\n    const cursor = this._drawPath(createCursorPath(x, y), 'svg-collaboration-cursor', undefined, OverlayLayer.Foreground);\n    cursor.style.fill = color;\n    cursor.style.stroke = '#ffffff';\n    cursor.style.strokeWidth = '1.2px';\n\n    this._drawCircle(x, y, 2.5, 'svg-collaboration-cursor-hotspot', undefined, OverlayLayer.Foreground).style.fill = color;\n\n    this._drawTextWithBackground(\n      truncateText(peer.displayName ?? peer.peerId, 18),\n      x + 16,\n      y + 16,\n      color,\n      'svg-collaboration-cursor-label',\n      undefined,\n      OverlayLayer.Foreground\n    );\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/extensions/CollaborationCursorOverlayExtensionProvider.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { CollaborationCursorOverlayExtension } from './CollaborationCursorOverlayExtension.js';\nimport { IDesignerExtensionProvider, IExtensionManager, IDesignerCanvas, IDesignItem, IDesignerExtension } from '@node-projects/web-component-designer';\n\nexport class CollaborationCursorOverlayExtensionProvider implements IDesignerExtensionProvider {\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\n    return designItem.isRootItem;\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new CollaborationCursorOverlayExtension(extensionManager, designerView, designItem);\n  }\n\n  static readonly style = css`\n    .svg-collaboration-cursor,\n    .svg-collaboration-cursor-hotspot,\n    .svg-collaboration-cursor-label {\n      pointer-events: none;\n    }\n\n    .svg-collaboration-cursor-label {\n      font-size: 11px;\n    }\n  `;\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/extensions/CollaborationOverlayExtension.ts",
    "content": "import { ICollaborationPeerPresence, ICollaborationComment, IDesignItem, AbstractExtension, IExtensionManager, IDesignerCanvas, getCollaborationNodeIndex, OverlayLayer } from \"@node-projects/web-component-designer\";\n\nconst collaborationOverlayCacheKey = Symbol('collaborationOverlayCache');\n\ntype CollaborationOverlayCache = {\n  peerMap: Map<number, ICollaborationPeerPresence[]>;\n  commentMap: Map<number, ICollaborationComment[]>;\n};\n\nfunction getOverlayCache(designItem: IDesignItem, cache: Record<string | symbol, any>): CollaborationOverlayCache {\n  if (cache[collaborationOverlayCacheKey])\n    return cache[collaborationOverlayCacheKey];\n\n  const collaborationService = designItem.instanceServiceContainer.collaborationService;\n  const peerMap = new Map<number, ICollaborationPeerPresence[]>;\n  const commentMap = new Map<number, ICollaborationComment[]>;\n  const localPeerId = collaborationService?.session?.peerId;\n\n  for (const peer of collaborationService?.peers ?? []) {\n    if (!peer || peer.peerId === localPeerId)\n      continue;\n\n    const indexes = new Set<number>();\n    if (peer.activeNodeIndex != null)\n      indexes.add(peer.activeNodeIndex);\n    for (const nodeIndex of peer.selectedNodeIndexes ?? [])\n      indexes.add(nodeIndex);\n\n    for (const nodeIndex of indexes) {\n      const peers = peerMap.get(nodeIndex) ?? [];\n      peers.push(peer);\n      peerMap.set(nodeIndex, peers);\n    }\n  }\n\n  for (const comment of collaborationService?.comments ?? []) {\n    if (comment.targetNodeIndex == null)\n      continue;\n\n    const comments = commentMap.get(comment.targetNodeIndex) ?? [];\n    comments.push(comment);\n    commentMap.set(comment.targetNodeIndex, comments);\n  }\n\n  const overlayCache = { peerMap, commentMap };\n  cache[collaborationOverlayCacheKey] = overlayCache;\n  return overlayCache;\n}\n\nfunction truncateText(text: string, maxLength: number) {\n  if (!text || text.length <= maxLength)\n    return text;\n  return text.substring(0, Math.max(0, maxLength - 1)) + '…';\n}\n\nexport class CollaborationOverlayExtension extends AbstractExtension {\n  constructor(extensionManager: IExtensionManager, designerView: IDesignerCanvas, extendedItem: IDesignItem) {\n    super(extensionManager, designerView, extendedItem);\n  }\n\n  override extend(cache: Record<string | symbol, any>, event?: Event) {\n    this.refresh(cache, event);\n  }\n\n  override refresh(cache: Record<string | symbol, any>, event?: Event) {\n    const collaborationService = this.extendedItem.instanceServiceContainer.collaborationService;\n    if (!collaborationService) {\n      if (this.overlays.length)\n        this._removeAllOverlays();\n      return;\n    }\n\n    const nodeIndex = getCollaborationNodeIndex(this.extendedItem, cache);\n    if (nodeIndex == null) {\n      if (this.overlays.length)\n        this._removeAllOverlays();\n      return;\n    }\n\n    const overlayCache = getOverlayCache(this.extendedItem, cache);\n    const peers = [...(overlayCache.peerMap.get(nodeIndex) ?? [])].sort((a, b) => a.peerId.localeCompare(b.peerId));\n    const comments = [...(overlayCache.commentMap.get(nodeIndex) ?? [])].sort((a, b) => a.createdAt - b.createdAt);\n\n    if (!peers.length && !comments.length) {\n      if (this.overlays.length)\n        this._removeAllOverlays();\n      return;\n    }\n\n    const rect = this.designerCanvas.getNormalizedElementCoordinates(this.extendedItem.element);\n    const overlayState = JSON.stringify({\n      x: rect.x,\n      y: rect.y,\n      width: rect.width,\n      height: rect.height,\n      peers: peers.map(x => ({\n        peerId: x.peerId,\n        displayName: x.displayName,\n        color: x.color,\n        activeNodeIndex: x.activeNodeIndex,\n        selectedNodeIndexes: x.selectedNodeIndexes\n      })),\n      comments: comments.map(x => ({ id: x.id, resolved: x.resolved, text: x.text, authorPeerId: x.authorPeerId }))\n    });\n\n    if (!this._valuesHaveChanges(overlayState))\n      return;\n\n    this._removeAllOverlays();\n\n    peers.forEach((peer, index) => {\n      const offset = index * 3;\n      const peerRect = this._drawRect(rect.x - offset, rect.y - offset, rect.width + (offset * 2), rect.height + (offset * 2), 'svg-collaboration-peer', undefined, OverlayLayer.Foreground);\n      peerRect.style.stroke = peer.color ?? '#1f7ae0';\n      peerRect.style.strokeWidth = peer.activeNodeIndex === nodeIndex ? '2.5px' : '1.5px';\n      peerRect.style.opacity = peer.activeNodeIndex === nodeIndex ? '1' : '0.75';\n      peerRect.style.fill = 'none';\n\n      const labelText = truncateText(peer.displayName ?? peer.peerId, 24);\n      this._drawTextWithBackground(labelText, rect.x - offset, rect.y - 8 - (index * 14), peer.color ?? '#1f7ae0', 'svg-collaboration-label', undefined, OverlayLayer.Foreground);\n    });\n\n    if (comments.length) {\n      const unresolvedComments = comments.filter(x => !x.resolved);\n      const badgeColor = unresolvedComments.length ? '#f57c00' : '#9e9e9e';\n      const commentRect = this._drawRect(rect.x + 2, rect.y + 2, Math.max(0, rect.width - 4), Math.max(0, rect.height - 4), 'svg-collaboration-comment', undefined, OverlayLayer.Foreground);\n      commentRect.style.stroke = badgeColor;\n      commentRect.style.fill = 'none';\n\n      const commentLabel = unresolvedComments.length\n        ? `${unresolvedComments.length} comment${unresolvedComments.length === 1 ? '' : 's'}`\n        : `${comments.length} resolved`;\n      this._drawTextWithBackground(commentLabel, rect.x + rect.width - 12, rect.y + 14, badgeColor, 'svg-collaboration-comment-label', undefined, OverlayLayer.Foreground);\n\n      const previewComment = comments[comments.length - 1];\n      this._drawTextWithBackground(truncateText(previewComment.text, 28), rect.x + 4, rect.y + rect.height + 16, badgeColor, 'svg-collaboration-comment-preview', undefined, OverlayLayer.Foreground);\n    }\n  }\n\n  override dispose() {\n    this._removeAllOverlays();\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/extensions/CollaborationOverlayExtensionProvider.ts",
    "content": "import { css } from '@node-projects/base-custom-webcomponent';\nimport { IDesignerExtensionProvider, IExtensionManager, IDesignerCanvas, IDesignItem, IDesignerExtension } from '@node-projects/web-component-designer';\nimport { CollaborationOverlayExtension } from './CollaborationOverlayExtension.js';\n\nexport class CollaborationOverlayExtensionProvider implements IDesignerExtensionProvider {\n  shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\n    return !designItem.isRootItem && !(designItem.element instanceof HTMLTemplateElement);\n  }\n\n  getExtension(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): IDesignerExtension {\n    return new CollaborationOverlayExtension(extensionManager, designerView, designItem);\n  }\n\n  static readonly style = css`\n    .svg-collaboration-peer {\n      stroke-dasharray: 5 3;\n      vector-effect: non-scaling-stroke;\n      pointer-events: none;\n    }\n\n    .svg-collaboration-comment {\n      stroke-dasharray: 2 4;\n      vector-effect: non-scaling-stroke;\n      pointer-events: none;\n    }\n\n    .svg-collaboration-label,\n    .svg-collaboration-comment-label,\n    .svg-collaboration-comment-preview {\n      font-size: 11px;\n      pointer-events: none;\n    }\n  `;\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/index.ts",
    "content": "export * from './setupCollaborationService.js';\n\nexport * from \"./extensions/CollaborationCommentsContextMenu.js\";\nexport * from \"./extensions/CollaborationOverlayExtensionProvider.js\";\nexport * from \"./extensions/CollaborationOverlayExtension.js\";\nexport * from \"./extensions/CollaborationCursorOverlayExtensionProvider.js\";\nexport * from \"./extensions/CollaborationCursorOverlayExtension.js\";\n\nexport * from \"./services/DefaultCollaborationService.js\";\nexport * from \"./services/WebRtcTabCollaborationTransport.js\";\n\n"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/services/DefaultCollaborationService.ts",
    "content": "import { TypedEvent } from '@node-projects/base-custom-webcomponent';\nimport { CollaborationConnectionState, DomConverter, getCollaborationNodeIndex, getCollaborationNodeIndexes, getDesignItemByCollaborationNodeIndex, ICollaborationComment, ICollaborationCommentsChangedEvent, ICollaborationDocumentSnapshot, ICollaborationPeerPresence, ICollaborationPeersChangedEvent, ICollaborationRemoteChange, ICollaborationSelectionEvent, ICollaborationService, ICollaborationSession, ICollaborationStateChangedEvent, ICollaborationTransport, IDesignerCanvas, ITransactionItem, IUndoChangeEvent, UndoChangeSource } from '@node-projects/web-component-designer';\n\nfunction createPeerColor(peerId: string) {\n  let hash = 0;\n  for (let i = 0; i < peerId.length; i++)\n    hash = ((hash << 5) - hash) + peerId.charCodeAt(i);\n  const hue = Math.abs(hash) % 360;\n  return `hsl(${hue}deg 74% 48%)`;\n}\n\nfunction createSyntheticTransactionItem(title?: string): ITransactionItem {\n  return {\n    title: title ?? 'remote change',\n    affectedItems: [],\n    do: () => { return null; },\n    undo: () => { return null; },\n    mergeWith: () => false\n  };\n}\n\nexport class DefaultCollaborationService implements ICollaborationService {\n  private _state: CollaborationConnectionState = 'disconnected';\n  private _session: ICollaborationSession | null = null;\n  private _transport: ICollaborationTransport | null = null;\n  private _peers = new Map<string, ICollaborationPeerPresence>();\n  private _comments = new Map<string, ICollaborationComment>();\n  private _applyingRemoteChanges = false;\n  private _lastLocalCursorPosition: { x: number, y: number } | undefined;\n  private _lastLocalCursorUpdate = 0;\n  private _refreshExtensionsHandle: number | null = null;\n\n  constructor(private _designerCanvas: IDesignerCanvas) {\n    this._designerCanvas.instanceServiceContainer.undoService.onTransaction.on(change => {\n      if (this._applyingRemoteChanges)\n        return;\n\n      this.onChange.emit(change);\n      if (change.source !== 'remote')\n        void this._transport?.sendChange({ kind: change.kind, title: change.item?.title }, this.createSnapshot());\n    });\n\n    this._designerCanvas.instanceServiceContainer.selectionService.onSelectionChanged.on(() => {\n      if (!this._session || this._applyingRemoteChanges)\n        return;\n\n      const selectedElements = this._designerCanvas.instanceServiceContainer.selectionService.selectedElements;\n      const selectedNodeIndexes = getCollaborationNodeIndexes(selectedElements);\n      const primaryDesignItem = this._designerCanvas.instanceServiceContainer.selectionService.primarySelection;\n      const primaryNodeIndex = getCollaborationNodeIndex(primaryDesignItem);\n      const selectedDesignItemIds = selectedElements\n        .map(x => x?.id)\n        .filter(x => !!x);\n      const primaryDesignItemId = primaryDesignItem?.id;\n\n      this.storePeerPresence({\n        peerId: this._session.peerId,\n        displayName: this._session.displayName,\n        color: this._peers.get(this._session.peerId)?.color ?? createPeerColor(this._session.peerId),\n        activeDesignItemId: primaryDesignItemId,\n        activeNodeIndex: primaryNodeIndex,\n        selectedDesignItemIds,\n        selectedNodeIndexes,\n        updatedAt: Date.now()\n      }, 'local');\n\n      const event: ICollaborationSelectionEvent = {\n        source: 'local',\n        peerId: this._session.peerId,\n        selectedNodeIndexes,\n        primaryNodeIndex,\n        selectedDesignItemIds,\n        primaryDesignItemId\n      };\n\n      this.onSelectionChanged.emit(event);\n      void this._transport?.sendSelection(event);\n    });\n\n    this._designerCanvas.clickOverlay.addEventListener('pointermove', this._handlePointerMove);\n    this._designerCanvas.clickOverlay.addEventListener('pointerleave', this._handlePointerLeave);\n  }\n\n  get state(): CollaborationConnectionState {\n    return this._state;\n  }\n\n  get session(): ICollaborationSession {\n    return this._session!;\n  }\n\n  get peers(): readonly ICollaborationPeerPresence[] {\n    return [...this._peers.values()];\n  }\n\n  get comments(): readonly ICollaborationComment[] {\n    return [...this._comments.values()];\n  }\n\n  get transport(): ICollaborationTransport {\n    return this._transport!;\n  }\n\n  get isApplyingRemoteChanges(): boolean {\n    return this._applyingRemoteChanges;\n  }\n\n  attachTransport(transport: ICollaborationTransport): void {\n    if (this._transport === transport)\n      return;\n\n    this.detachTransport();\n    this._transport = transport;\n    void this._transport?.attach(this);\n    if (this._session)\n      void this._transport?.connect(this._session);\n  }\n\n  detachTransport(): void {\n    if (this._transport) {\n      void this._transport.disconnect();\n      void this._transport.detach();\n    }\n    this._transport = null;\n  }\n\n  connect(sessionId: string, peerId: string, displayName?: string): void {\n    const currentSelection = this.getCurrentSelectionState();\n    this._state = 'connected';\n    this._session = { sessionId, peerId, displayName: displayName ?? `peer-${peerId.substring(0, 4)}` };\n    this.onStateChanged.emit({ state: this._state, session: this._session });\n    this.updatePeerPresence({\n      peerId,\n      displayName: this._session.displayName,\n      color: createPeerColor(peerId),\n      activeDesignItemId: currentSelection.primaryDesignItemId,\n      activeNodeIndex: currentSelection.primaryNodeIndex,\n      selectedDesignItemIds: currentSelection.selectedDesignItemIds,\n      selectedNodeIndexes: currentSelection.selectedNodeIndexes,\n      updatedAt: Date.now()\n    }, 'local');\n    void this._transport?.connect(this._session);\n  }\n\n  disconnect(): void {\n    const previousSession = this._session;\n    if (previousSession?.peerId)\n      this._peers.delete(previousSession.peerId);\n\n    this._lastLocalCursorPosition = undefined;\n    void this._transport?.disconnect();\n    this._session = null;\n    this._state = 'disconnected';\n    this.onStateChanged.emit({ state: this._state, session: previousSession! });\n    this.onPeersChanged.emit({ source: 'local', peerId: previousSession?.peerId, peers: this.peers });\n  }\n\n  createSnapshot(): ICollaborationDocumentSnapshot {\n    const html = this._designerCanvas.rootDesignItem.childCount > 0\n      ? DomConverter.ConvertToString(Array.from(this._designerCanvas.rootDesignItem.children()), true, true)\n      : '';\n    const stylesheets = this._designerCanvas.instanceServiceContainer.stylesheetService?.getStylesheets()?.map(x => ({ ...x })) ?? [];\n    return { html, stylesheets, updatedAt: Date.now() };\n  }\n\n  async applyRemoteSnapshot(snapshot: ICollaborationDocumentSnapshot): Promise<void> {\n    try {\n      this._applyingRemoteChanges = true;\n\n      const designItems = snapshot.html\n        ? await this._designerCanvas.serviceContainer.htmlParserService.parse(snapshot.html, this._designerCanvas.serviceContainer, this._designerCanvas.instanceServiceContainer, false)\n        : [];\n\n      this._designerCanvas._internalSetDesignItems(designItems);\n      if (this._designerCanvas.instanceServiceContainer.stylesheetService)\n        await this._designerCanvas.instanceServiceContainer.stylesheetService.setStylesheets(snapshot.stylesheets ?? []);\n    } finally {\n      this._applyingRemoteChanges = false;\n    }\n  }\n\n  async applyRemoteChange(change: ICollaborationRemoteChange, snapshot?: ICollaborationDocumentSnapshot): Promise<void> {\n    if (snapshot)\n      await this.applyRemoteSnapshot(snapshot);\n\n    this.onChange.emit({\n      item: createSyntheticTransactionItem(change.title),\n      kind: change.kind,\n      source: 'remote'\n    });\n  }\n\n  updateRemoteSelection(peerId: string, selectedNodeIndexes: number[], primaryNodeIndex?: number): void {\n    const rootDesignItem = this._designerCanvas.instanceServiceContainer.rootDesignItem;\n    const selectedDesignItemIds = selectedNodeIndexes\n      .map(x => getDesignItemByCollaborationNodeIndex(rootDesignItem, x)?.id)\n      .filter(x => !!x);\n    const primaryDesignItemId = primaryNodeIndex != null\n      ? getDesignItemByCollaborationNodeIndex(rootDesignItem, primaryNodeIndex)?.id\n      : undefined;\n\n    this.storePeerPresence({\n      peerId,\n      activeDesignItemId: primaryDesignItemId,\n      activeNodeIndex: primaryNodeIndex,\n      selectedDesignItemIds,\n      selectedNodeIndexes,\n      updatedAt: Date.now()\n    }, 'remote');\n\n    this.onSelectionChanged.emit({\n      source: 'remote',\n      peerId,\n      selectedNodeIndexes,\n      primaryNodeIndex,\n      selectedDesignItemIds,\n      primaryDesignItemId\n    });\n  }\n\n  updatePeerPresence(peer: ICollaborationPeerPresence, source: UndoChangeSource = 'local'): void {\n    const normalizedPeer = { ...peer, color: peer.color ?? createPeerColor(peer.peerId) };\n    this.storePeerPresence(normalizedPeer, source);\n    if (source !== 'remote')\n      void this._transport?.sendPresence(normalizedPeer);\n  }\n\n  removePeer(peerId: string, source: UndoChangeSource = 'local'): void {\n    this._peers.delete(peerId);\n    this.onPeersChanged.emit({ source, peerId, peers: this.peers });\n    if (source === 'remote')\n      this.requestExtensionRefresh();\n  }\n\n  upsertComment(comment: ICollaborationComment, source: UndoChangeSource = 'local'): void {\n    const normalizedComment = {\n      ...comment,\n      updatedAt: comment.updatedAt ?? Date.now()\n    };\n    this._comments.set(normalizedComment.id, normalizedComment);\n\n    const event: ICollaborationCommentsChangedEvent = {\n      source,\n      comment: normalizedComment,\n      comments: this.comments\n    };\n\n    this.onCommentsChanged.emit(event);\n    this.requestExtensionRefresh();\n    if (source !== 'remote')\n      void this._transport?.sendComment(event);\n  }\n\n  removeComment(commentId: string, source: UndoChangeSource = 'local'): void {\n    this._comments.delete(commentId);\n\n    const event: ICollaborationCommentsChangedEvent = {\n      source,\n      commentId,\n      comments: this.comments\n    };\n\n    this.onCommentsChanged.emit(event);\n    this.requestExtensionRefresh();\n    if (source !== 'remote')\n      void this._transport?.sendComment(event);\n  }\n\n  readonly onStateChanged = new TypedEvent<ICollaborationStateChangedEvent>();\n  readonly onChange = new TypedEvent<IUndoChangeEvent>();\n  readonly onSelectionChanged = new TypedEvent<ICollaborationSelectionEvent>();\n  readonly onPeersChanged = new TypedEvent<ICollaborationPeersChangedEvent>();\n  readonly onCommentsChanged = new TypedEvent<ICollaborationCommentsChangedEvent>();\n\n  private storePeerPresence(peer: ICollaborationPeerPresence, source: UndoChangeSource) {\n    const currentPeer = this._peers.get(peer.peerId);\n    const nextPeer = { ...currentPeer, ...peer, color: peer.color ?? currentPeer?.color ?? createPeerColor(peer.peerId), updatedAt: peer.updatedAt ?? Date.now() };\n    this._peers.set(peer.peerId, nextPeer);\n    this.onPeersChanged.emit({ source, peer: nextPeer, peers: this.peers });\n    if (source === 'remote')\n      this.requestExtensionRefresh();\n  }\n\n  private requestExtensionRefresh() {\n    if (this._refreshExtensionsHandle != null)\n      return;\n\n    this._refreshExtensionsHandle = requestAnimationFrame(() => {\n      this._refreshExtensionsHandle = null;\n      this._designerCanvas.extensionManager?.refreshAllAppliedExtentions();\n    });\n  }\n\n  private _handlePointerMove = (event: PointerEvent) => {\n    if (!this._session || this._applyingRemoteChanges)\n      return;\n\n    const now = Date.now();\n    const cursorPosition = this._designerCanvas.getNormalizedEventCoordinates(event);\n    const previousPosition = this._lastLocalCursorPosition;\n    const hasMoved = !previousPosition\n      || Math.abs(previousPosition.x - cursorPosition.x) >= 1\n      || Math.abs(previousPosition.y - cursorPosition.y) >= 1;\n\n    if (!hasMoved && now - this._lastLocalCursorUpdate < 40)\n      return;\n\n    this._lastLocalCursorPosition = cursorPosition;\n    this._lastLocalCursorUpdate = now;\n    this.updatePeerPresence({\n      peerId: this._session.peerId,\n      displayName: this._session.displayName,\n      cursorPosition,\n      updatedAt: now\n    }, 'local');\n  };\n\n  private _handlePointerLeave = () => {\n    if (!this._session)\n      return;\n\n    if (!this._lastLocalCursorPosition)\n      return;\n\n    this._lastLocalCursorPosition = undefined;\n    this._lastLocalCursorUpdate = Date.now();\n    this.updatePeerPresence({\n      peerId: this._session.peerId,\n      displayName: this._session.displayName,\n      cursorPosition: undefined,\n      updatedAt: this._lastLocalCursorUpdate\n    }, 'local');\n  };\n\n  private getCurrentSelectionState() {\n    const selectedElements = this._designerCanvas.instanceServiceContainer.selectionService.selectedElements ?? [];\n    const primarySelection = this._designerCanvas.instanceServiceContainer.selectionService.primarySelection;\n    return {\n      selectedDesignItemIds: selectedElements.map(x => x?.id).filter(x => !!x),\n      selectedNodeIndexes: getCollaborationNodeIndexes(selectedElements),\n      primaryDesignItemId: primarySelection?.id,\n      primaryNodeIndex: getCollaborationNodeIndex(primarySelection)\n    };\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/services/WebRtcTabCollaborationTransport.ts",
    "content": "import { ICollaborationDocumentSnapshot, ICollaborationRemoteChange, ICollaborationComment, ICollaborationTransport, ICollaborationService, ICollaborationSession } from \"@node-projects/web-component-designer\";\n\nexport type WebRtcTabCollaborationSignalingChannelKind = 'broadcast-channel' | 'manual';\n\nexport interface WebRtcTabCollaborationTransportOptions {\n  enabledSignalingChannels?: readonly WebRtcTabCollaborationSignalingChannelKind[];\n  rtcConfiguration?: RTCConfiguration;\n}\n\nexport interface IWebRtcManualSignalingBundle {\n  version: 1;\n  sessionId: string;\n  fromPeerId: string;\n  connectionId: string;\n  createdAt: number;\n  messages: SignalingMessage[];\n}\n\nexport interface IWebRtcManualSignalingImportResult {\n  importedCount: number;\n  ignoredCount: number;\n}\n\ntype SignalingMessage = {\n  id: string;\n  createdAt: number;\n  connectionId: string;\n  sessionId: string;\n  fromPeerId: string;\n  toPeerId?: string;\n  type: 'hello' | 'hello-ack' | 'offer' | 'answer' | 'ice' | 'bye';\n  requestInitialSnapshot?: boolean;\n  description?: RTCSessionDescriptionInit;\n  candidate?: RTCIceCandidateInit;\n};\n\ntype DataMessage =\n  | { type: 'snapshot'; snapshot: ICollaborationDocumentSnapshot; }\n  | { type: 'change'; change: ICollaborationRemoteChange; snapshot: ICollaborationDocumentSnapshot; }\n  | { type: 'selection'; selection: { peerId: string; selectedNodeIndexes: number[]; primaryNodeIndex?: number; }; }\n  | { type: 'presence'; peer: { peerId: string; displayName?: string; color?: string; activeNodeIndex?: number; selectedNodeIndexes?: number[]; updatedAt: number; }; }\n  | { type: 'comment-upsert'; comment: ICollaborationComment; }\n  | { type: 'comment-remove'; commentId: string; };\n\nexport const defaultWebRtcTabCollaborationSignalingChannels: readonly WebRtcTabCollaborationSignalingChannelKind[] = ['broadcast-channel', 'manual'];\n\nfunction createRandomId() {\n  return globalThis.crypto?.randomUUID?.() ?? `wcd-collab-${Date.now()}-${Math.floor(Math.random() * 100000)}`;\n}\n\nfunction createEnabledSignalingChannels(channels?: readonly WebRtcTabCollaborationSignalingChannelKind[]) {\n  if (channels == null)\n    return new Set<WebRtcTabCollaborationSignalingChannelKind>(defaultWebRtcTabCollaborationSignalingChannels);\n\n  const validChannels = channels.filter((channel): channel is WebRtcTabCollaborationSignalingChannelKind =>\n    channel === 'broadcast-channel' || channel === 'manual');\n  return new Set<WebRtcTabCollaborationSignalingChannelKind>(validChannels);\n}\n\nfunction cloneRtcConfiguration(configuration?: RTCConfiguration) {\n  if (!configuration)\n    return undefined;\n\n  return {\n    ...configuration,\n    iceServers: configuration.iceServers?.map(server => ({\n      ...server,\n      urls: Array.isArray(server.urls) ? [...server.urls] : server.urls,\n    })),\n  } satisfies RTCConfiguration;\n}\n\nfunction areSetsEqual<T>(first: Set<T>, second: Set<T>) {\n  if (first.size !== second.size)\n    return false;\n\n  for (const entry of first) {\n    if (!second.has(entry))\n      return false;\n  }\n\n  return true;\n}\n\nfunction cloneSignalingMessage(message: SignalingMessage): SignalingMessage {\n  return {\n    ...message,\n    description: message.description ? { ...message.description } : undefined,\n    candidate: message.candidate ? { ...message.candidate } : undefined\n  };\n}\n\nfunction escapeControlCharacter(character: string) {\n  switch (character) {\n    case '\\b':\n      return '\\\\b';\n    case '\\f':\n      return '\\\\f';\n    case '\\n':\n      return '\\\\n';\n    case '\\r':\n      return '\\\\r';\n    case '\\t':\n      return '\\\\t';\n    default:\n      return `\\\\u${character.charCodeAt(0).toString(16).padStart(4, '0')}`;\n  }\n}\n\nfunction normalizeJsonControlCharactersInStrings(data: string) {\n  let normalized = '';\n  let inString = false;\n  let isEscaped = false;\n\n  for (const character of data) {\n    if (inString) {\n      if (isEscaped) {\n        normalized += character;\n        isEscaped = false;\n        continue;\n      }\n\n      if (character === '\\\\') {\n        normalized += character;\n        isEscaped = true;\n        continue;\n      }\n\n      if (character === '\"') {\n        normalized += character;\n        inString = false;\n        continue;\n      }\n\n      if (character < ' ') {\n        normalized += escapeControlCharacter(character);\n        continue;\n      }\n\n      normalized += character;\n      continue;\n    }\n\n    normalized += character;\n    if (character === '\"')\n      inString = true;\n  }\n\n  return normalized;\n}\n\nfunction parseManualSignalingBundleData(data: string) {\n  try {\n    return JSON.parse(data) as IWebRtcManualSignalingBundle;\n  } catch (error) {\n    const normalizedData = normalizeJsonControlCharactersInStrings(data);\n    if (normalizedData === data)\n      throw error;\n\n    return JSON.parse(normalizedData) as IWebRtcManualSignalingBundle;\n  }\n}\n\nexport class WebRtcTabCollaborationTransport implements ICollaborationTransport {\n  private _service: ICollaborationService | null = null;\n  private _session: ICollaborationSession | null = null;\n  private _broadcastSignalingChannel: BroadcastChannel | null = null;\n  private _enabledSignalingChannels = createEnabledSignalingChannels();\n  private _rtcConfiguration: RTCConfiguration | undefined;\n  private _manualSignalingMessages: SignalingMessage[] = [];\n  private _peerConnections = new Map<string, RTCPeerConnection>();\n  private _dataChannels = new Map<string, RTCDataChannel>();\n  private _initiatedPeers = new Set<string>();\n  private _peersNeedingInitialSnapshot = new Set<string>();\n  private _pendingCandidates = new Map<string, RTCIceCandidateInit[]>();\n  private _processedSignalIds = new Set<string>();\n  private _remoteConnectionIds = new Map<string, string>();\n  private _localConnectionId: string | null = null;\n\n  constructor(options?: WebRtcTabCollaborationTransportOptions) {\n    this._enabledSignalingChannels = createEnabledSignalingChannels(options?.enabledSignalingChannels);\n    this._rtcConfiguration = cloneRtcConfiguration(options?.rtcConfiguration);\n  }\n\n  get enabledSignalingChannels(): readonly WebRtcTabCollaborationSignalingChannelKind[] {\n    return [...this._enabledSignalingChannels];\n  }\n\n  get rtcConfiguration(): RTCConfiguration | undefined {\n    return cloneRtcConfiguration(this._rtcConfiguration);\n  }\n\n  setEnabledSignalingChannels(channels: readonly WebRtcTabCollaborationSignalingChannelKind[]) {\n    const nextChannels = createEnabledSignalingChannels(channels);\n    if (areSetsEqual(this._enabledSignalingChannels, nextChannels))\n      return;\n\n    this._enabledSignalingChannels = nextChannels;\n    this.ensureBroadcastChannelState();\n\n    if (this._session && this.hasAvailableSignalingChannel()) {\n      this.sendSignal({\n        sessionId: this._session.sessionId,\n        fromPeerId: this._session.peerId,\n        type: 'hello',\n        requestInitialSnapshot: this.shouldRequestInitialSnapshot()\n      });\n    }\n  }\n\n  setRtcConfiguration(configuration?: RTCConfiguration) {\n    this._rtcConfiguration = cloneRtcConfiguration(configuration);\n  }\n\n  getManualSignalingBundle(): IWebRtcManualSignalingBundle | null {\n    if (!this._session || !this._localConnectionId)\n      return null;\n\n    return {\n      version: 1,\n      sessionId: this._session.sessionId,\n      fromPeerId: this._session.peerId,\n      connectionId: this._localConnectionId,\n      createdAt: Date.now(),\n      messages: this._manualSignalingMessages.map(cloneSignalingMessage)\n    };\n  }\n\n  exportManualSignalingData(space: number = 2): string {\n    const bundle = this.getManualSignalingBundle();\n    return bundle ? JSON.stringify(bundle, null, space) : '';\n  }\n\n  async importManualSignalingData(data: string | IWebRtcManualSignalingBundle): Promise<IWebRtcManualSignalingImportResult> {\n    if (!this._session)\n      throw new Error('Collaboration is not connected for this document.');\n\n    const bundle = typeof data === 'string'\n      ? parseManualSignalingBundleData(data)\n      : data;\n\n    if (!bundle || !Array.isArray(bundle.messages))\n      throw new Error('Invalid collaboration signaling data.');\n\n    if (bundle.sessionId !== this._session.sessionId)\n      throw new Error(`Signaling data is for session \"${bundle.sessionId}\", but the active session is \"${this._session.sessionId}\".`);\n\n    let importedCount = 0;\n    let ignoredCount = 0;\n\n    for (const message of bundle.messages) {\n      if (!this.isSignalingMessage(message)) {\n        ignoredCount++;\n        continue;\n      }\n\n      const handled = await this.handleSignalMessage(message);\n      if (handled)\n        importedCount++;\n      else\n        ignoredCount++;\n    }\n\n    return { importedCount, ignoredCount };\n  }\n\n  async attach(service: ICollaborationService) {\n    this._service = service;\n  }\n\n  async detach() {\n    await this.disconnect();\n    this._service = null;\n  }\n\n  async connect(session: ICollaborationSession) {\n    if (typeof RTCPeerConnection === 'undefined') {\n      console.warn('WebRTC collaboration is not available in this browser.');\n      return;\n    }\n\n    await this.disconnect();\n    this._session = session;\n    this._localConnectionId = createRandomId();\n    this.ensureBroadcastChannelState();\n\n    if (!this.hasAvailableSignalingChannel())\n      console.warn('WebRTC collaboration signaling is disabled. Enable manual or broadcast signaling to connect peers.');\n\n    this.sendSignal({\n      sessionId: session.sessionId,\n      fromPeerId: session.peerId,\n      type: 'hello',\n      requestInitialSnapshot: this.shouldRequestInitialSnapshot()\n    });\n  }\n\n  async disconnect() {\n    if (this._session) {\n      this.sendSignal({\n        sessionId: this._session.sessionId,\n        fromPeerId: this._session.peerId,\n        type: 'bye'\n      }, { includeManual: false });\n    }\n\n    if (this._broadcastSignalingChannel) {\n      this._broadcastSignalingChannel.close();\n      this._broadcastSignalingChannel = null;\n    }\n\n    for (const channel of this._dataChannels.values()) {\n      try {\n        channel.close();\n      } catch {\n      }\n    }\n    this._dataChannels.clear();\n\n    for (const peerConnection of this._peerConnections.values()) {\n      try {\n        peerConnection.close();\n      } catch {\n      }\n    }\n    this._peerConnections.clear();\n    this._pendingCandidates.clear();\n    this._initiatedPeers.clear();\n    this._peersNeedingInitialSnapshot.clear();\n    this._manualSignalingMessages = [];\n    this._processedSignalIds.clear();\n    this._remoteConnectionIds.clear();\n    this._localConnectionId = null;\n    this._session = null;\n  }\n\n  async sendChange(change: ICollaborationRemoteChange, snapshot: ICollaborationDocumentSnapshot) {\n    this.sendData({ type: 'change', change, snapshot });\n  }\n\n  async sendSelection(selection: { peerId: string; selectedNodeIndexes: number[]; primaryNodeIndex?: number; }) {\n    this.sendData({ type: 'selection', selection });\n  }\n\n  async sendPresence(peer: { peerId: string; displayName?: string; color?: string; activeNodeIndex?: number; selectedNodeIndexes?: number[]; updatedAt: number; }) {\n    this.sendData({ type: 'presence', peer });\n  }\n\n  async sendComment(change: { comment?: ICollaborationComment; commentId?: string; }) {\n    if (change.comment)\n      this.sendData({ type: 'comment-upsert', comment: change.comment });\n    else if (change.commentId)\n      this.sendData({ type: 'comment-remove', commentId: change.commentId });\n  }\n\n  private ensureBroadcastChannelState() {\n    if (!this._session || !this._enabledSignalingChannels.has('broadcast-channel') || typeof BroadcastChannel === 'undefined') {\n      if (this._broadcastSignalingChannel) {\n        this._broadcastSignalingChannel.close();\n        this._broadcastSignalingChannel = null;\n      }\n      return;\n    }\n\n    if (this._broadcastSignalingChannel)\n      return;\n\n    this._broadcastSignalingChannel = new BroadcastChannel(`wcd-collab:${this._session.sessionId}`);\n    this._broadcastSignalingChannel.onmessage = event => {\n      void this.handleSignalMessage(event.data as SignalingMessage);\n    };\n  }\n\n  private hasAvailableSignalingChannel() {\n    return this._enabledSignalingChannels.has('manual')\n      || (this._enabledSignalingChannels.has('broadcast-channel') && typeof BroadcastChannel !== 'undefined');\n  }\n\n  private isSignalingMessage(message: Partial<SignalingMessage>): message is SignalingMessage {\n    if (!message)\n      return false;\n\n    return typeof message.id === 'string'\n      && typeof message.connectionId === 'string'\n      && typeof message.createdAt === 'number'\n      && typeof message.sessionId === 'string'\n      && typeof message.fromPeerId === 'string'\n      && (message.requestInitialSnapshot == null || typeof message.requestInitialSnapshot === 'boolean')\n      && (message.type === 'hello'\n        || message.type === 'hello-ack'\n        || message.type === 'offer'\n        || message.type === 'answer'\n        || message.type === 'ice'\n        || message.type === 'bye');\n  }\n\n  private rememberRemoteConnection(remotePeerId: string, connectionId: string) {\n    const previousConnectionId = this._remoteConnectionIds.get(remotePeerId);\n    if (previousConnectionId === connectionId)\n      return;\n\n    this.cleanupPeer(remotePeerId, false);\n    this._remoteConnectionIds.set(remotePeerId, connectionId);\n  }\n\n  private shouldRequestInitialSnapshot() {\n    const snapshot = this._service?.createSnapshot();\n    return !snapshot?.html?.trim();\n  }\n\n  private updateInitialSnapshotRequest(remotePeerId: string, requestInitialSnapshot?: boolean) {\n    if (requestInitialSnapshot)\n      this._peersNeedingInitialSnapshot.add(remotePeerId);\n    else\n      this._peersNeedingInitialSnapshot.delete(remotePeerId);\n  }\n\n  private async handleSignalMessage(message: SignalingMessage) {\n    if (!this._session || !message || message.sessionId !== this._session.sessionId || message.fromPeerId === this._session.peerId)\n      return false;\n\n    if (message.toPeerId && message.toPeerId !== this._session.peerId)\n      return false;\n\n    if (this._processedSignalIds.has(message.id))\n      return false;\n    this._processedSignalIds.add(message.id);\n\n    const knownRemoteConnectionId = this._remoteConnectionIds.get(message.fromPeerId);\n    const isHandshakeMessage = message.type === 'hello' || message.type === 'hello-ack';\n    if (!isHandshakeMessage) {\n      if (!knownRemoteConnectionId || knownRemoteConnectionId !== message.connectionId)\n        return false;\n    }\n\n    switch (message.type) {\n      case 'hello':\n        this.rememberRemoteConnection(message.fromPeerId, message.connectionId);\n        this.updateInitialSnapshotRequest(message.fromPeerId, message.requestInitialSnapshot);\n        this.sendSignal({\n          sessionId: this._session.sessionId,\n          fromPeerId: this._session.peerId,\n          toPeerId: message.fromPeerId,\n          type: 'hello-ack',\n          requestInitialSnapshot: this.shouldRequestInitialSnapshot()\n        });\n        if (this.shouldInitiate(message.fromPeerId))\n          await this.ensureOffer(message.fromPeerId);\n        else\n          this.getOrCreatePeerConnection(message.fromPeerId);\n        return true;\n      case 'hello-ack':\n        this.rememberRemoteConnection(message.fromPeerId, message.connectionId);\n        this.updateInitialSnapshotRequest(message.fromPeerId, message.requestInitialSnapshot);\n        if (this.shouldInitiate(message.fromPeerId))\n          await this.ensureOffer(message.fromPeerId);\n        return true;\n      case 'offer':\n        if (message.description)\n          await this.acceptOffer(message.fromPeerId, message.description);\n        return true;\n      case 'answer':\n        if (message.description)\n          await this.acceptAnswer(message.fromPeerId, message.description);\n        return true;\n      case 'ice':\n        if (message.candidate)\n          await this.acceptIceCandidate(message.fromPeerId, message.candidate);\n        return true;\n      case 'bye':\n        this.cleanupPeer(message.fromPeerId, false);\n        this._remoteConnectionIds.delete(message.fromPeerId);\n        this._service?.removePeer(message.fromPeerId, 'remote');\n        return true;\n    }\n  }\n\n  private shouldInitiate(remotePeerId: string) {\n    if (!this._session)\n      return false;\n    return this._session.peerId.localeCompare(remotePeerId) < 0;\n  }\n\n  private getOrCreatePeerConnection(remotePeerId: string) {\n    let peerConnection = this._peerConnections.get(remotePeerId);\n    if (peerConnection)\n      return peerConnection;\n\n    peerConnection = this._rtcConfiguration ? new RTCPeerConnection(this._rtcConfiguration) : new RTCPeerConnection();\n    peerConnection.onicecandidate = event => {\n      if (event.candidate && this._session) {\n        this.sendSignal({\n          sessionId: this._session.sessionId,\n          fromPeerId: this._session.peerId,\n          toPeerId: remotePeerId,\n          type: 'ice',\n          candidate: event.candidate.toJSON()\n        });\n      }\n    };\n    peerConnection.ondatachannel = event => {\n      this.registerDataChannel(remotePeerId, event.channel);\n    };\n    peerConnection.onconnectionstatechange = () => {\n      if (peerConnection.connectionState === 'closed' || peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'disconnected') {\n        this.cleanupPeer(remotePeerId, false);\n        this._service?.removePeer(remotePeerId, 'remote');\n      }\n    };\n\n    this._peerConnections.set(remotePeerId, peerConnection);\n    return peerConnection;\n  }\n\n  private async ensureOffer(remotePeerId: string) {\n    if (this._initiatedPeers.has(remotePeerId))\n      return;\n\n    const peerConnection = this.getOrCreatePeerConnection(remotePeerId);\n    this._initiatedPeers.add(remotePeerId);\n\n    if (!this._dataChannels.has(remotePeerId)) {\n      const channel = peerConnection.createDataChannel('web-component-designer-collaboration');\n      this.registerDataChannel(remotePeerId, channel);\n    }\n\n    const offer = await peerConnection.createOffer();\n    await peerConnection.setLocalDescription(offer);\n    if (!this._session)\n      return;\n    this.sendSignal({\n      sessionId: this._session.sessionId,\n      fromPeerId: this._session.peerId,\n      toPeerId: remotePeerId,\n      type: 'offer',\n      description: peerConnection.localDescription?.toJSON() ?? offer\n    });\n  }\n\n  private async acceptOffer(remotePeerId: string, description: RTCSessionDescriptionInit) {\n    const peerConnection = this.getOrCreatePeerConnection(remotePeerId);\n    await peerConnection.setRemoteDescription(description);\n    await this.flushPendingCandidates(remotePeerId);\n    const answer = await peerConnection.createAnswer();\n    await peerConnection.setLocalDescription(answer);\n    if (!this._session)\n      return;\n    this.sendSignal({\n      sessionId: this._session.sessionId,\n      fromPeerId: this._session.peerId,\n      toPeerId: remotePeerId,\n      type: 'answer',\n      description: peerConnection.localDescription?.toJSON() ?? answer\n    });\n  }\n\n  private async acceptAnswer(remotePeerId: string, description: RTCSessionDescriptionInit) {\n    const peerConnection = this.getOrCreatePeerConnection(remotePeerId);\n    await peerConnection.setRemoteDescription(description);\n    await this.flushPendingCandidates(remotePeerId);\n  }\n\n  private async acceptIceCandidate(remotePeerId: string, candidate: RTCIceCandidateInit) {\n    const peerConnection = this.getOrCreatePeerConnection(remotePeerId);\n    if (!peerConnection.remoteDescription) {\n      const pending = this._pendingCandidates.get(remotePeerId) ?? [];\n      pending.push(candidate);\n      this._pendingCandidates.set(remotePeerId, pending);\n      return;\n    }\n\n    await peerConnection.addIceCandidate(candidate);\n  }\n\n  private async flushPendingCandidates(remotePeerId: string) {\n    const peerConnection = this._peerConnections.get(remotePeerId);\n    const candidates = this._pendingCandidates.get(remotePeerId);\n    if (!peerConnection || !candidates?.length)\n      return;\n\n    for (const candidate of candidates)\n      await peerConnection.addIceCandidate(candidate);\n\n    this._pendingCandidates.delete(remotePeerId);\n  }\n\n  private registerDataChannel(remotePeerId: string, channel: RTCDataChannel) {\n    const previousChannel = this._dataChannels.get(remotePeerId);\n    if (previousChannel && previousChannel !== channel) {\n      try {\n        previousChannel.close();\n      } catch {\n      }\n    }\n\n    channel.onopen = () => {\n      void this.handleOpenChannel(remotePeerId);\n    };\n    channel.onmessage = event => {\n      void this.handleDataMessage(remotePeerId, event.data);\n    };\n    channel.onclose = () => {\n      if (this._dataChannels.get(remotePeerId) === channel)\n        this._dataChannels.delete(remotePeerId);\n    };\n\n    this._dataChannels.set(remotePeerId, channel);\n  }\n\n  private async handleOpenChannel(remotePeerId: string) {\n    if (!this._session || !this._service)\n      return;\n\n    const session = this._session;\n    const service = this._service;\n\n    this._manualSignalingMessages = this._manualSignalingMessages.filter(x => x.toPeerId !== remotePeerId);\n\n    const localPeer = service.peers.find(x => x.peerId === session.peerId);\n    if (localPeer)\n      await this.sendPresence(localPeer);\n\n    const currentSelection = localPeer?.selectedNodeIndexes?.length || localPeer?.activeNodeIndex != null\n      ? {\n          peerId: session.peerId,\n          selectedNodeIndexes: [...(localPeer.selectedNodeIndexes ?? [])],\n          primaryNodeIndex: localPeer.activeNodeIndex\n        }\n      : null;\n\n    if (currentSelection)\n      await this.sendSelection(currentSelection);\n\n    if (this._peersNeedingInitialSnapshot.has(remotePeerId)) {\n      this._peersNeedingInitialSnapshot.delete(remotePeerId);\n      this.sendData({ type: 'snapshot', snapshot: service.createSnapshot() }, remotePeerId);\n      for (const comment of service.comments)\n        this.sendData({ type: 'comment-upsert', comment }, remotePeerId);\n    }\n  }\n\n  private async handleDataMessage(remotePeerId: string, rawData: string) {\n    if (!this._service)\n      return;\n\n    const message = JSON.parse(rawData) as DataMessage;\n\n    switch (message.type) {\n      case 'snapshot':\n        await this._service.applyRemoteSnapshot(message.snapshot);\n        break;\n      case 'change':\n        await this._service.applyRemoteChange(message.change, message.snapshot);\n        break;\n      case 'selection':\n        this._service.updateRemoteSelection(remotePeerId, message.selection.selectedNodeIndexes ?? [], message.selection.primaryNodeIndex);\n        break;\n      case 'presence':\n        this._service.updatePeerPresence({ ...message.peer, peerId: remotePeerId }, 'remote');\n        break;\n      case 'comment-upsert':\n        this._service.upsertComment(message.comment, 'remote');\n        break;\n      case 'comment-remove':\n        this._service.removeComment(message.commentId, 'remote');\n        break;\n    }\n  }\n\n  private sendSignal(\n    message: Omit<SignalingMessage, 'id' | 'createdAt' | 'connectionId'>,\n    options?: { includeBroadcast?: boolean; includeManual?: boolean; }\n  ) {\n    if (!this._localConnectionId)\n      return;\n\n    const signal: SignalingMessage = {\n      ...message,\n      id: createRandomId(),\n      createdAt: Date.now(),\n      connectionId: this._localConnectionId\n    };\n\n    this._processedSignalIds.add(signal.id);\n\n    const includeBroadcast = options?.includeBroadcast ?? this._enabledSignalingChannels.has('broadcast-channel');\n    if (includeBroadcast)\n      this._broadcastSignalingChannel?.postMessage(signal);\n\n    const includeManual = options?.includeManual ?? this._enabledSignalingChannels.has('manual');\n    if (includeManual && signal.type !== 'bye')\n      this.enqueueManualSignal(signal);\n  }\n\n  private enqueueManualSignal(signal: SignalingMessage) {\n    if (signal.type === 'hello')\n      this._manualSignalingMessages = this._manualSignalingMessages.filter(x => x.type !== 'hello');\n    else if (signal.type === 'hello-ack' || signal.type === 'offer' || signal.type === 'answer')\n      this._manualSignalingMessages = this._manualSignalingMessages.filter(x => !(x.type === signal.type && x.toPeerId === signal.toPeerId));\n\n    this._manualSignalingMessages.push(cloneSignalingMessage(signal));\n  }\n\n  private sendData(message: DataMessage, remotePeerId?: string) {\n    const payload = JSON.stringify(message);\n    for (const [peerId, channel] of this._dataChannels) {\n      if (remotePeerId && peerId !== remotePeerId)\n        continue;\n      if (channel.readyState === 'open')\n        channel.send(payload);\n    }\n  }\n\n  private cleanupPeer(remotePeerId: string, clearInitialSnapshotState: boolean = true) {\n    const dataChannel = this._dataChannels.get(remotePeerId);\n    if (dataChannel) {\n      try {\n        dataChannel.close();\n      } catch {\n      }\n      this._dataChannels.delete(remotePeerId);\n    }\n\n    const peerConnection = this._peerConnections.get(remotePeerId);\n    if (peerConnection) {\n      try {\n        peerConnection.close();\n      } catch {\n      }\n      this._peerConnections.delete(remotePeerId);\n    }\n\n    this._initiatedPeers.delete(remotePeerId);\n    if (clearInitialSnapshotState)\n      this._peersNeedingInitialSnapshot.delete(remotePeerId);\n    this._pendingCandidates.delete(remotePeerId);\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/src/setupCollaborationService.ts",
    "content": "import { ExtensionType, IDesignerCanvas, SeperatorContextMenu, ServiceContainer } from \"@node-projects/web-component-designer\";\nimport { CollaborationCommentsContextMenu } from \"./extensions/CollaborationCommentsContextMenu.js\";\nimport { CollaborationOverlayExtensionProvider } from \"./extensions/CollaborationOverlayExtensionProvider.js\";\nimport { CollaborationCursorOverlayExtensionProvider } from \"./extensions/CollaborationCursorOverlayExtensionProvider.js\";\nimport { DefaultCollaborationService } from \"./services/DefaultCollaborationService.js\";\n\nexport function setupCollaborationService(serviceContainer: ServiceContainer) {\n    serviceContainer.register(\"collaborationService\", (designerCanvas: IDesignerCanvas) => new DefaultCollaborationService(designerCanvas));\n\n    serviceContainer.designerExtensions.set(ExtensionType.Permanent, [\n        ...serviceContainer.designerExtensions.get(ExtensionType.Permanent) ?? [],\n        new CollaborationOverlayExtensionProvider(),\n        new CollaborationCursorOverlayExtensionProvider(),\n    ]);\n\n    serviceContainer.designerContextMenuExtensions = [\n        ...serviceContainer.designerContextMenuExtensions,\n        new SeperatorContextMenu(),\n        new CollaborationCommentsContextMenu(),\n    ];\n}"
  },
  {
    "path": "packages/web-component-designer-collaboration-service/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false,\r\n    \"moduleResolution\": \"bundler\"\r\n  },\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/README.md",
    "content": "# web-component-designer-htmlparserservice-base-custom-webcomponent\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-htmlparserservice-base-custom-webcomponent\r\n\r\n     npm i @node-projects/web-component-designer-htmlparserservice-base-custom-webcomponent\r\n\r\n## Description\r\n\r\nThis is a HTML Parser Service for Templates inside of BaseCustomWebcomponent, it needs a HTML Parser Service as Parameter.\r\n\r\n## Usage\r\n\r\n    import { NodeHtmlParserService } from '@node-projects/web-component-designer-htmlparserservice-nodehtmlparser';\r\n    import { BaseCustomWebcomponentParserService } from '@node-projects/web-component-designer-htmlparserservice-base-custom-webcomponent';\r\n    serviceContainer.register(\"htmlParserService\", new BaseCustomWebcomponentParserService(new NodeHtmlParserService()));"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Parser Service for Templates inside of BaseCustomWebcomponent\",\n  \"name\": \"@node-projects/web-component-designer-htmlparserservice-base-custom-webcomponent\",\n  \"version\": \"0.1.5\",\n  \"type\": \"module\",\n  \"main\": \"./dist/service/htmlParserService/BaseCustomWebcomponentParserService.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@node-projects/web-component-designer\": \">=0.1.224\",\n    \"typescript\": \">=5.6.3\"   \n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/src/service/htmlParserService/BaseCustomWebcomponentParserService.ts",
    "content": "import { IHtmlParserService, ServiceContainer, InstanceServiceContainer, IDesignItem } from '@node-projects/web-component-designer';\r\nimport type { SourceFile } from 'typescript'\r\n\r\nfunction* findAllNodesOfKind(node: ts.Node, kind: ts.SyntaxKind) {\r\n  if (node.kind == kind)\r\n    yield node;\r\n  for (const c of node.getChildren())\r\n    yield* findAllNodesOfKind(c, kind);\r\n}\r\n\r\nexport class BaseCustomWebcomponentParserService implements IHtmlParserService {\r\n  private htmlParser: IHtmlParserService;\r\n\r\n  constructor(htmlParser: IHtmlParserService) {\r\n    this.htmlParser = htmlParser;\r\n  }\r\n\r\n  async parse(code: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\r\n    const sourceFile = this.parseTypescriptFile(code);\r\n\r\n    let htmlCode = \"\";\r\n    let cssStyle = \"\";\r\n    let positionOffset = 0;\r\n    //let cssOffset = 0;\r\n    const nodes = findAllNodesOfKind(sourceFile, ts.SyntaxKind.TaggedTemplateExpression);\r\n    for (let nd of nodes) {\r\n      if (nd.tag.escapedText == 'html' && nd.parent.name.escapedText == \"template\") {\r\n        positionOffset = nd.pos;\r\n        htmlCode = nd.template.rawText;\r\n      }\r\n      if (nd.tag.escapedText == 'css' && nd.parent.name.escapedText == \"style\") {\r\n        //cssOffset = nd.pos;\r\n        cssStyle = nd.template.rawText;\r\n      }\r\n    }\r\n\r\n    if (cssStyle)\r\n      instanceServiceContainer.stylesheetService.setStylesheets([{ name: 'css', content: cssStyle }]);\r\n\r\n    return this.htmlParser.parse(htmlCode, serviceContainer, instanceServiceContainer, parseSnippet, positionOffset);\r\n  }\r\n\r\n  public writeBack(code: string, html: string, css: string, newLineCrLf: boolean): string {\r\n    const sourceFile = this.parseTypescriptFile(code);\r\n\r\n\r\n    const transformTemplateLiterals = <T extends ts.Node>(context: ts.TransformationContext) =>\r\n      (rootNode: T) => {\r\n        function visit(node: ts.Node): ts.Node {\r\n\r\n          if (ts.isTemplateLiteral(node) &&\r\n            ts.isTaggedTemplateExpression(node.parent) &&\r\n            (<any>node.parent.tag).escapedText == 'html' &&\r\n            (<any>node.parent.parent).name.escapedText == \"template\") {\r\n            return <ts.Node>ts.factory.createNoSubstitutionTemplateLiteral(html.replaceAll('\\n', '\\r\\n'), html.replaceAll('\\n', '\\r\\n'));\r\n          } else if (css &&\r\n            ts.isTemplateLiteral(node) &&\r\n            ts.isTaggedTemplateExpression(node.parent) &&\r\n            (<any>node.parent.tag).escapedText == 'css' &&\r\n            (<any>node.parent.parent).name.escapedText == \"style\") {\r\n            return <ts.Node>ts.factory.createNoSubstitutionTemplateLiteral(css.replaceAll('\\n', '\\r\\n'), css.replaceAll('\\n', '\\r\\n'));\r\n          }\r\n          return ts.visitEachChild(node, visit, context);\r\n        }\r\n        return ts.visitNode(rootNode, visit);\r\n      };\r\n    let transformed = ts.transform(sourceFile, [transformTemplateLiterals]).transformed[0];\r\n    const printer = ts.createPrinter({ newLine: newLineCrLf ? ts.NewLineKind.CarriageReturnLineFeed : ts.NewLineKind.LineFeed });\r\n    const result = printer.printNode(ts.EmitHint.Unspecified, transformed, <SourceFile>transformed);\r\n\r\n    return result;\r\n  }\r\n\r\n  private parseTypescriptFile(code: string) {\r\n    const compilerHost: ts.CompilerHost = {\r\n      fileExists: () => true,\r\n      getCanonicalFileName: filename => filename,\r\n      getCurrentDirectory: () => '',\r\n      getDefaultLibFileName: () => 'lib.d.ts',\r\n      getNewLine: () => '\\n',\r\n      getSourceFile: filename => {\r\n        return ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true);\r\n      },\r\n      readFile: () => null,\r\n      useCaseSensitiveFileNames: () => true,\r\n      writeFile: () => null,\r\n    };\r\n\r\n    const filename = 'aa.ts';\r\n    const program = ts.createProgram([filename], {\r\n      noResolve: true,\r\n      target: ts.ScriptTarget.Latest,\r\n    }, compilerHost);\r\n\r\n    const sourceFile = program.getSourceFile(filename);\r\n    return sourceFile;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/src/service/htmlParserService/Typescript.d.ts",
    "content": "//import typescript = require(\"typescript\");\r\n//export = typescript;\r\n//export as namespace ts;\r\n\r\nimport ts = require('typescript');\r\n\r\ndeclare global {\r\n    namespace globalThis {\r\n        export { ts }\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-base-custom-webcomponent/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-lit-element/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-lit-element/README.md",
    "content": "# web-component-designer-htmlparserservice-lit-element\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-htmlparserservice-lit-element\r\n\r\n     npm i @node-projects/web-component-designer-htmlparserservice-lit-element\r\n\r\n## Description\r\n\r\nThis is a HTML Parser Service for Templates inside of LitElement, it needs a HTML Parser Service as Parameter.\r\nAnd it only can parse very simple Templates at the moment.\r\nIt also needed to be switched to use Typescript instead of esprima.\r\n\r\n## Usage\r\n\r\n    import { NodeHtmlParserService } from '@node-projects/web-component-designer-htmlparserservice-nodehtmlparser';\r\n    import { LitElementParserService } from '@node-projects/web-component-designer-htmlparserservice-lit-element';\r\n    serviceContainer.register(\"htmlParserService\", new LitElementParserService(new NodeHtmlParserService()));"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-lit-element/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Parser Service for Templates inside of BaseCustomWebcomponent\",\n  \"name\": \"@node-projects/web-component-designer-htmlparserservice-lit-element\",\n  \"version\": \"0.1.4\",\n  \"type\": \"module\",\n  \"main\": \"./dist/service/htmlParserService/LitElementParserService.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"esprima-next\": \"^6.0.3\",\n    \"@node-projects/web-component-designer\": \">=0.1.224\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-lit-element/src/service/htmlParserService/LitElementParserService.ts",
    "content": "import { CssAttributeParser, DesignItem, IDesignItem, IHtmlParserService, InstanceServiceContainer, ServiceContainer, newElementFromString } from \"@node-projects/web-component-designer\";\r\nimport { BlockStatement, ClassDeclaration, FunctionExpression, Identifier, MethodDefinition, ReturnStatement, TaggedTemplateExpression } from \"esprima-next\";\r\nimport * as esprima from \"esprima-next\";\r\n\r\nexport class LitElementParserService implements IHtmlParserService {\r\n  //TODO: switch to typescript\r\n  private htmlParser: IHtmlParserService;\r\n\r\n  constructor(htmlParser: IHtmlParserService) {\r\n    this.htmlParser = htmlParser;\r\n  }\r\n\r\n  async parse(module: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\r\n    const parsedModule = esprima.parseModule(module);\r\n    const classDecl: ClassDeclaration = <any>parsedModule.body.find(x => x.type == esprima.Syntax.ClassDeclaration);\r\n    const renderMethod: MethodDefinition = <any>classDecl.body.body.find(x => x.type == esprima.Syntax.MethodDefinition && (<Identifier>x.key).name == 'render');\r\n    const renderMethodStatement = <ReturnStatement>(<BlockStatement>(<FunctionExpression>renderMethod.value).body).body[0];\r\n    const taggedTemplate = <TaggedTemplateExpression>renderMethodStatement.argument;\r\n    const templateLiteral = taggedTemplate.quasi\r\n\r\n    const html = templateLiteral.quasis.map(x => x.value.raw).join();\r\n\r\n    return this.htmlParser.parse(html, serviceContainer, instanceServiceContainer, parseSnippet);\r\n  }\r\n\r\n  private _parseDiv = document.createElement(\"div\");\r\n\r\n  _createDesignItemsRecursive(item: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, namespace: string): IDesignItem {\r\n    let designItem: IDesignItem = null;\r\n    if (item.nodeType == 1) {\r\n      let element: Element;\r\n      let manualCreatedElement = false;\r\n      if (!namespace)\r\n        element = newElementFromString('<' + item.rawTagName + ' ' + item.rawAttrs + '></' + item.rawTagName + '>', instanceServiceContainer.designerCanvas.rootDesignItem.document); // some custom elements only parse attributes during constructor call \r\n      if (!element) {\r\n        if (namespace)\r\n          element = document.createElementNS(namespace, item.rawTagName);\r\n        else\r\n          element = document.createElement(item.rawTagName);\r\n        manualCreatedElement = true;\r\n      }\r\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\r\n\r\n      let style = '';\r\n\r\n      let attr = item.attributes;\r\n      for (let a in attr) {\r\n        if (a !== 'style') {\r\n          designItem._withoutUndoSetAttribute(a, attr[a])\r\n          if (manualCreatedElement) {\r\n            element.setAttribute(a, attr[a]);\r\n          }\r\n        } else {\r\n          style = attr[a];\r\n        }\r\n      }\r\n\r\n      if ((element instanceof HTMLElement || element instanceof SVGElement) && style) {\r\n        let styleParser = new CssAttributeParser();\n        styleParser.parse(style);\n        for (let s of styleParser.entries) {\n          designItem._withoutUndoSetStyle(s.name, s.value, s.important);\n          if (manualCreatedElement) {\n            element.style.setProperty(s.name, s.value, s.important ? 'important' : '');\n          }\n        }\n      }\n\r\n      (<HTMLElement>element).draggable = false; //even if it should be true, for better designer exp.\r\n\r\n      for (let c of item.childNodes) {\r\n        let di = this._createDesignItemsRecursive(c, serviceContainer, instanceServiceContainer, element instanceof SVGElement ? 'http://www.w3.org/2000/svg' : null);\r\n        designItem._insertChildInternal(di);\r\n      }\r\n    } else if (item.nodeType == 3) {\r\n      this._parseDiv.innerHTML = item.rawText;\r\n      let element = this._parseDiv.childNodes[0];\r\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\r\n    } else if (item.nodeType == 8) {\r\n      let element = document.createComment(item.rawText);\r\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\r\n    }\r\n    return designItem;\r\n  }\r\n}\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-lit-element/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-nodehtmlparser/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-nodehtmlparser/README.md",
    "content": "# web-component-designer-htmlparserservice-nodehtmlparser\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-htmlparserservice-nodehtmlparser\r\n\r\n     npm i @node-projects/web-component-designer-htmlparserservice-nodehtmlparser\r\n\r\n## Description\r\n\r\nThis is a HTML Parser Service using a external Lib. The browser Parser strips some Attributes from Templates, so often it's better to use this parser.\r\n\r\n## Usage\r\n\r\n    import { NodeHtmlParserService } from '@node-projects/web-component-designer-htmlparserservice-nodehtmlparser';\r\n    serviceContainer.register(\"htmlParserService\", new NodeHtmlParserService());"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-nodehtmlparser/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Parser Service using the Node HTML Parser\",\n  \"name\": \"@node-projects/web-component-designer-htmlparserservice-nodehtmlparser\",\n  \"version\": \"0.1.14\",\n  \"type\": \"module\",\n  \"main\": \"./dist/service/htmlParserService/NodeHtmlParserService.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@node-projects/node-html-parser-esm\": \"^6.2.0\",\n    \"@node-projects/web-component-designer\": \">=0.1.278\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-nodehtmlparser/src/service/htmlParserService/NodeHtmlParserService.ts",
    "content": "import { IHtmlParserService, ServiceContainer, InstanceServiceContainer, IDesignItem, newElementFromString, DesignItem, CssAttributeParser } from \"@node-projects/web-component-designer\";\r\nimport * as parser from \"@node-projects/node-html-parser-esm\";\r\n// Alternative Parser, cause when you use the Browser, it instanciates the CusomElements, \r\n// and some Elements remove attributes from their DOM, so you loose Data\r\nexport class NodeHtmlParserService implements IHtmlParserService {\r\n\r\n  _designItemCreatedCallback?: (IDesignItem) => void;\r\n\r\n  constructor(designItemCreatedCallback?: (IDesignItem) => void) {\r\n    this._designItemCreatedCallback = designItemCreatedCallback;\r\n  }\r\n\r\n  async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean, positionOffset = 0): Promise<IDesignItem[]> {\r\n    if (!parseSnippet)\r\n      instanceServiceContainer.designItemDocumentPositionService?.clearSourceParts();\r\n\r\n    const parsed = parser.parse(html, { comment: true });\r\n\r\n    let designItems: IDesignItem[] = [];\r\n    for (let p of parsed.childNodes) {\r\n      let di = this._createDesignItemsRecursive(p, serviceContainer, instanceServiceContainer, null, parseSnippet, positionOffset);\r\n\r\n      if (di != null)\r\n        designItems.push(di)\r\n      else\r\n        console.warn(\"NodeHtmlParserService - could not parse element\", p)\r\n    }\r\n    return designItems;\r\n  }\r\n\r\n  _createDesignItemsRecursive(item: any, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, namespace: string, snippet: boolean, positionOffset = 0): IDesignItem {\r\n    let designItem: IDesignItem = null;\r\n    if (item.nodeType == 1) {\r\n      let element: Element;\r\n      let manualCreatedElement = false;\r\n      if (!namespace)\r\n        element = newElementFromString('<' + item.rawTagName + ' ' + item.rawAttrs + '></' + item.rawTagName + '>', instanceServiceContainer.designerCanvas.rootDesignItem.document); // some custom elements only parse attributes during constructor call \r\n      if (!element) {\n        if (namespace)\n          element = instanceServiceContainer.designerCanvas.rootDesignItem.document.createElementNS(namespace, item.rawTagName);\n        else\n          element = instanceServiceContainer.designerCanvas.rootDesignItem.document.createElement(item.rawTagName);\n        manualCreatedElement = true;\n      }\n      element = <Element>DesignItem.updateRenderedNode(serviceContainer, element);\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\n      if (this._designItemCreatedCallback)\r\n        this._designItemCreatedCallback(designItem);\r\n      if (!snippet && instanceServiceContainer.designItemDocumentPositionService)\r\n        instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });\r\n      this._addAttributeSourceParts(item, designItem, serviceContainer, snippet, positionOffset);\r\n\r\n      let style = '';\r\n\r\n      let attr = item.attributes;\r\n      for (let a in attr) {\r\n        if (a !== 'style') {\r\n          designItem._withoutUndoSetAttribute(a, attr[a])\r\n          if (manualCreatedElement) {\r\n            element.setAttribute(a, attr[a]);\r\n          }\r\n        } else {\r\n          style = attr[a];\r\n        }\r\n      }\r\n\r\n      if ((element instanceof HTMLElement || element instanceof SVGElement) && style) {\r\n        let styleParser = new CssAttributeParser();\n        styleParser.parse(style);\n        for (let s of styleParser.entries) {\n          designItem._withoutUndoSetStyle(s.name, s.value, s.important);\n          if (manualCreatedElement) {\n            element.style.setProperty(s.name, s.value, s.important ? 'important' : '');\n          }\n        }\n      }\n\r\n      if (!designItem.lockAtDesignTime && (<HTMLElement>element).style)\r\n        (<HTMLElement>element).style.pointerEvents = 'auto';\r\n      (<HTMLElement>element).draggable = false; //even if it should be true, for better designer exp.\r\n\r\n      for (let c of item.childNodes) {\r\n        let ns = namespace;\r\n        if (element instanceof SVGSVGElement)\r\n          ns = 'http://www.w3.org/2000/svg';\r\n        else if (element instanceof SVGForeignObjectElement)\r\n          ns = null;\r\n        else if (element instanceof MathMLElement)\r\n          ns = 'http://www.w3.org/1998/Math/MathML';\r\n        let di = this._createDesignItemsRecursive(c, serviceContainer, instanceServiceContainer, ns, snippet, positionOffset);\n        designItem._insertChildInternal(di);\n\n        if (di.node instanceof HTMLTemplateElement && di.getAttribute('shadowrootmode') == 'open') {\n          try {\n            const shadow = (<HTMLElement>designItem.node).attachShadow({ mode: 'open' });\n            const content = di.node.content.cloneNode(true);\n            shadow.appendChild(content);\n          } catch (err) {\n            console.error(\"error attaching shadowdom\", err)\n          }\n        }\r\n      }\r\n    } else if (item.nodeType == 3) {\r\n      const parseDiv = instanceServiceContainer.designerCanvas.rootDesignItem.document.createElement(\"div\");\n      parseDiv.innerHTML = item.rawText;\n      let element = DesignItem.updateRenderedNode(serviceContainer, parseDiv.childNodes[0]);\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\n      if (!snippet && instanceServiceContainer.designItemDocumentPositionService)\r\n        instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });\r\n    } else if (item.nodeType == 8) {\r\n      let element = DesignItem.updateRenderedNode(serviceContainer, document.createComment(item.rawText));\n      designItem = DesignItem.GetOrCreateDesignItem(element, item, serviceContainer, instanceServiceContainer);\n      if (!snippet && instanceServiceContainer.designItemDocumentPositionService)\r\n        instanceServiceContainer.designItemDocumentPositionService.setPosition(designItem, { start: item.range[0] + positionOffset, length: item.range[1] - item.range[0] });\r\n    }\r\n    if (designItem) {\n      const renderedNode = DesignItem.updateRenderedNode(serviceContainer, designItem.node);\n      if (renderedNode !== designItem.node)\n        designItem.replaceNode(renderedNode);\n      designItem.refreshRenderedDesignItem();\n    }\n    if (serviceContainer.designItemService.finishedDesignItem)\n      serviceContainer.designItemService.finishedDesignItem(designItem);\n    return designItem;\r\n  }\r\n\r\n  private _addAttributeSourceParts(item: any, designItem: IDesignItem, serviceContainer: ServiceContainer, snippet: boolean, positionOffset: number) {\r\n    const positionService = designItem.instanceServiceContainer.designItemDocumentPositionService;\r\n    if (snippet || !positionService || !item.rawAttrs)\r\n      return;\r\n\r\n    const rawAttrs = item.rawAttrs as string;\r\n    const openingTag = this._getOpeningTagText(item.outerHTML ?? '');\r\n    const rawAttrsIndex = openingTag.indexOf(rawAttrs);\r\n    if (rawAttrsIndex < 0)\r\n      return;\r\n\r\n    const rawAttrsStart = item.range[0] + positionOffset + rawAttrsIndex;\r\n    const attributeRegex = /([^\\s\"'<>/=]+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g;\r\n    let match: RegExpExecArray;\r\n    while (match = attributeRegex.exec(rawAttrs)) {\r\n      const attributeName = match[1];\r\n      const attributeStart = rawAttrsStart + match.index;\r\n      positionService.addSourcePart({\r\n        designItem,\r\n        kind: 'attribute',\r\n        key: `attribute:${attributeName}`,\r\n        name: attributeName,\r\n        textRange: { start: attributeStart, length: match[0].length }\r\n      });\r\n\r\n      const valueRange = this._getAttributeValueRange(match[0]);\r\n      if (valueRange) {\r\n        const textRange = { start: attributeStart + valueRange.start, length: valueRange.length };\r\n        positionService.addSourcePart({\r\n          designItem,\r\n          kind: 'attribute-value',\r\n          key: `attribute:${attributeName}/value`,\r\n          name: attributeName,\r\n          textRange\r\n        });\r\n\r\n        const context = { designItem, sourceKind: 'attribute' as const, name: attributeName, value: valueRange.value, valueTextRange: textRange };\r\n        for (const provider of serviceContainer.sourceMapProviders) {\r\n          if (provider.canMap(context))\r\n            positionService.addSourceParts(provider.map(context));\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  private _getOpeningTagText(outerHtml: string) {\r\n    let quote: '\"' | '\\'' = null;\r\n    for (let i = 0; i < outerHtml.length; i++) {\r\n      const char = outerHtml[i];\r\n      if (quote) {\r\n        if (char === quote)\r\n          quote = null;\r\n      } else if (char === '\"' || char === '\\'') {\r\n        quote = char;\r\n      } else if (char === '>') {\r\n        return outerHtml.substring(0, i + 1);\r\n      }\r\n    }\r\n    return outerHtml;\r\n  }\r\n\r\n  private _getAttributeValueRange(attributeText: string): { start: number, length: number, value: string } {\r\n    const equalsIndex = attributeText.indexOf('=');\r\n    if (equalsIndex < 0)\r\n      return null;\r\n\r\n    let valueStart = equalsIndex + 1;\r\n    while (valueStart < attributeText.length && /\\s/.test(attributeText[valueStart]))\r\n      valueStart++;\r\n\r\n    const quote = attributeText[valueStart];\r\n    if (quote === '\"' || quote === '\\'') {\r\n      const contentStart = valueStart + 1;\r\n      const contentEnd = attributeText.lastIndexOf(quote);\r\n      const length = Math.max(0, contentEnd - contentStart);\r\n      return { start: contentStart, length, value: attributeText.substring(contentStart, contentStart + length) };\r\n    }\r\n\r\n    return { start: valueStart, length: attributeText.length - valueStart, value: attributeText.substring(valueStart) };\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-htmlparserservice-nodehtmlparser/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/README.md",
    "content": "# web-component-designer-mermaid\n\nThis package contains widgets and services which help to create a Mermaid designer.\n\n## Installation\n\n```bash\nnpm i @node-projects/web-component-designer-mermaid\n```\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Widgets and Services for creating a Mermaid Designer\",\n  \"name\": \"@node-projects/web-component-designer-mermaid\",\n  \"version\": \"0.1.1\",\n  \"type\": \"module\",\n  \"main\": \"./dist/index.js\",\n  \"types\": \"./dist/index.d.ts\",\n  \"files\": [\n    \"dist\",\n    \"README.md\"\n  ],\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\n    \"@node-projects/web-component-designer\": \">=0.2.0\",\n    \"mermaid\": \"^11.12.0\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/index.ts",
    "content": "export * from \"./widgets/mermaid-node.js\";\nexport * from \"./widgets/mermaid-edge.js\";\nexport * from \"./widgets/mermaid-sequence-message.js\";\nexport * from \"./widgets/mermaid-sequence-participant.js\";\nexport * from \"./widgets/mermaid-mindmap-node.js\";\nexport * from \"./widgets/mermaid-requirement-node.js\";\nexport * from \"./widgets/mermaid-requirement-relationship.js\";\nexport * from \"./widgets/mermaid-subgraph.js\";\nexport * from \"./widgets/mermaid-flowchart-directive.js\";\n\nexport * from \"./monaco/MermaidLanguage.js\";\n\nexport * from \"./services/MermaidLayoutCopyPasteService.js\";\nexport * from \"./services/MermaidLayoutPlacementService.js\";\nexport * from \"./services/MermaidParserService.js\";\nexport * from \"./services/MermaidConnectionRouting.js\";\nexport * from \"./services/MermaidElementsService.js\";\nexport * from \"./toolbar/ConnectMermaidNodesTool.js\";\n\nexport * from \"./setupMermaidServiceContainer.js\";\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/monaco/MermaidLanguage.ts",
    "content": "export function addMermaidLanguageToMonaco(monaco?: any) {\n    if (!monaco)\n        //@ts-ignore\n        monaco = window.monaco;\n\n    monaco.languages.register({ id: \"mermaidLanguage\" });\n\n    monaco.languages.setMonarchTokensProvider(\"mermaidLanguage\", {\n        tokenizer: {\n            root: [\n                [/%%.*$/, \"comment\"],\n                [/\\b(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram-v2|erDiagram|journey|gantt|pie|mindmap|requirementDiagram|requirement|functionalRequirement|interfaceRequirement|performanceRequirement|physicalRequirement|designConstraint|element)\\b/, \"keyword\"],\n                [/[a-zA-Z][\\w-]*(?=\\s*(\\[|\\(|--|==|-\\.))/, \"identifier\"],\n                [/(-->|---|-.->|-.-|==>|===)/, \"operator\"],\n                [/\\|[^|]*\\|/, \"string\"],\n            ],\n        },\n    });\n\n    monaco.editor.defineTheme(\"mermaidTheme\", {\n        base: \"vs\",\n        inherit: false,\n        rules: [\n            { token: \"comment\", foreground: \"008000\" },\n            { token: \"keyword\", foreground: \"0000FF\", fontStyle: \"bold\" },\n            { token: \"identifier\", foreground: \"795E26\" },\n            { token: \"operator\", foreground: \"AF00DB\" },\n            { token: \"string\", foreground: \"A31515\" },\n        ],\n        colors: {\n            \"editor.foreground\": \"#000000\",\n        },\n    });\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidConnectionRouting.ts",
    "content": "import type { IDesignItem, InstanceServiceContainer } from \"@node-projects/web-component-designer\";\nimport { encodeWaypoints, FlowchartDirection, routeBetweenBounds } from \"./mermaidGeometry.js\";\n\nfunction isEdgeItem(designItem: IDesignItem) {\n    return designItem.element.localName === \"mermaid-edge\" || designItem.element.localName === \"mermaid-requirement-relationship\";\n}\n\nfunction isNodeItem(designItem: IDesignItem) {\n    return designItem.element.localName === \"mermaid-node\" || designItem.element.localName === \"mermaid-requirement-node\";\n}\n\nfunction getNodeId(designItem: IDesignItem) {\n    if (designItem.element.localName === \"mermaid-requirement-node\")\n        return designItem.element.getAttribute(\"label\");\n    return designItem.element.getAttribute(\"node-id\");\n}\n\nfunction collectCanvasDesignItems(rootDesignItem: IDesignItem) {\n    const allItems: IDesignItem[] = [];\n    const stack = Array.from(rootDesignItem.children());\n\n    while (stack.length) {\n        const item = stack.pop()!;\n        allItems.push(item);\n        stack.push(...item.children());\n    }\n\n    return allItems;\n}\n\nfunction createNodeItemsById(rootItems: IDesignItem[]) {\n    return new Map(\n        rootItems\n            .filter(isNodeItem)\n            .map(item => [getNodeId(item), item] as const)\n            .filter((entry): entry is [string, IDesignItem] => !!entry[0])\n    );\n}\n\nexport function rerouteConnectedMermaidEdges(instanceServiceContainer: InstanceServiceContainer, changedItems: IDesignItem[], operationFinished: boolean) {\n    const changedNodeIds = new Set(\n        changedItems\n            .filter(isNodeItem)\n            .map(getNodeId)\n            .filter((id): id is string => !!id)\n    );\n\n    if (!changedNodeIds.size)\n        return;\n\n    const designerCanvas = instanceServiceContainer.designerCanvas;\n    const rootItems = collectCanvasDesignItems(designerCanvas.rootDesignItem);\n    const nodeItemsById = createNodeItemsById(rootItems);\n    const affectedEdges = rootItems.filter(item => {\n        if (!isEdgeItem(item))\n            return false;\n\n        const from = item.element.getAttribute(\"from\");\n        const to = item.element.getAttribute(\"to\");\n        return !!from && !!to && (changedNodeIds.has(from) || changedNodeIds.has(to));\n    });\n\n    for (const edgeItem of affectedEdges) {\n        const sourceItem = nodeItemsById.get(edgeItem.element.getAttribute(\"from\"));\n        const targetItem = nodeItemsById.get(edgeItem.element.getAttribute(\"to\"));\n        if (!sourceItem || !targetItem)\n            continue;\n\n        const waypoints = encodeWaypoints(routeBetweenBounds(\n            designerCanvas.getNormalizedElementCoordinates(sourceItem.element),\n            designerCanvas.getNormalizedElementCoordinates(targetItem.element),\n            getDiagramDirection(edgeItem)\n        ));\n        const edgeElement = edgeItem.element as HTMLElement & { setPreviewWaypoints?: (waypoints: string | null) => void };\n\n        if (operationFinished) {\n            edgeElement.setPreviewWaypoints?.(waypoints);\n            if (edgeItem.getAttribute(\"waypoints\") !== waypoints)\n                edgeItem.setAttribute(\"waypoints\", waypoints);\n            queueMicrotask(() => edgeElement.setPreviewWaypoints?.(null));\n        } else {\n            edgeElement.setPreviewWaypoints?.(waypoints);\n        }\n    }\n}\n\nfunction getDiagramDirection(designItem: IDesignItem): FlowchartDirection {\n    const direction = designItem.element.getAttribute(\"diagram-direction\");\n    if (direction === \"TB\" || direction === \"TD\" || direction === \"BT\" || direction === \"RL\" || direction === \"LR\")\n        return direction;\n    return undefined;\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidDocumentPropertiesService.ts",
    "content": "import { AbstractPropertiesService, BindingTarget, IDesignItem, IProperty, PropertyType, RefreshMode, ValueType } from \"@node-projects/web-component-designer\";\nimport { rerouteConnectedMermaidEdges } from \"./MermaidConnectionRouting.js\";\nimport { FlowchartDirection } from \"./mermaidGeometry.js\";\n\nexport const mermaidDiagramTypeAttribute = \"data-mermaid-diagram-type\";\nexport const mermaidFlowchartDirectionAttribute = \"data-mermaid-flowchart-direction\";\nexport const mermaidTitleAttribute = \"data-mermaid-title\";\nexport const mermaidFrontmatterAttribute = \"data-mermaid-frontmatter\";\n\nexport type MermaidDocumentDiagramType = \"flowchart\" | \"sequenceDiagram\" | \"mindmap\" | \"requirementDiagram\";\n\nexport class MermaidDocumentPropertiesService extends AbstractPropertiesService {\n    private readonly _diagramTypeProperty: IProperty = {\n        name: \"diagramType\",\n        displayName: \"diagram type\",\n        type: \"list\",\n        values: [\"flowchart\", \"sequenceDiagram\", \"mindmap\", \"requirementDiagram\"],\n        attributeName: mermaidDiagramTypeAttribute,\n        service: this,\n        propertyType: PropertyType.attribute,\n    };\n\n    private readonly _flowchartDirectionProperty: IProperty = {\n        name: \"flowchartDirection\",\n        displayName: \"flowchart direction\",\n        type: \"list\",\n        values: [\"TD\", \"TB\", \"BT\", \"LR\", \"RL\"],\n        attributeName: mermaidFlowchartDirectionAttribute,\n        service: this,\n        propertyType: PropertyType.attribute,\n    };\n\n    private readonly _requirementDirectionProperty: IProperty = {\n        name: \"requirementDirection\",\n        displayName: \"direction\",\n        type: \"list\",\n        values: [\"TB\", \"BT\", \"LR\", \"RL\"],\n        attributeName: mermaidFlowchartDirectionAttribute,\n        service: this,\n        propertyType: PropertyType.attribute,\n    };\n\n    private readonly _titleProperty: IProperty = {\n        name: \"title\",\n        displayName: \"title\",\n        type: \"string\",\n        attributeName: mermaidTitleAttribute,\n        service: this,\n        propertyType: PropertyType.attribute,\n    };\n\n    override getRefreshMode() {\n        return RefreshMode.fullOnValueChange;\n    }\n\n    override isHandledElement(designItem: IDesignItem): boolean {\n        return designItem.isRootItem;\n    }\n\n    override async getProperties(designItem: IDesignItem): Promise<IProperty[]> {\n        const diagramType = getMermaidDocumentDiagramType(designItem);\n        if (diagramType === \"requirementDiagram\")\n            return [this._diagramTypeProperty, this._requirementDirectionProperty, this._titleProperty];\n        if (diagramType === \"sequenceDiagram\" || diagramType === \"mindmap\")\n            return [this._diagramTypeProperty, this._titleProperty];\n\n        return [this._diagramTypeProperty, this._flowchartDirectionProperty, this._titleProperty];\n    }\n\n    override async getProperty(designItem: IDesignItem, name: string): Promise<IProperty> {\n        if (name === this._diagramTypeProperty.name)\n            return this._diagramTypeProperty;\n        if (name === this._flowchartDirectionProperty.name)\n            return this._flowchartDirectionProperty;\n        if (name === this._requirementDirectionProperty.name)\n            return this._requirementDirectionProperty;\n        if (name === this._titleProperty.name)\n            return this._titleProperty;\n        return null;\n    }\n\n    override async setValue(designItems: IDesignItem[], property: IProperty, value: any): Promise<void> {\n        const rootDesignItems = designItems.filter(designItem => this.isHandledElement(designItem));\n        if (!rootDesignItems.length)\n            return;\n\n        const changeGroup = rootDesignItems[0].openGroup(\"mermaid document property changed: \" + property.name + \" to \" + value);\n        try {\n            for (const designItem of rootDesignItems)\n                setAttribute(designItem, property.attributeName, value);\n\n            if (property.name === this._diagramTypeProperty.name && (value === \"flowchart\" || value === \"requirementDiagram\")) {\n                for (const designItem of rootDesignItems) {\n                    if (!designItem.hasAttribute(mermaidFlowchartDirectionAttribute))\n                        setAttribute(designItem, mermaidFlowchartDirectionAttribute, value === \"requirementDiagram\" ? \"TB\" : \"TD\");\n                }\n            }\n\n            if (property.name === this._flowchartDirectionProperty.name && isFlowchartDirection(value)) {\n                for (const designItem of rootDesignItems)\n                    updateFlowchartDirection(designItem, value);\n            }\n            changeGroup.commit();\n        } catch (error) {\n            changeGroup.abort();\n            throw error;\n        }\n    }\n\n    override getPropertyTarget(): BindingTarget {\n        return BindingTarget.attribute;\n    }\n\n    override getValue(designItems: IDesignItem[], property: IProperty): any {\n        const designItem = designItems?.[0];\n        if (!designItem)\n            return null;\n\n        if (property.name === this._diagramTypeProperty.name)\n            return getMermaidDocumentDiagramType(designItem);\n\n        if (property.name === this._flowchartDirectionProperty.name)\n            return getMermaidDocumentFlowchartDirection(designItem);\n        if (property.name === this._requirementDirectionProperty.name)\n            return getMermaidDocumentFlowchartDirection(designItem);\n\n        if (property.name === this._titleProperty.name)\n            return getMermaidDocumentTitle(designItem);\n\n        return super.getValue(designItems, property);\n    }\n\n    override isSet(designItems: IDesignItem[], property: IProperty): ValueType {\n        return this.getValue(designItems, property) != null ? ValueType.all : ValueType.none;\n    }\n}\n\nexport function getMermaidDocumentDiagramType(rootDesignItem: IDesignItem): MermaidDocumentDiagramType {\n    const value = rootDesignItem?.getAttribute(mermaidDiagramTypeAttribute);\n    if (value === \"sequenceDiagram\" || value === \"mindmap\" || value === \"requirementDiagram\")\n        return value;\n    return \"flowchart\";\n}\n\nexport function getMermaidDocumentFlowchartDirection(rootDesignItem: IDesignItem) {\n    return rootDesignItem?.getAttribute(mermaidFlowchartDirectionAttribute) ?? \"TD\";\n}\n\nexport function getMermaidDocumentTitle(rootDesignItem: IDesignItem) {\n    return rootDesignItem?.getAttribute(mermaidTitleAttribute) ?? \"\";\n}\n\nexport function getMermaidDocumentFrontmatter(rootDesignItem: IDesignItem) {\n    return rootDesignItem?.getAttribute(mermaidFrontmatterAttribute) ?? \"\";\n}\n\nfunction setAttribute(designItem: IDesignItem, attributeName: string, value: any) {\n    if (value == null || value === \"\")\n        designItem.removeAttribute(attributeName);\n    else\n        designItem.setAttribute(attributeName, value.toString());\n}\n\nfunction updateFlowchartDirection(rootDesignItem: IDesignItem, direction: FlowchartDirection) {\n    const flowchartNodes: IDesignItem[] = [];\n    for (const child of rootDesignItem.children(true)) {\n        if (child.element.localName === \"mermaid-node\") {\n            child.setAttribute(\"diagram-direction\", direction);\n            flowchartNodes.push(child);\n        } else if (child.element.localName === \"mermaid-edge\") {\n            child.setAttribute(\"diagram-direction\", direction);\n        }\n    }\n    rerouteConnectedMermaidEdges(rootDesignItem.instanceServiceContainer, flowchartNodes, true);\n}\n\nfunction isFlowchartDirection(value: string): value is FlowchartDirection {\n    return value === \"TB\" || value === \"TD\" || value === \"BT\" || value === \"RL\" || value === \"LR\";\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidElementsService.ts",
    "content": "import { IElementDefinition, IElementsService, InstanceServiceContainer } from \"@node-projects/web-component-designer\";\nimport { LazyLoader, TypedEvent } from \"@node-projects/base-custom-webcomponent\";\nimport { getMermaidDocumentDiagramType, MermaidDocumentDiagramType } from \"./MermaidDocumentPropertiesService.js\";\n\ntype MermaidElementDefinition = IElementDefinition & {\n    diagramTypes?: MermaidDocumentDiagramType[];\n}\n\ntype MermaidElementsJson = {\n    elements: Array<string | MermaidElementDefinition>;\n}\n\nexport class MermaidElementsService implements IElementsService {\n    readonly onElementsChanged = new TypedEvent<void>();\n\n    private _allElements: MermaidElementDefinition[];\n    private _instanceServiceContainer: InstanceServiceContainer;\n    private _currentDiagramType: MermaidDocumentDiagramType = \"flowchart\";\n    private _resolveStored: ((value: IElementDefinition[]) => void)[];\n    private _rejectStored: ((errorCode: number) => void)[];\n\n    constructor(private readonly _name: string, file: string | URL) {\n        LazyLoader.LoadText(file.toString()).then(data => {\n            const parsed = JSON.parse(data) as MermaidElementsJson;\n            this._allElements = parsed.elements.map(element => typeof element === \"string\" ? { tag: element } : element);\n            this._resolveWaiting();\n        }).catch(error => this._rejectWaiting(error));\n    }\n\n    get name() {\n        return this._name;\n    }\n\n    setInstanceServiceContainer(instanceServiceContainer: InstanceServiceContainer) {\n        this._instanceServiceContainer = instanceServiceContainer;\n        this._refreshCurrentDiagramType();\n        instanceServiceContainer.onContentChanged.on(() => this._refreshCurrentDiagramType());\n    }\n\n    getElements(): Promise<IElementDefinition[]> {\n        if (this._allElements)\n            return Promise.resolve(this._filterElements());\n        if (!this._resolveStored) {\n            this._resolveStored = [];\n            this._rejectStored = [];\n        }\n        return new Promise((resolve, reject) => {\n            this._resolveStored.push(resolve);\n            this._rejectStored.push(reject);\n        });\n    }\n\n    private _filterElements() {\n        return this._allElements.filter(element => !element.diagramTypes || element.diagramTypes.includes(this._currentDiagramType));\n    }\n\n    private _refreshCurrentDiagramType() {\n        const nextDiagramType = getMermaidDocumentDiagramType(this._instanceServiceContainer?.rootDesignItem);\n        if (nextDiagramType === this._currentDiagramType)\n            return;\n        this._currentDiagramType = nextDiagramType;\n        this.onElementsChanged.emit();\n    }\n\n    private _resolveWaiting() {\n        if (!this._resolveStored)\n            return;\n        const elements = this._filterElements();\n        this._resolveStored.forEach(resolve => resolve(elements));\n        this._resolveStored = null;\n        this._rejectStored = null;\n    }\n\n    private _rejectWaiting(error: number) {\n        if (!this._rejectStored)\n            return;\n        this._rejectStored.forEach(reject => reject(error));\n        this._resolveStored = null;\n        this._rejectStored = null;\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidLayoutCopyPasteService.ts",
    "content": "import { copyTextToClipboard, DefaultHtmlParserService, getTextFromClipboard, ICopyPasteService, IDesignItem, IRect, InstanceServiceContainer, ServiceContainer } from \"@node-projects/web-component-designer\";\n\nexport class MermaidLayoutCopyPasteService implements ICopyPasteService {\n    async copyItems(designItems: IDesignItem[]): Promise<void> {\n        let savedata = \"\";\n        for (const designItem of designItems) {\n            savedata += designItem.element.outerHTML;\n        }\n        await copyTextToClipboard(savedata);\n    }\n\n    async getPasteItems(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<[designItems: IDesignItem[], positions?: IRect[]]> {\n        const text = await getTextFromClipboard();\n        const htmlParser = new DefaultHtmlParserService();\n        const result = await htmlParser.parse(text, serviceContainer, instanceServiceContainer, true);\n        return [result, null];\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidLayoutPlacementService.ts",
    "content": "import { DefaultPlacementService, IDesignItem, IDesignerCanvas, IPoint, filterChildPlaceItems } from \"@node-projects/web-component-designer\";\n\nexport class MermaidLayoutPlacementService extends DefaultPlacementService {\n    override serviceForContainer(container: IDesignItem) {\n        return container.element.localName === \"mermaid-mindmap-node\" || container.element.localName === \"mermaid-subgraph\";\n    }\n\n    override canEnter(container: IDesignItem, items: IDesignItem[]) {\n        if (container.element.localName === \"mermaid-mindmap-node\")\n            return items.every(item => item.element.localName === \"mermaid-mindmap-node\" && !item.element.contains(container.element) && item !== container);\n        if (container.element.localName === \"mermaid-subgraph\")\n            return items.every(item => (item.element.localName === \"mermaid-node\" || item.element.localName === \"mermaid-subgraph\") && !item.element.contains(container.element) && item !== container);\n        return false;\n    }\n\n    override enterContainer(container: IDesignItem, items: IDesignItem[]) {\n        const filteredItems = filterChildPlaceItems(items);\n        for (const item of filteredItems) {\n            if (item.element.localName === \"mermaid-mindmap-node\" || item.element.localName === \"mermaid-node\" || item.element.localName === \"mermaid-subgraph\")\n                container.insertChild(item);\n        }\n    }\n\n    override leaveContainer(container: IDesignItem, items: IDesignItem[]) {\n    }\n\n    override finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\n        super.finishPlace(event, designerCanvas, container, startPoint, offsetInControl, newPoint, items);\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidParserService.ts",
    "content": "import { DesignItem, IDesignItem, IHtmlParserService, IHtmlWriterOptions, IHtmlWriterService, InstanceServiceContainer, ITextWriter, ServiceContainer } from \"@node-projects/web-component-designer\";\nimport { MermaidEdge } from \"../widgets/mermaid-edge.js\";\nimport { MermaidNode, normalizeLabelLineBreaks } from \"../widgets/mermaid-node.js\";\nimport { MermaidSequenceMessage } from \"../widgets/mermaid-sequence-message.js\";\nimport { MermaidSequenceParticipant } from \"../widgets/mermaid-sequence-participant.js\";\nimport { MermaidMindmapNode } from \"../widgets/mermaid-mindmap-node.js\";\nimport { MermaidRequirementNode } from \"../widgets/mermaid-requirement-node.js\";\nimport { MermaidRequirementRelationship } from \"../widgets/mermaid-requirement-relationship.js\";\nimport { MermaidSubgraph } from \"../widgets/mermaid-subgraph.js\";\nimport { MermaidFlowchartDirective } from \"../widgets/mermaid-flowchart-directive.js\";\nimport { getMermaidDocumentDiagramType, getMermaidDocumentFrontmatter, getMermaidDocumentTitle, mermaidDiagramTypeAttribute, mermaidFlowchartDirectionAttribute, mermaidFrontmatterAttribute, mermaidTitleAttribute, MermaidDocumentDiagramType } from \"./MermaidDocumentPropertiesService.js\";\nimport { encodeWaypoints, FlowchartDirection, routeBetweenBounds } from \"./mermaidGeometry.js\";\n\ntype ParsedNode = {\n    id: string;\n    label: string;\n    shape: string;\n    parentId?: string;\n    classes?: string;\n    style?: string;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedEdge = {\n    from: string;\n    to: string;\n    fromNode?: ParsedNode;\n    toNode?: ParsedNode;\n    label: string;\n    edgeType: string;\n    connector?: string;\n    edgeId?: string;\n    animation?: string;\n    connectorRange?: { start: number; length: number };\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedSubgraph = {\n    id: string;\n    title: string;\n    parentId?: string;\n    direction?: FlowchartDirection;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedFlowchartDirective = {\n    line: string;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedSequenceParticipant = {\n    id: string;\n    label: string;\n    participantType: string;\n    implicit?: boolean;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedSequenceMessage = {\n    from: string;\n    to: string;\n    label: string;\n    messageType: string;\n    connector: string;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedMindmapNode = {\n    id: string;\n    parentId: string;\n    label: string;\n    shape: string;\n    indent: number;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedRequirementNode = {\n    id: string;\n    kind: \"requirement\" | \"element\";\n    requirementType?: string;\n    requirementId?: string;\n    text?: string;\n    risk?: string;\n    verifyMethod?: string;\n    elementType?: string;\n    docRef?: string;\n    sourceRange?: { start: number; length: number };\n}\n\ntype ParsedRequirementRelationship = {\n    from: string;\n    to: string;\n    relationshipType: string;\n    syntaxDirection: \"forward\" | \"reverse\";\n    sourceRange?: { start: number; length: number };\n}\n\ntype LineRecord = {\n    text: string;\n    trimmed: string;\n    indent: number;\n    start: number;\n}\n\ntype ParsedMermaidDocument = {\n    lines: LineRecord[];\n    title: string;\n    frontmatter: string;\n}\n\nexport class MermaidParserService implements IHtmlParserService, IHtmlWriterService {\n    options: IHtmlWriterOptions = {};\n    supportsRootItemWrite = true;\n\n    async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\n        const diagramKind = getDiagramKind(readMermaidDocument(html));\n        if (diagramKind === \"sequence\")\n            return parseSequenceDiagram(html, serviceContainer, instanceServiceContainer);\n        if (diagramKind === \"mindmap\")\n            return parseMindmapDiagram(html, serviceContainer, instanceServiceContainer);\n        if (diagramKind === \"requirementDiagram\")\n            return parseRequirementDiagram(html, serviceContainer, instanceServiceContainer);\n        return parseFlowchart(html, serviceContainer, instanceServiceContainer);\n    }\n\n    write(textWriter: ITextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean, updatePositions?: boolean) {\n        const rootDesignItem = getRootDesignItem(designItems);\n        const mermaidDesignItems = getMermaidDesignItems(designItems);\n        const documentType = getWritableDocumentDiagramType(mermaidDesignItems.length ? mermaidDesignItems : designItems);\n        if (documentType === \"mindmap\") {\n            writeMindmapDiagram(textWriter, mermaidDesignItems, updatePositions, rootDesignItem);\n            return;\n        }\n        if (documentType === \"sequenceDiagram\") {\n            writeSequenceDiagram(textWriter, mermaidDesignItems, updatePositions, rootDesignItem);\n            return;\n        }\n        if (documentType === \"requirementDiagram\") {\n            writeRequirementDiagram(textWriter, mermaidDesignItems, updatePositions, rootDesignItem);\n            return;\n        }\n\n        writeFlowchart(textWriter, mermaidDesignItems, updatePositions, rootDesignItem);\n    }\n}\n\nfunction getDiagramKind(document: ParsedMermaidDocument) {\n    const firstMeaningfulLine = document.lines.find(line => line.trimmed && !line.trimmed.startsWith(\"%%\"))?.trimmed ?? \"\";\n    if (equalsIgnoreCase(firstMeaningfulLine, \"sequenceDiagram\"))\n        return \"sequence\";\n    if (equalsIgnoreCase(firstMeaningfulLine, \"mindmap\"))\n        return \"mindmap\";\n    if (equalsIgnoreCase(firstMeaningfulLine, \"requirementDiagram\"))\n        return \"requirementDiagram\";\n    return \"flowchart\";\n}\n\nasync function parseFlowchart(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<IDesignItem[]> {\n        const document = readMermaidDocument(html);\n        const lines = document.lines;\n        const designItems: IDesignItem[] = [];\n        const nodes = new Map<string, ParsedNode>();\n        const nodeBounds = new Map<string, { x: number; y: number; width: number; height: number }>();\n        const edges: ParsedEdge[] = [];\n        const subgraphs = new Map<string, ParsedSubgraph>();\n        const directives: ParsedFlowchartDirective[] = [];\n        const subgraphStack: ParsedSubgraph[] = [];\n        let direction: FlowchartDirection = \"TD\";\n        const scannedNodes = new Map<string, ParsedNode>();\n        const scannedEdges: ParsedEdge[] = [];\n\n        for (const line of lines) {\n            if (!line.trimmed || line.trimmed.startsWith(\"%%\"))\n                continue;\n\n            const subgraph = parseSubgraphStart(line.trimmed, line.start, subgraphStack[subgraphStack.length - 1]?.id);\n            if (subgraph) {\n                subgraphs.set(subgraph.id, subgraph);\n                subgraphStack.push(subgraph);\n                continue;\n            }\n\n            if (equalsIgnoreCase(line.trimmed, \"end\")) {\n                subgraphStack.pop();\n                continue;\n            }\n\n            const directive = parseFlowchartDirective(line.trimmed, line.start);\n            if (directive) {\n                directives.push(directive);\n                continue;\n            }\n\n            const parsedDirection = parseFlowchartDirection(line.trimmed);\n            if (parsedDirection) {\n                if (subgraphStack.length)\n                    subgraphStack[subgraphStack.length - 1].direction = parsedDirection;\n                else\n                    direction = parsedDirection;\n                continue;\n            }\n            const parsedSubgraphDirection = parseDirectionStatement(line.trimmed);\n            if (parsedSubgraphDirection && subgraphStack.length) {\n                subgraphStack[subgraphStack.length - 1].direction = parsedSubgraphDirection;\n                continue;\n            }\n\n            const edge = parseEdge(line.trimmed, line.start);\n            if (edge) {\n                scannedEdges.push(edge);\n                mergeNode(scannedNodes, edge.fromNode);\n                mergeNode(scannedNodes, edge.toNode);\n                continue;\n            }\n\n            const node = parseNode(line.trimmed, line.start);\n            if (node) {\n                node.parentId = subgraphStack[subgraphStack.length - 1]?.id;\n                mergeNode(scannedNodes, node);\n            }\n        }\n\n        setMermaidDocumentAttributes(instanceServiceContainer, \"flowchart\", direction, document.title, document.frontmatter);\n        const parsed = await getMermaidFlowchartData(html);\n        if (parsed) {\n            for (const node of parsed.nodes) {\n                const scanned = scannedNodes.get(node.id);\n                mergeNode(nodes, { ...node, parentId: scanned?.parentId, classes: scanned?.classes, style: scanned?.style, sourceRange: scanned?.sourceRange });\n            }\n            for (const edge of parsed.edges) {\n                const scanned = getScannedEdge(scannedEdges, edge);\n                edges.push({ ...edge, ...scanned });\n            }\n        } else {\n            for (const node of scannedNodes.values())\n                mergeNode(nodes, node);\n            edges.push(...scannedEdges);\n        }\n\n        const mermaidLayout = await getMermaidLayout(html, nodes);\n        const designItemsById = new Map<string, IDesignItem>();\n        let subgraphIndex = 0;\n        for (const subgraph of subgraphs.values()) {\n            const layout = mermaidLayout?.subgraphs?.get(subgraph.id);\n            const element = new MermaidSubgraph();\n            element.style.position = \"absolute\";\n            element.style.left = (layout?.x ?? (30 + subgraphIndex * 40)) + \"px\";\n            element.style.top = (layout?.y ?? (30 + subgraphIndex * 40)) + \"px\";\n            element.style.width = (layout?.width ?? 360) + \"px\";\n            element.style.height = (layout?.height ?? 240) + \"px\";\n            element.setAttribute(\"subgraph-id\", subgraph.id);\n            element.setAttribute(\"title\", subgraph.title);\n            if (subgraph.direction)\n                element.setAttribute(\"direction\", subgraph.direction);\n            const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n            designItemsById.set(subgraph.id, designItem);\n            const parentItem = designItemsById.get(subgraph.parentId);\n            if (parentItem)\n                parentItem._insertChildInternal(designItem);\n            else\n                designItems.push(designItem);\n            if (subgraph.sourceRange)\n                instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, subgraph.sourceRange);\n            subgraphIndex++;\n        }\n\n        let index = 0;\n        for (const node of nodes.values()) {\n            const layout = mermaidLayout?.nodes.get(node.id);\n            const parentLayout = node.parentId ? mermaidLayout?.subgraphs?.get(node.parentId) : null;\n            const fallbackPosition = getFallbackFlowchartNodePosition(index, nodes.size, direction);\n            const left = layout ? layout.x - (parentLayout?.x ?? 0) : fallbackPosition.left;\n            const top = layout ? layout.y - (parentLayout?.y ?? 0) : fallbackPosition.top;\n            const width = layout?.width ?? getDefaultNodeSize(node.shape).width;\n            const height = layout?.height ?? getDefaultNodeSize(node.shape).height;\n            const element = new MermaidNode();\n            element.style.position = \"absolute\";\n            element.style.left = left + \"px\";\n            element.style.top = top + \"px\";\n            element.style.width = width + \"px\";\n            element.style.height = height + \"px\";\n            element.setAttribute(\"node-id\", node.id);\n            element.setAttribute(\"label\", node.label);\n            element.setAttribute(\"shape\", node.shape);\n            element.setAttribute(\"diagram-direction\", direction);\n            if (node.classes)\n                element.setAttribute(\"classes\", node.classes);\n            if (node.style)\n                element.setAttribute(\"style-directive\", node.style);\n            const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n            const parentItem = designItemsById.get(node.parentId);\n            if (parentItem)\n                parentItem._insertChildInternal(designItem);\n            else\n                designItems.push(designItem);\n            designItemsById.set(node.id, designItem);\n            if (node.sourceRange)\n                instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, node.sourceRange);\n            nodeBounds.set(node.id, { x: layout?.x ?? left, y: layout?.y ?? top, width, height });\n            index++;\n        }\n\n        let edgeIndex = 0;\n        for (const edge of edges) {\n            const element = new MermaidEdge();\n            element.style.position = \"absolute\";\n            element.style.left = \"40px\";\n            element.style.top = 60 + index * 110 + edgeIndex * 44 + \"px\";\n            element.style.width = \"220px\";\n            element.style.height = \"28px\";\n            element.setAttribute(\"from\", edge.from);\n            element.setAttribute(\"to\", edge.to);\n            element.setAttribute(\"label\", edge.label);\n            element.setAttribute(\"edge-type\", edge.edgeType);\n            if (edge.connector)\n                element.setAttribute(\"connector\", edge.connector);\n            if (edge.edgeId)\n                element.setAttribute(\"edge-id\", edge.edgeId);\n            if (edge.animation)\n                element.setAttribute(\"animation\", edge.animation);\n            element.setAttribute(\"diagram-direction\", direction);\n            const sourceBounds = nodeBounds.get(edge.from);\n            const targetBounds = nodeBounds.get(edge.to);\n            if (sourceBounds && targetBounds)\n                element.setAttribute(\"waypoints\", encodeWaypoints(routeBetweenBounds(sourceBounds, targetBounds, direction)));\n            const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n            designItems.push(designItem);\n            if (edge.sourceRange)\n                instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, edge.connectorRange ?? edge.sourceRange);\n            edgeIndex++;\n        }\n\n        let directiveIndex = 0;\n        for (const directive of directives) {\n            const element = new MermaidFlowchartDirective();\n            element.style.position = \"absolute\";\n            element.style.left = \"40px\";\n            element.style.top = 60 + index * 110 + edgeIndex * 44 + directiveIndex * 28 + \"px\";\n            element.style.width = \"320px\";\n            element.style.height = \"24px\";\n            element.setAttribute(\"line\", directive.line);\n            const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n            designItems.push(designItem);\n            if (directive.sourceRange)\n                instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, directive.sourceRange);\n            directiveIndex++;\n        }\n\n        return designItems;\n}\n\nfunction writeFlowchart(textWriter: ITextWriter, designItems: IDesignItem[], updatePositions?: boolean, rootDesignItem?: IDesignItem) {\n        const rootDirection = rootDesignItem?.getAttribute(mermaidFlowchartDirectionAttribute);\n        const direction = isFlowchartDirection(rootDirection) ? rootDirection : getDiagramDirection(designItems);\n        writeMermaidFrontmatter(textWriter, designItems, rootDesignItem);\n        textWriter.writeLine(\"flowchart \" + direction);\n        const allFlowchartItems = getAllFlowchartItems(designItems);\n        const nodeIdsUsedByEdges = getFlowchartNodeIdsUsedByEdges(allFlowchartItems);\n        const nodeItemsById = getFlowchartNodeItemsById(allFlowchartItems);\n        const explicitlyWrittenNodeIds = new Set<string>();\n\n        const writeNodeOrSubgraph = (designItem: IDesignItem, depth: number) => {\n            const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n            if (element.nodeName === \"MERMAID-SUBGRAPH\") {\n                const subgraphElement = element as HTMLElement & { createMermaidStart?: () => string };\n                writeDesignItemLine(textWriter, designItem, repeatText(\"    \", depth) + subgraphElement.createMermaidStart(), updatePositions);\n                const subgraphDirection = element.getAttribute(\"direction\");\n                if (isFlowchartDirection(subgraphDirection))\n                    textWriter.writeLine(repeatText(\"    \", depth + 1) + \"direction \" + subgraphDirection);\n                for (const child of designItem.children())\n                    writeNodeOrSubgraph(child, depth + 1);\n                textWriter.writeLine(repeatText(\"    \", depth) + \"end\");\n            } else if (element.nodeName === \"MERMAID-NODE\" && element.createMermaid && shouldWriteFlowchartNode(element, nodeIdsUsedByEdges)) {\n                writeDesignItemLine(textWriter, designItem, repeatText(\"    \", depth) + element.createMermaid(), updatePositions);\n                explicitlyWrittenNodeIds.add(element.getAttribute(\"node-id\"));\n            }\n        };\n\n        for (const designItem of designItems) {\n            writeNodeOrSubgraph(designItem, 1);\n        }\n        for (const designItem of allFlowchartItems) {\n            const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n            if (element.nodeName === \"MERMAID-EDGE\" && element.createMermaid)\n                writeFlowchartEdgeLine(textWriter, designItem, element, nodeItemsById, explicitlyWrittenNodeIds, updatePositions);\n        }\n        for (const designItem of allFlowchartItems) {\n            const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n            if (element.nodeName === \"MERMAID-FLOWCHART-DIRECTIVE\" && element.createMermaid)\n                writeDesignItemLine(textWriter, designItem, \"    \" + element.createMermaid(), updatePositions);\n        }\n}\n\nfunction writeFlowchartEdgeLine(textWriter: ITextWriter, designItem: IDesignItem, element: HTMLElement & { createMermaid?: () => string }, nodeItemsById: Map<string, IDesignItem>, explicitlyWrittenNodeIds: Set<string>, updatePositions?: boolean) {\n    const start = textWriter.position;\n    const line = indentEmbeddedNewlines(\"    \" + element.createMermaid());\n    textWriter.writeLine(line);\n    if (!updatePositions)\n        return;\n\n    const from = element.getAttribute(\"from\");\n    const to = element.getAttribute(\"to\");\n    const fromIndex = from ? line.indexOf(from) : -1;\n    const toIndex = to ? line.lastIndexOf(to) : -1;\n\n    if (from && fromIndex >= 0 && !explicitlyWrittenNodeIds.has(from))\n        nodeItemsById.get(from)?.instanceServiceContainer.designItemDocumentPositionService?.setPosition(nodeItemsById.get(from), { start: start + fromIndex, length: from.length });\n    if (to && toIndex >= 0 && !explicitlyWrittenNodeIds.has(to))\n        nodeItemsById.get(to)?.instanceServiceContainer.designItemDocumentPositionService?.setPosition(nodeItemsById.get(to), { start: start + toIndex, length: to.length });\n\n    if (fromIndex >= 0 && toIndex > fromIndex) {\n        const connectorStart = fromIndex + from.length;\n        designItem.instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, { start: start + connectorStart, length: toIndex - connectorStart });\n    } else {\n        designItem.instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, { start, length: line.length });\n    }\n}\n\nfunction getFlowchartNodeIdsUsedByEdges(designItems: IDesignItem[]) {\n    const ids = new Set<string>();\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement;\n        if (element.nodeName === \"MERMAID-EDGE\") {\n            const from = element.getAttribute(\"from\");\n            const to = element.getAttribute(\"to\");\n            if (from)\n                ids.add(from);\n            if (to)\n                ids.add(to);\n        }\n    }\n    return ids;\n}\n\nfunction getFlowchartNodeItemsById(designItems: IDesignItem[]) {\n    const nodeItemsById = new Map<string, IDesignItem>();\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement;\n        if (element.nodeName === \"MERMAID-NODE\") {\n            const id = element.getAttribute(\"node-id\");\n            if (id)\n                nodeItemsById.set(id, designItem);\n        }\n    }\n    return nodeItemsById;\n}\n\nfunction getAllFlowchartItems(designItems: IDesignItem[]) {\n    const result: IDesignItem[] = [];\n    const append = (item: IDesignItem) => {\n        result.push(item);\n        for (const child of item.children())\n            append(child);\n    };\n    for (const designItem of designItems)\n        append(designItem);\n    return result;\n}\n\nfunction shouldWriteFlowchartNode(element: HTMLElement, nodeIdsUsedByEdges: Set<string>) {\n    const id = element.getAttribute(\"node-id\");\n    const label = element.getAttribute(\"label\");\n    const shape = element.getAttribute(\"shape\") ?? \"rectangle\";\n    if (!nodeIdsUsedByEdges.has(id))\n        return true;\n    return shape !== \"rectangle\" || (label && label !== id);\n}\n\nasync function parseSequenceDiagram(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<IDesignItem[]> {\n    const document = readMermaidDocument(html);\n    setMermaidDocumentAttributes(instanceServiceContainer, \"sequenceDiagram\", undefined, document.title, document.frontmatter);\n    const lines = document.lines;\n    const participants = new Map<string, ParsedSequenceParticipant>();\n    const messages: ParsedSequenceMessage[] = [];\n    const scannedParticipants = new Map<string, ParsedSequenceParticipant>();\n    const scannedMessages: ParsedSequenceMessage[] = [];\n\n    for (const line of lines) {\n        if (!line.trimmed || line.trimmed.startsWith(\"%%\") || equalsIgnoreCase(line.trimmed, \"sequenceDiagram\"))\n            continue;\n\n        const participant = parseSequenceParticipant(line.trimmed, line.start);\n        if (participant) {\n            scannedParticipants.set(participant.id, participant);\n            continue;\n        }\n\n        const message = parseSequenceMessage(line.trimmed, line.start);\n        if (message) {\n            scannedMessages.push(message);\n        }\n    }\n\n    const parsed = await getMermaidSequenceData(html);\n    if (parsed) {\n        for (const participant of parsed.participants) {\n            const scanned = scannedParticipants.get(participant.id);\n            participants.set(participant.id, {\n                ...participant,\n                implicit: !scanned,\n                sourceRange: scanned?.sourceRange,\n            });\n        }\n        for (let i = 0; i < parsed.messages.length; i++) {\n            messages.push({\n                ...parsed.messages[i],\n                sourceRange: scannedMessages[i]?.sourceRange,\n            });\n        }\n    } else {\n        for (const participant of scannedParticipants.values())\n            participants.set(participant.id, participant);\n        messages.push(...scannedMessages);\n    }\n\n    for (const message of messages) {\n        ensureSequenceParticipant(participants, message.from);\n        ensureSequenceParticipant(participants, message.to);\n    }\n\n    const designItems: IDesignItem[] = [];\n    const participantBounds = new Map<string, { x: number; y: number; width: number; height: number }>();\n    let index = 0;\n    for (const participant of participants.values()) {\n        const width = 140;\n        const height = 44;\n        const left = 60 + index * 220;\n        const top = 40;\n        const element = new MermaidSequenceParticipant();\n        element.style.position = \"absolute\";\n        element.style.left = left + \"px\";\n        element.style.top = top + \"px\";\n        element.style.width = width + \"px\";\n        element.style.height = height + \"px\";\n        element.setAttribute(\"participant-id\", participant.id);\n        element.setAttribute(\"label\", participant.label);\n        element.setAttribute(\"participant-type\", participant.participantType);\n        if (participant.implicit)\n            element.setAttribute(\"implicit\", \"true\");\n        const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n        designItems.push(designItem);\n        if (participant.sourceRange)\n            instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, participant.sourceRange);\n        participantBounds.set(participant.id, { x: left, y: top, width, height });\n        index++;\n    }\n\n    let messageIndex = 0;\n    for (const message of messages) {\n        const fromBounds = participantBounds.get(message.from);\n        const toBounds = participantBounds.get(message.to);\n        const y = 130 + messageIndex * 54;\n        const startX = fromBounds ? fromBounds.x + fromBounds.width / 2 : 60;\n        const endX = toBounds ? toBounds.x + toBounds.width / 2 : startX + 220;\n        const left = Math.min(startX, endX);\n        const width = Math.max(80, Math.abs(endX - startX));\n        const element = new MermaidSequenceMessage();\n        element.style.position = \"absolute\";\n        element.style.left = left + \"px\";\n        element.style.top = y + \"px\";\n        element.style.width = width + \"px\";\n        element.style.height = \"36px\";\n        element.setAttribute(\"from\", message.from);\n        element.setAttribute(\"to\", message.to);\n        element.setAttribute(\"label\", message.label);\n        element.setAttribute(\"message-type\", message.messageType);\n        element.setAttribute(\"connector\", message.connector);\n        const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n        designItems.push(designItem);\n        if (message.sourceRange)\n            instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, message.sourceRange);\n        messageIndex++;\n    }\n\n    return designItems;\n}\n\nfunction writeSequenceDiagram(textWriter: ITextWriter, designItems: IDesignItem[], updatePositions?: boolean, rootDesignItem?: IDesignItem) {\n    writeMermaidFrontmatter(textWriter, designItems, rootDesignItem);\n    textWriter.writeLine(\"sequenceDiagram\");\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n        if (element.nodeName === \"MERMAID-SEQUENCE-PARTICIPANT\" && element.getAttribute(\"implicit\") !== \"true\" && element.createMermaid)\n            writeDesignItemLine(textWriter, designItem, \"    \" + element.createMermaid(), updatePositions);\n    }\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n        if (element.nodeName === \"MERMAID-SEQUENCE-MESSAGE\" && element.createMermaid)\n            writeDesignItemLine(textWriter, designItem, \"    \" + element.createMermaid(), updatePositions);\n    }\n}\n\nasync function parseMindmapDiagram(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<IDesignItem[]> {\n    const document = readMermaidDocument(html);\n    setMermaidDocumentAttributes(instanceServiceContainer, \"mindmap\", undefined, document.title, document.frontmatter);\n\n    const parsedNodes: ParsedMindmapNode[] = [];\n    const parentStack: ParsedMindmapNode[] = [];\n    let index = 0;\n\n    for (const line of document.lines) {\n        if (!line.trimmed || line.trimmed.startsWith(\"%%\") || equalsIgnoreCase(line.trimmed, \"mindmap\"))\n            continue;\n\n        while (parentStack.length && parentStack[parentStack.length - 1].indent >= line.indent)\n            parentStack.pop();\n\n        const parent = parentStack[parentStack.length - 1];\n        const parsed = parseMindmapNode(line.trimmed, index, parent?.id ?? \"\", line.start, line.text.length, line.indent);\n        parsedNodes.push(parsed);\n        parentStack.push(parsed);\n        index++;\n    }\n\n    const siblingsByParent = new Map<string, number>();\n    const designItemsById = new Map<string, IDesignItem>();\n    const rootDesignItems: IDesignItem[] = [];\n    const mermaidLayout = await getMermaidMindmapLayout(html, parsedNodes);\n    for (const node of parsedNodes) {\n        const siblingIndex = siblingsByParent.get(node.parentId) ?? 0;\n        siblingsByParent.set(node.parentId, siblingIndex + 1);\n        const layout = mermaidLayout?.nodes.get(node.id);\n        const parentLayout = node.parentId ? mermaidLayout?.nodes.get(node.parentId) : null;\n        const left = layout ? layout.x - (parentLayout?.x ?? 0) : (node.parentId ? 220 : 40);\n        const top = layout ? layout.y - (parentLayout?.y ?? 0) : (node.parentId ? siblingIndex * 76 : 40 + rootDesignItems.length * 96);\n        const element = new MermaidMindmapNode();\n        element.style.position = \"absolute\";\n        element.style.left = left + \"px\";\n        element.style.top = top + \"px\";\n        element.style.width = (layout?.width ?? getDefaultMindmapNodeSize(node.shape).width) + \"px\";\n        element.style.height = (layout?.height ?? getDefaultMindmapNodeSize(node.shape).height) + \"px\";\n        element.setAttribute(\"mindmap-id\", node.id);\n        element.setAttribute(\"label\", node.label);\n        element.setAttribute(\"shape\", node.shape);\n        const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n        designItemsById.set(node.id, designItem);\n        const parentItem = designItemsById.get(node.parentId);\n        if (parentItem) {\n            parentItem._insertChildInternal(designItem);\n        } else {\n            rootDesignItems.push(designItem);\n        }\n        if (node.sourceRange)\n            instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, node.sourceRange);\n    }\n\n    return rootDesignItems;\n}\n\nfunction writeMindmapDiagram(textWriter: ITextWriter, designItems: IDesignItem[], updatePositions?: boolean, rootDesignItem?: IDesignItem) {\n    writeMermaidFrontmatter(textWriter, designItems, rootDesignItem);\n    textWriter.writeLine(\"mindmap\");\n    const writeItems = (items: IDesignItem[], depth: number) => {\n        for (const item of sortMindmapItems(items)) {\n            const element = item.element as HTMLElement & { createMermaid?: () => string };\n            if (!element.createMermaid)\n                continue;\n            writeDesignItemLine(textWriter, item, repeatText(\"    \", depth + 1) + element.createMermaid(), updatePositions);\n            writeItems(Array.from(item.children()).filter(child => child.element.localName === MermaidMindmapNode.is), depth + 1);\n        }\n    };\n    writeItems(designItems.filter(designItem => designItem.element.localName === MermaidMindmapNode.is), 0);\n}\n\nasync function parseRequirementDiagram(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<IDesignItem[]> {\n    const document = readMermaidDocument(html);\n    const lines = document.lines;\n    const nodes = new Map<string, ParsedRequirementNode>();\n    const relationships: ParsedRequirementRelationship[] = [];\n    let direction: FlowchartDirection = \"TB\";\n\n    for (let i = 0; i < lines.length; i++) {\n        const line = lines[i];\n        if (!line.trimmed || line.trimmed.startsWith(\"%%\") || equalsIgnoreCase(line.trimmed, \"requirementDiagram\"))\n            continue;\n\n        const parsedDirection = parseDirectionStatement(line.trimmed);\n        if (parsedDirection) {\n            direction = parsedDirection;\n            continue;\n        }\n\n        const blockStart = parseRequirementBlockStart(line.trimmed);\n        if (blockStart) {\n            const blockLines: LineRecord[] = [];\n            const start = line.start;\n            while (++i < lines.length) {\n                if (lines[i].trimmed === \"}\")\n                    break;\n                blockLines.push(lines[i]);\n            }\n            const endLine = lines[Math.min(i, lines.length - 1)] ?? line;\n            const node = parseRequirementBlock(blockStart, blockLines, start, endLine.start + endLine.text.length - start);\n            nodes.set(node.id, node);\n            continue;\n        }\n\n        const relationship = parseRequirementRelationship(line.trimmed, line.start);\n        if (relationship)\n            relationships.push(relationship);\n    }\n\n    setMermaidDocumentAttributes(instanceServiceContainer, \"requirementDiagram\", direction, document.title, document.frontmatter);\n\n    const designItems: IDesignItem[] = [];\n    const nodeBounds = new Map<string, { x: number; y: number; width: number; height: number }>();\n    const mermaidLayout = await getMermaidRequirementLayout(html, nodes);\n    let index = 0;\n    for (const node of nodes.values()) {\n        const fallbackPosition = getFallbackRequirementNodePosition(index, nodes.size, direction);\n        const layout = mermaidLayout?.nodes.get(node.id);\n        const size = getDefaultRequirementNodeSize(node);\n        const left = layout?.x ?? fallbackPosition.left;\n        const top = layout?.y ?? fallbackPosition.top;\n        const width = layout?.width ?? size.width;\n        const height = layout?.height ?? size.height;\n        const element = new MermaidRequirementNode();\n        element.style.position = \"absolute\";\n        element.style.left = left + \"px\";\n        element.style.top = top + \"px\";\n        element.style.width = width + \"px\";\n        element.style.height = height + \"px\";\n        element.setAttribute(\"node-kind\", node.kind);\n        element.setAttribute(\"label\", node.id);\n        if (node.kind === \"requirement\") {\n            element.setAttribute(\"requirement-type\", node.requirementType ?? \"requirement\");\n            element.setAttribute(\"requirement-id\", node.requirementId ?? \"\");\n            element.setAttribute(\"text\", node.text ?? \"\");\n            element.setAttribute(\"risk\", node.risk ?? \"\");\n            element.setAttribute(\"verify-method\", node.verifyMethod ?? \"\");\n        } else {\n            element.setAttribute(\"element-type\", node.elementType ?? \"\");\n            element.setAttribute(\"doc-ref\", node.docRef ?? \"\");\n        }\n        const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n        designItems.push(designItem);\n        if (node.sourceRange)\n            instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, node.sourceRange);\n        nodeBounds.set(node.id, { x: left, y: top, width, height });\n        index++;\n    }\n\n    let relationshipIndex = 0;\n    for (const relationship of relationships) {\n        const element = new MermaidRequirementRelationship();\n        element.style.position = \"absolute\";\n        element.setAttribute(\"from\", relationship.from);\n        element.setAttribute(\"to\", relationship.to);\n        element.setAttribute(\"relationship-type\", relationship.relationshipType);\n        element.setAttribute(\"syntax-direction\", relationship.syntaxDirection);\n        const sourceBounds = nodeBounds.get(relationship.from);\n        const targetBounds = nodeBounds.get(relationship.to);\n        if (sourceBounds && targetBounds) {\n            element.setAttribute(\"waypoints\", encodeWaypoints(routeBetweenBounds(sourceBounds, targetBounds, direction)));\n        } else {\n            element.style.left = \"40px\";\n            element.style.top = 80 + index * 120 + relationshipIndex * 40 + \"px\";\n            element.style.width = \"220px\";\n            element.style.height = \"28px\";\n        }\n        const designItem = DesignItem.createDesignItemFromInstance(element, serviceContainer, instanceServiceContainer);\n        designItems.push(designItem);\n        if (relationship.sourceRange)\n            instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, relationship.sourceRange);\n        relationshipIndex++;\n    }\n\n    return designItems;\n}\n\nfunction writeRequirementDiagram(textWriter: ITextWriter, designItems: IDesignItem[], updatePositions?: boolean, rootDesignItem?: IDesignItem) {\n    const rootDirection = rootDesignItem?.getAttribute(mermaidFlowchartDirectionAttribute);\n    const direction = isRequirementDirection(rootDirection) ? rootDirection : \"TB\";\n    writeMermaidFrontmatter(textWriter, designItems, rootDesignItem);\n    textWriter.writeLine(\"requirementDiagram\");\n    if (direction !== \"TB\")\n        textWriter.writeLine(\"    direction \" + direction);\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n        if (element.nodeName === \"MERMAID-REQUIREMENT-NODE\" && element.createMermaid)\n            writeDesignItemLine(textWriter, designItem, \"    \" + element.createMermaid(), updatePositions);\n    }\n    for (const designItem of designItems) {\n        const element = designItem.element as HTMLElement & { createMermaid?: () => string };\n        if (element.nodeName === \"MERMAID-REQUIREMENT-RELATIONSHIP\" && element.createMermaid)\n            writeDesignItemLine(textWriter, designItem, \"    \" + element.createMermaid(), updatePositions);\n    }\n}\n\nfunction parseSequenceParticipant(line: string, sourceStart: number): ParsedSequenceParticipant {\n    const cursor = new SourceCursor(line);\n    const keyword = cursor.readIdentifier();\n    if (!equalsIgnoreCase(keyword, \"participant\") && !equalsIgnoreCase(keyword, \"actor\"))\n        return null;\n    if (!cursor.skipRequiredWhitespace())\n        return null;\n    const id = cursor.readIdentifier();\n    if (!id)\n        return null;\n\n    let label = id;\n    cursor.skipWhitespace();\n    if (!cursor.atEnd()) {\n        const marker = cursor.readIdentifier();\n        if (!equalsIgnoreCase(marker, \"as\"))\n            return null;\n        if (!cursor.skipRequiredWhitespace())\n            return null;\n        label = cursor.readRest().trim();\n    }\n\n    return {\n        id,\n        label: unquoteLabel(label),\n        participantType: keyword.toLowerCase(),\n        sourceRange: { start: sourceStart, length: line.length },\n    };\n}\n\nfunction parseSequenceMessage(line: string, sourceStart: number): ParsedSequenceMessage {\n    const parsed = parseBinaryStatement(line, [\"-->>\", \"->>\", \"-->\", \"->\", \"--x\", \"-x\", \"--)\", \"-)\"]);\n    if (!parsed)\n        return null;\n    const separatorIndex = parsed.right.indexOf(\":\");\n    if (separatorIndex < 0)\n        return null;\n    const to = parsed.right.substring(0, separatorIndex).trim();\n    const label = parsed.right.substring(separatorIndex + 1).trim();\n    if (!to || !isIdentifier(to))\n        return null;\n\n    return {\n        from: parsed.left,\n        to,\n        label: unquoteLabel(label),\n        messageType: getSequenceMessageType(parsed.operator),\n        connector: parsed.operator,\n        sourceRange: { start: sourceStart, length: line.length },\n    };\n}\n\nfunction ensureSequenceParticipant(participants: Map<string, ParsedSequenceParticipant>, id: string) {\n    if (!participants.has(id))\n        participants.set(id, { id, label: id, participantType: \"participant\", implicit: true });\n}\n\nfunction getSequenceMessageType(connector: string) {\n    if (connector.includes(\")\"))\n        return \"async\";\n    if (connector.includes(\"--\"))\n        return \"dotted\";\n    if (connector.includes(\"x\"))\n        return \"cross\";\n    if (connector === \"->\")\n        return \"open\";\n    return \"solid\";\n}\n\nclass SourceCursor {\n    public position = 0;\n\n    constructor(private readonly text: string) {\n    }\n\n    atEnd() {\n        return this.position >= this.text.length;\n    }\n\n    skipWhitespace() {\n        while (!this.atEnd() && isWhitespace(this.text[this.position]))\n            this.position++;\n    }\n\n    skipRequiredWhitespace() {\n        const start = this.position;\n        this.skipWhitespace();\n        return this.position > start;\n    }\n\n    readIdentifier() {\n        if (this.atEnd() || !isIdentifierStart(this.text[this.position]))\n            return \"\";\n        const start = this.position;\n        this.position++;\n        while (!this.atEnd() && isIdentifierPart(this.text[this.position]))\n            this.position++;\n        return this.text.substring(start, this.position);\n    }\n\n    readRest() {\n        const rest = this.text.substring(this.position);\n        this.position = this.text.length;\n        return rest;\n    }\n\n    consume(value: string) {\n        if (!this.text.startsWith(value, this.position))\n            return false;\n        this.position += value.length;\n        return true;\n    }\n}\n\nfunction parseFlowchartDirection(line: string): FlowchartDirection | null {\n    const cursor = new SourceCursor(line);\n    const keyword = cursor.readIdentifier();\n    if (!equalsIgnoreCase(keyword, \"graph\") && !equalsIgnoreCase(keyword, \"flowchart\"))\n        return null;\n    if (!cursor.skipRequiredWhitespace())\n        return null;\n    const direction = cursor.readIdentifier().toUpperCase();\n    return isFlowchartDirection(direction) ? direction : null;\n}\n\nfunction parseDirectionStatement(line: string): FlowchartDirection | null {\n    const cursor = new SourceCursor(line);\n    const keyword = cursor.readIdentifier();\n    if (!equalsIgnoreCase(keyword, \"direction\"))\n        return null;\n    if (!cursor.skipRequiredWhitespace())\n        return null;\n    const direction = cursor.readIdentifier().toUpperCase();\n    return isRequirementDirection(direction) ? direction : null;\n}\n\nfunction parseSubgraphStart(line: string, sourceStart: number, parentId?: string): ParsedSubgraph | null {\n    const cursor = new SourceCursor(line);\n    const keyword = cursor.readIdentifier();\n    if (!equalsIgnoreCase(keyword, \"subgraph\"))\n        return null;\n    if (!cursor.skipRequiredWhitespace())\n        return null;\n    const rest = cursor.readRest().trim();\n    if (!rest)\n        return null;\n    const bracketStart = rest.indexOf(\"[\");\n    const bracketEnd = rest.endsWith(\"]\") ? rest.length - 1 : -1;\n    if (bracketStart > 0 && bracketEnd > bracketStart) {\n        const id = rest.substring(0, bracketStart).trim();\n        const title = unquoteLabel(rest.substring(bracketStart + 1, bracketEnd).trim());\n        return { id, title, parentId, sourceRange: { start: sourceStart, length: line.length } };\n    }\n    const title = unquoteLabel(rest);\n    return { id: sanitizeDirectiveId(title), title, parentId, sourceRange: { start: sourceStart, length: line.length } };\n}\n\nfunction parseFlowchartDirective(line: string, sourceStart: number): ParsedFlowchartDirective | null {\n    const cursor = new SourceCursor(line);\n    const keyword = cursor.readIdentifier();\n    if (!keyword)\n        return null;\n    if (equalsIgnoreCase(keyword, \"style\") || equalsIgnoreCase(keyword, \"classDef\") || equalsIgnoreCase(keyword, \"class\") || equalsIgnoreCase(keyword, \"click\") || equalsIgnoreCase(keyword, \"linkStyle\"))\n        return { line, sourceRange: { start: sourceStart, length: line.length } };\n    if (line.includes(\"@{\") && !line.trim().startsWith(\"subgraph\"))\n        return null;\n    return null;\n}\n\nfunction parseBinaryStatement(line: string, operators: string[]) {\n    const parsed = findOperator(line, operators);\n    if (!parsed)\n        return null;\n\n    const leftRange = trimRange(line, 0, parsed.index);\n    const rightRange = trimRange(line, parsed.index + parsed.operator.length, line.length);\n    if (leftRange.start >= leftRange.end || rightRange.start >= rightRange.end)\n        return null;\n\n    return {\n        left: line.substring(leftRange.start, leftRange.end),\n        leftStart: leftRange.start,\n        operator: parsed.operator,\n        operatorStart: parsed.index,\n        right: line.substring(rightRange.start, rightRange.end),\n        rightStart: rightRange.start,\n    };\n}\n\nfunction parseRequirementBlockStart(line: string): { kind: \"requirement\" | \"element\"; type: string; id: string } | null {\n    const cursor = new SourceCursor(line);\n    const type = cursor.readIdentifier();\n    if (!type)\n        return null;\n    const kind = equalsIgnoreCase(type, \"element\") ? \"element\" : isRequirementType(type) ? \"requirement\" : null;\n    if (!kind)\n        return null;\n    if (!cursor.skipRequiredWhitespace())\n        return null;\n    const id = cursor.readIdentifier();\n    if (!id)\n        return null;\n    cursor.skipWhitespace();\n    if (cursor.consume(\":::\")) {\n        cursor.readIdentifier();\n        cursor.skipWhitespace();\n    }\n    if (!cursor.consume(\"{\"))\n        return null;\n    return { kind, type, id };\n}\n\nfunction parseRequirementBlock(blockStart: { kind: \"requirement\" | \"element\"; type: string; id: string }, lines: LineRecord[], sourceStart: number, sourceLength: number): ParsedRequirementNode {\n    const values = new Map<string, string>();\n    for (const line of lines) {\n        const separator = findTopLevelColon(line.trimmed);\n        if (separator < 0)\n            continue;\n        const key = line.trimmed.substring(0, separator).trim().toLowerCase();\n        const value = unquoteLabel(line.trimmed.substring(separator + 1).trim());\n        values.set(key, value);\n    }\n\n    if (blockStart.kind === \"element\") {\n        return {\n            id: blockStart.id,\n            kind: \"element\",\n            elementType: values.get(\"type\") ?? \"\",\n            docRef: values.get(\"docref\") ?? \"\",\n            sourceRange: { start: sourceStart, length: sourceLength },\n        };\n    }\n\n    return {\n        id: blockStart.id,\n        kind: \"requirement\",\n        requirementType: blockStart.type,\n        requirementId: values.get(\"id\") ?? \"\",\n        text: values.get(\"text\") ?? \"\",\n        risk: values.get(\"risk\") ?? \"\",\n        verifyMethod: values.get(\"verifymethod\") ?? values.get(\"verifymethod\") ?? \"\",\n        sourceRange: { start: sourceStart, length: sourceLength },\n    };\n}\n\nfunction parseRequirementRelationship(line: string, sourceStart: number): ParsedRequirementRelationship | null {\n    const forward = parseRequirementRelationshipForward(line, sourceStart);\n    if (forward)\n        return forward;\n    return parseRequirementRelationshipReverse(line, sourceStart);\n}\n\nfunction parseRequirementRelationshipForward(line: string, sourceStart: number): ParsedRequirementRelationship | null {\n    const cursor = new SourceCursor(line);\n    const from = cursor.readIdentifier();\n    if (!from)\n        return null;\n    cursor.skipWhitespace();\n    if (!cursor.consume(\"-\"))\n        return null;\n    cursor.skipWhitespace();\n    const relationshipType = cursor.readIdentifier();\n    if (!isRequirementRelationshipType(relationshipType))\n        return null;\n    cursor.skipWhitespace();\n    if (!cursor.consume(\"->\"))\n        return null;\n    cursor.skipWhitespace();\n    const to = cursor.readIdentifier();\n    if (!to)\n        return null;\n    return { from, to, relationshipType, syntaxDirection: \"forward\", sourceRange: { start: sourceStart, length: line.length } };\n}\n\nfunction parseRequirementRelationshipReverse(line: string, sourceStart: number): ParsedRequirementRelationship | null {\n    const cursor = new SourceCursor(line);\n    const to = cursor.readIdentifier();\n    if (!to)\n        return null;\n    cursor.skipWhitespace();\n    if (!cursor.consume(\"<-\"))\n        return null;\n    cursor.skipWhitespace();\n    const relationshipType = cursor.readIdentifier();\n    if (!isRequirementRelationshipType(relationshipType))\n        return null;\n    cursor.skipWhitespace();\n    if (!cursor.consume(\"-\"))\n        return null;\n    cursor.skipWhitespace();\n    const from = cursor.readIdentifier();\n    if (!from)\n        return null;\n    return { from, to, relationshipType, syntaxDirection: \"reverse\", sourceRange: { start: sourceStart, length: line.length } };\n}\n\nfunction parseFlowchartEdgeStatement(line: string) {\n    const parsed = parseBinaryStatement(line, getFlowchartOperators());\n    if (!parsed)\n        return null;\n\n    let left = parsed.left;\n    let leftStart = parsed.leftStart;\n    let edgeId = \"\";\n    const edgeIdSeparator = left.lastIndexOf(\"@\");\n    if (edgeIdSeparator > 0) {\n        const candidate = left.substring(edgeIdSeparator + 1).trim();\n        if (!candidate) {\n            const idRange = trimRange(left, 0, edgeIdSeparator);\n            const idCandidate = left.substring(idRange.start, idRange.end).split(\" \").pop();\n            if (isIdentifier(idCandidate)) {\n                edgeId = idCandidate;\n                left = left.substring(0, left.lastIndexOf(idCandidate)).trim();\n                leftStart = parsed.leftStart;\n            }\n        }\n    }\n\n    let label = \"\";\n    let labelPartLength = 0;\n    const right = parsed.right;\n    if (right[0] === \"|\") {\n        const end = right.indexOf(\"|\", 1);\n        if (end > 0) {\n            label = right.substring(1, end);\n            const afterLabel = right.substring(end + 1).trim();\n            const skipped = countLeadingWhitespace(right);\n            labelPartLength = skipped + end + 1;\n            const afterLabelWhitespace = countLeadingWhitespace(right.substring(end + 1));\n            return {\n                ...parsed,\n                left,\n                leftStart,\n                right: afterLabel,\n                rightStart: parsed.rightStart + skipped + end + 1 + afterLabelWhitespace,\n                label,\n                labelPartLength,\n                edgeId,\n            };\n        }\n    }\n\n    return { ...parsed, left, leftStart, label, labelPartLength, edgeId };\n}\n\nfunction getFlowchartOperators() {\n    return [\n        \"<-->\", \"<==>\", \"<-.->\",\n        \"--o\", \"--x\", \"o--o\", \"x--x\", \"o-->\", \"x-->\",\n        \"~~~\",\n        \"-...->\", \"-..->\", \"-.->\", \"-...-\", \"-..-\", \"-.-\",\n        \"====>\", \"===>\", \"==>\", \"====\", \"===\",\n        \"---->\", \"--->\", \"-->\", \"-----\", \"----\", \"---\",\n    ];\n}\n\nfunction parseExpandedShape(value: string): { shape: string; label?: string } | null {\n    const cursor = new SourceCursor(value);\n    if (!cursor.consume(\"@{\"))\n        return null;\n    const contentEnd = value.lastIndexOf(\"}\");\n    const content = contentEnd >= 0 ? value.substring(cursor.position, contentEnd) : value.substring(cursor.position);\n    const properties = parseObjectLiteralProperties(content);\n    const shape = properties.get(\"shape\");\n    if (!shape)\n        return null;\n    return { shape, label: properties.get(\"label\") };\n}\n\nfunction parseDelimitedShapeValue(value: string): { label: string; shape: string } | null {\n    const candidates: Array<{ start: string; end: string; shape: string }> = [\n        { start: \"(((\", end: \")))\", shape: \"doubleCircle\" },\n        { start: \"((\", end: \"))\", shape: \"circle\" },\n        { start: \"([\", end: \"])\", shape: \"stadium\" },\n        { start: \"(\", end: \")\", shape: \"round\" },\n        { start: \"[[\", end: \"]]\", shape: \"subroutine\" },\n        { start: \"[(\", end: \")]\", shape: \"cylinder\" },\n        { start: \">\", end: \"]\", shape: \"asymmetric\" },\n        { start: \"{{\", end: \"}}\", shape: \"hexagon\" },\n        { start: \"{\", end: \"}\", shape: \"decision\" },\n        { start: \"[/\", end: \"/]\", shape: \"parallelogram\" },\n        { start: \"[\\\\\", end: \"\\\\]\", shape: \"parallelogramAlt\" },\n        { start: \"[/\", end: \"\\\\]\", shape: \"trapezoid\" },\n        { start: \"[\\\\\", end: \"/]\", shape: \"trapezoidAlt\" },\n        { start: \"[\", end: \"]\", shape: \"rectangle\" },\n    ];\n\n    for (const candidate of candidates) {\n        if (value.startsWith(candidate.start) && value.endsWith(candidate.end)) {\n            return {\n                label: value.substring(candidate.start.length, value.length - candidate.end.length).trim(),\n                shape: candidate.shape,\n            };\n        }\n    }\n\n    return null;\n}\n\nfunction parseObjectLiteralProperties(content: string) {\n    const properties = new Map<string, string>();\n    let start = 0;\n    let quote = \"\";\n    for (let i = 0; i <= content.length; i++) {\n        const char = content[i];\n        if (quote) {\n            if (char === quote)\n                quote = \"\";\n            continue;\n        }\n        if (char === \"\\\"\" || char === \"'\") {\n            quote = char;\n            continue;\n        }\n        if (i === content.length || char === \",\") {\n            const part = content.substring(start, i).trim();\n            const separator = findTopLevelColon(part);\n            if (separator > 0) {\n                const key = part.substring(0, separator).trim();\n                const value = unquoteLabel(part.substring(separator + 1).trim());\n                properties.set(key, value);\n            }\n            start = i + 1;\n        }\n    }\n    return properties;\n}\n\nfunction findOperator(line: string, operators: string[]) {\n    let depth = 0;\n    let quote = \"\";\n    for (let i = 0; i < line.length; i++) {\n        const char = line[i];\n        if (quote) {\n            if (char === quote)\n                quote = \"\";\n            continue;\n        }\n        if (char === \"\\\"\" || char === \"'\") {\n            quote = char;\n            continue;\n        }\n        if (char === \"[\" || char === \"(\" || char === \"{\")\n            depth++;\n        else if (char === \"]\" || char === \")\" || char === \"}\")\n            depth = Math.max(0, depth - 1);\n        if (depth > 0)\n            continue;\n        for (const operator of operators) {\n            if (line.startsWith(operator, i))\n                return { index: i, operator };\n        }\n    }\n    return null;\n}\n\nfunction trimRange(text: string, start: number, end: number) {\n    while (start < end && isWhitespace(text[start]))\n        start++;\n    while (end > start && isWhitespace(text[end - 1]))\n        end--;\n    return { start, end };\n}\n\nfunction countLeadingWhitespace(text: string) {\n    let count = 0;\n    while (count < text.length && isWhitespace(text[count]))\n        count++;\n    return count;\n}\n\nfunction equalsIgnoreCase(a: string, b: string) {\n    return a.toLowerCase() === b.toLowerCase();\n}\n\nfunction isIdentifier(value: string) {\n    const cursor = new SourceCursor(value);\n    const id = cursor.readIdentifier();\n    cursor.skipWhitespace();\n    return id.length === value.length || (id.length > 0 && cursor.atEnd());\n}\n\nfunction isIdentifierStart(char: string) {\n    return isAsciiLetter(char) || char === \"_\";\n}\n\nfunction isIdentifierPart(char: string) {\n    return isIdentifierStart(char) || isAsciiDigit(char) || char === \"-\";\n}\n\nfunction isAsciiLetter(char: string) {\n    if (!char)\n        return false;\n    const code = char.charCodeAt(0);\n    return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);\n}\n\nfunction isAsciiDigit(char: string) {\n    if (!char)\n        return false;\n    const code = char.charCodeAt(0);\n    return code >= 48 && code <= 57;\n}\n\nfunction isWhitespace(char: string) {\n    return char === \" \" || char === \"\\t\" || char === \"\\r\" || char === \"\\n\";\n}\n\nasync function getMermaidFlowchartData(code: string): Promise<{ nodes: ParsedNode[]; edges: ParsedEdge[] } | null> {\n    try {\n        const mermaid = await import(\"mermaid\");\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"loose\" });\n        const diagram = await mermaid.default.mermaidAPI.getDiagramFromText(code);\n        const db = diagram.db as any;\n        const vertices = db?.getVertices?.();\n        const mermaidEdges = db?.getEdges?.();\n        if (!vertices || !mermaidEdges)\n            return null;\n\n        const nodes: ParsedNode[] = Array.from(vertices.values()).map((vertex: any) => ({\n            id: vertex.id,\n            label: normalizeMermaidText(vertex.text ?? vertex.label ?? vertex.id),\n            shape: mapMermaidVertexType(vertex.type),\n        }));\n        const edges: ParsedEdge[] = mermaidEdges.map((edge: any) => ({\n            from: edge.start,\n            to: edge.end,\n            label: normalizeMermaidText(edge.text ?? \"\"),\n            edgeType: mapMermaidEdgeType(edge),\n        }));\n\n        return { nodes, edges };\n    } catch {\n        return null;\n    }\n}\n\nasync function getMermaidSequenceData(code: string): Promise<{ participants: ParsedSequenceParticipant[]; messages: ParsedSequenceMessage[] } | null> {\n    try {\n        const mermaid = await import(\"mermaid\");\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"loose\" });\n        const diagram = await mermaid.default.mermaidAPI.getDiagramFromText(code);\n        const db = diagram.db as any;\n        const actors = db?.getActors?.();\n        const mermaidMessages = db?.getMessages?.();\n        if (!actors || !mermaidMessages)\n            return null;\n\n        const participants: ParsedSequenceParticipant[] = Array.from(actors.entries()).map(([id, actor]: [string, any]) => ({\n            id,\n            label: actor.description ?? actor.name ?? id,\n            participantType: actor.type ?? \"participant\",\n        }));\n        const messages: ParsedSequenceMessage[] = mermaidMessages\n            .filter((message: any) => typeof message.from === \"string\" && typeof message.to === \"string\")\n            .map((message: any) => {\n                const connector = mapMermaidSequenceConnector(message.type);\n                return {\n                    from: message.from,\n                    to: message.to,\n                    label: message.message ?? \"\",\n                    messageType: getSequenceMessageType(connector),\n                    connector,\n                };\n            });\n\n        return { participants, messages };\n    } catch {\n        return null;\n    }\n}\n\nfunction getScannedEdge(scannedEdges: ParsedEdge[], edge: ParsedEdge): Partial<ParsedEdge> {\n    return scannedEdges.find(scannedEdge => scannedEdge.from === edge.from && scannedEdge.to === edge.to && scannedEdge.label === edge.label) ?? {};\n}\n\nfunction mapMermaidVertexType(type: string) {\n    const expanded = mapExpandedShape(type);\n    if (expanded !== \"rectangle\")\n        return expanded;\n    switch (type) {\n        case \"diamond\":\n            return \"decision\";\n        case \"round\":\n            return \"round\";\n        case \"stadium\":\n            return \"stadium\";\n        case \"circle\":\n            return \"circle\";\n        case \"doublecircle\":\n            return \"doubleCircle\";\n        case \"subroutine\":\n            return \"subroutine\";\n        case \"cylinder\":\n            return \"cylinder\";\n        case \"odd\":\n            return \"asymmetric\";\n        case \"hexagon\":\n            return \"hexagon\";\n        case \"lean_right\":\n            return \"parallelogram\";\n        case \"lean_left\":\n            return \"parallelogramAlt\";\n        case \"trapezoid\":\n            return \"trapezoid\";\n        case \"inv_trapezoid\":\n            return \"trapezoidAlt\";\n        default:\n            return \"rectangle\";\n    }\n}\n\nfunction mapMermaidEdgeType(edge: any) {\n    if (edge.stroke === \"dotted\")\n        return \"dotted\";\n    if (edge.stroke === \"thick\")\n        return \"thick\";\n    if (!edge.type?.includes(\"arrow\"))\n        return \"open\";\n    return \"arrow\";\n}\n\nfunction mapMermaidSequenceConnector(type: number) {\n    switch (type) {\n        case 1:\n            return \"-->>\";\n        case 3:\n            return \"-x\";\n        case 4:\n            return \"--x\";\n        case 5:\n            return \"->\";\n        case 6:\n            return \"-->\";\n        case 24:\n            return \"-)\";\n        case 25:\n            return \"--)\";\n        default:\n            return \"->>\";\n    }\n}\n\nfunction normalizeMermaidText(value: any) {\n    if (value == null)\n        return \"\";\n    if (typeof value === \"string\")\n        return stripMarkdownString(value);\n    if (Array.isArray(value))\n        return value.map(normalizeMermaidText).join(\"\\n\");\n    if (typeof value === \"object\") {\n        if (typeof value.text === \"string\")\n            return stripMarkdownString(value.text);\n        if (typeof value.label === \"string\")\n            return stripMarkdownString(value.label);\n        if (typeof value.markdown === \"string\")\n            return stripMarkdownString(value.markdown);\n    }\n    return stripMarkdownString(value.toString());\n}\n\nfunction stripMarkdownString(value: string) {\n    let result = value;\n    if (result.startsWith(\"`\") && result.endsWith(\"`\") && result.length >= 2)\n        result = result.substring(1, result.length - 1);\n    return normalizeLabelLineBreaks(result);\n}\n\nfunction mergeNode(nodes: Map<string, ParsedNode>, node: ParsedNode) {\n    const existing = nodes.get(node.id);\n    if (!existing || existing.label === existing.id || node.label !== node.id)\n        nodes.set(node.id, node);\n}\n\nfunction parseNode(line: string, sourceStart?: number): ParsedNode {\n    const cursor = new SourceCursor(line);\n    const id = cursor.readIdentifier();\n    if (!id)\n        return null;\n\n    cursor.skipWhitespace();\n    const value = cursor.readRest().trim();\n    const parsedShape = parseShapeValue(id, value);\n    if (!parsedShape)\n        return null;\n\n    return {\n        ...parsedShape,\n        sourceRange: sourceStart == null ? undefined : { start: sourceStart, length: line.length },\n    };\n}\n\nfunction parseMindmapNode(line: string, index: number, parentId: string, sourceStart: number, sourceLength: number, indent: number): ParsedMindmapNode {\n    const parsedNode = parseNode(line);\n    if (parsedNode) {\n        return {\n            id: parsedNode.id,\n            parentId,\n            label: parsedNode.label,\n            shape: mapMindmapShape(parsedNode.shape),\n            indent,\n            sourceRange: { start: sourceStart, length: sourceLength },\n        };\n    }\n\n    const id = sanitizeMindmapId(line, index);\n    return {\n        id,\n        parentId,\n        label: unquoteLabel(line),\n        shape: \"default\",\n        indent,\n        sourceRange: { start: sourceStart, length: sourceLength },\n    };\n}\n\nfunction parseEdge(line: string, sourceStart: number): ParsedEdge & { fromNode: ParsedNode; toNode: ParsedNode } {\n    const parsed = parseFlowchartEdgeStatement(line);\n    if (!parsed)\n        return null;\n\n    const fromNode = parseNodeToken(parsed.left);\n    const toNode = parseNodeToken(parsed.right);\n    if (!fromNode || !toNode)\n        return null;\n\n    fromNode.sourceRange = { start: sourceStart + parsed.leftStart + parsed.left.indexOf(fromNode.id), length: fromNode.id.length };\n    toNode.sourceRange = { start: sourceStart + parsed.rightStart + parsed.right.indexOf(toNode.id), length: toNode.id.length };\n\n    return {\n        from: fromNode.id,\n        to: toNode.id,\n        fromNode,\n        toNode,\n        label: parsed.label,\n        edgeType: getEdgeType(parsed.operator),\n        connector: parsed.operator,\n        edgeId: parsed.edgeId,\n        connectorRange: { start: sourceStart + parsed.operatorStart, length: parsed.operator.length + parsed.labelPartLength },\n        sourceRange: { start: sourceStart, length: line.length },\n    };\n}\n\nfunction parseNodeToken(token: string): ParsedNode {\n    const node = parseNode(token);\n    if (node)\n        return node;\n\n    const id = new SourceCursor(token).readIdentifier();\n    return id ? { id, label: id, shape: \"rectangle\" } : null;\n}\n\nfunction parseShapeValue(id: string, value: string): ParsedNode {\n    if (!value)\n        return { id, label: id, shape: \"rectangle\" };\n\n    const expandedShape = parseExpandedShape(value);\n    if (expandedShape)\n        return { id, label: expandedShape.label ?? id, shape: mapExpandedShape(expandedShape.shape) };\n\n    const shapeValue = parseDelimitedShapeValue(value);\n    if (shapeValue)\n        return { id, label: unquoteLabel(shapeValue.label), shape: shapeValue.shape };\n\n    return null;\n}\n\nfunction getEdgeType(connector: string) {\n    if (connector.includes(\"~\"))\n        return \"invisible\";\n    if (connector.includes(\"o\"))\n        return \"circle\";\n    if (connector.includes(\"x\"))\n        return \"cross\";\n    if (connector.startsWith(\"<\") && connector.endsWith(\">\"))\n        return \"multi\";\n    if (connector.includes(\"==\"))\n        return \"thick\";\n    if (connector.includes(\"-.\"))\n        return \"dotted\";\n    if (!connector.includes(\">\"))\n        return \"open\";\n    return \"arrow\";\n}\n\nfunction unquoteLabel(label: string) {\n    let result = label.trim();\n    if (result.length >= 2) {\n        const first = result[0];\n        const last = result[result.length - 1];\n        if ((first === \"\\\"\" && last === \"\\\"\") || (first === \"'\" && last === \"'\"))\n            result = result.substring(1, result.length - 1);\n    }\n    return normalizeLabelLineBreaks(result);\n}\n\nfunction mapExpandedShape(shape: string) {\n    switch (shape) {\n        case \"rect\":\n        case \"proc\":\n        case \"process\":\n        case \"rectangle\":\n            return \"rectangle\";\n        case \"bang\":\n            return \"bang\";\n        case \"notch-rect\":\n        case \"card\":\n        case \"notched-rectangle\":\n            return \"notch-rect\";\n        case \"cloud\":\n            return \"cloud\";\n        case \"hourglass\":\n        case \"collate\":\n            return \"hourglass\";\n        case \"bolt\":\n        case \"com-link\":\n        case \"lightning-bolt\":\n            return \"bolt\";\n        case \"brace\":\n        case \"brace-l\":\n        case \"comment\":\n            return \"brace\";\n        case \"brace-r\":\n            return \"brace-r\";\n        case \"braces\":\n            return \"braces\";\n        case \"datastore\":\n        case \"data-store\":\n            return \"datastore\";\n        case \"delay\":\n        case \"half-rounded-rectangle\":\n            return \"delay\";\n        case \"h-cyl\":\n        case \"das\":\n        case \"horizontal-cylinder\":\n            return \"h-cyl\";\n        case \"lin-cyl\":\n        case \"disk\":\n        case \"lined-cylinder\":\n            return \"lin-cyl\";\n        case \"curv-trap\":\n        case \"curved-trapezoid\":\n        case \"display\":\n            return \"curv-trap\";\n        case \"div-rect\":\n        case \"div-proc\":\n        case \"divided-process\":\n        case \"divided-rectangle\":\n            return \"div-rect\";\n        case \"doc\":\n        case \"document\":\n            return \"doc\";\n        case \"tri\":\n        case \"extract\":\n        case \"triangle\":\n            return \"tri\";\n        case \"fork\":\n        case \"join\":\n            return \"fork\";\n        case \"win-pane\":\n        case \"internal-storage\":\n        case \"window-pane\":\n            return \"win-pane\";\n        case \"f-circ\":\n        case \"filled-circle\":\n        case \"junction\":\n            return \"f-circ\";\n        case \"lin-doc\":\n        case \"lined-document\":\n            return \"lin-doc\";\n        case \"lin-rect\":\n        case \"lin-proc\":\n        case \"lined-process\":\n        case \"lined-rectangle\":\n        case \"shaded-process\":\n            return \"lin-rect\";\n        case \"notch-pent\":\n        case \"loop-limit\":\n        case \"notched-pentagon\":\n            return \"notch-pent\";\n        case \"flip-tri\":\n        case \"flipped-triangle\":\n        case \"manual-file\":\n            return \"flip-tri\";\n        case \"sl-rect\":\n        case \"manual-input\":\n        case \"sloped-rectangle\":\n            return \"sl-rect\";\n        case \"docs\":\n        case \"documents\":\n        case \"st-doc\":\n        case \"stacked-document\":\n            return \"docs\";\n        case \"st-rect\":\n        case \"processes\":\n        case \"procs\":\n        case \"stacked-rectangle\":\n            return \"st-rect\";\n        case \"odd\":\n            return \"asymmetric\";\n        case \"flag\":\n        case \"paper-tape\":\n            return \"flag\";\n        case \"bow-rect\":\n        case \"bow-tie-rectangle\":\n        case \"stored-data\":\n            return \"bow-rect\";\n        case \"fr-circ\":\n        case \"framed-circle\":\n        case \"stop\":\n            return \"fr-circ\";\n        case \"cross-circ\":\n        case \"crossed-circle\":\n        case \"summary\":\n            return \"cross-circ\";\n        case \"tag-doc\":\n        case \"tagged-document\":\n            return \"tag-doc\";\n        case \"tag-rect\":\n        case \"tag-proc\":\n        case \"tagged-process\":\n        case \"tagged-rectangle\":\n            return \"tag-rect\";\n        case \"text\":\n            return \"text\";\n        case \"diam\":\n        case \"diamond\":\n        case \"decision\":\n        case \"question\":\n            return \"decision\";\n        case \"rounded\":\n        case \"round\":\n            return \"round\";\n        case \"stadium\":\n        case \"terminal\":\n            return \"stadium\";\n        case \"subroutine\":\n        case \"subprocess\":\n        case \"subproc\":\n        case \"fr-rect\":\n            return \"subroutine\";\n        case \"cyl\":\n        case \"cylinder\":\n        case \"database\":\n        case \"db\":\n            return \"cylinder\";\n        case \"hex\":\n        case \"hexagon\":\n            return \"hexagon\";\n        case \"circle\":\n            return \"circle\";\n        case \"dbl-circ\":\n        case \"double-circle\":\n            return \"doubleCircle\";\n        case \"lean-r\":\n        case \"in-out\":\n        case \"lean-right\":\n            return \"parallelogram\";\n        case \"lean-l\":\n        case \"lean-left\":\n        case \"out-in\":\n            return \"parallelogramAlt\";\n        case \"trap-b\":\n        case \"priority\":\n        case \"trapezoid\":\n        case \"trapezoid-bottom\":\n            return \"trapezoid\";\n        case \"trap-t\":\n        case \"inv-trapezoid\":\n        case \"manual\":\n        case \"trapezoid-top\":\n            return \"trapezoidAlt\";\n        default:\n            return \"rectangle\";\n    }\n}\n\nfunction mapMindmapShape(shape: string) {\n    switch (shape) {\n        case \"rectangle\":\n            return \"square\";\n        case \"round\":\n        case \"circle\":\n        case \"hexagon\":\n            return shape;\n        default:\n            return \"default\";\n    }\n}\n\nfunction sanitizeMindmapId(label: string, index: number) {\n    let result = \"\";\n    for (const char of label) {\n        if (isIdentifierPart(char))\n            result += char;\n        else if (isWhitespace(char) && result && result[result.length - 1] !== \"_\")\n            result += \"_\";\n    }\n    result = result.trim();\n    return result || \"mindmap_\" + index;\n}\n\nfunction sanitizeDirectiveId(label: string) {\n    let result = \"\";\n    for (const char of label) {\n        if (isIdentifierPart(char))\n            result += char;\n        else if (isWhitespace(char) && result && result[result.length - 1] !== \"_\")\n            result += \"_\";\n    }\n    return result || \"subgraph\";\n}\n\nfunction getDefaultMindmapNodeSize(shape: string) {\n    switch (shape) {\n        case \"circle\":\n        case \"bang\":\n            return { width: 88, height: 88 };\n        case \"hexagon\":\n            return { width: 150, height: 58 };\n        default:\n            return { width: 150, height: 44 };\n    }\n}\n\nfunction getDefaultRequirementNodeSize(node: ParsedRequirementNode) {\n    if (node.kind === \"element\")\n        return { width: 170, height: node.docRef ? 108 : 84 };\n    return { width: 220, height: 160 };\n}\n\nfunction getDefaultNodeSize(shape: string) {\n    switch (shape) {\n        case \"decision\":\n            return { width: 120, height: 80 };\n        case \"circle\":\n        case \"doubleCircle\":\n        case \"fr-circ\":\n        case \"cross-circ\":\n        case \"f-circ\":\n            return { width: 80, height: 80 };\n        case \"bang\":\n            return { width: 92, height: 92 };\n        case \"fork\":\n            return { width: 140, height: 28 };\n        case \"tri\":\n        case \"flip-tri\":\n            return { width: 110, height: 90 };\n        case \"brace\":\n        case \"brace-r\":\n        case \"braces\":\n        case \"text\":\n            return { width: 140, height: 54 };\n        default:\n            return { width: 120, height: 54 };\n    }\n}\n\nfunction getFallbackRequirementNodePosition(index: number, total: number, direction: FlowchartDirection) {\n    const columns = direction === \"LR\" || direction === \"RL\" ? Math.max(1, Math.ceil(total / 3)) : 3;\n    const column = index % columns;\n    const row = Math.floor(index / columns);\n    const left = 40 + column * 300;\n    const top = 40 + row * 230;\n    if (direction === \"RL\")\n        return { left: 40 + (columns - column - 1) * 300, top };\n    if (direction === \"BT\")\n        return { left, top: 40 + (Math.ceil(total / columns) - row - 1) * 230 };\n    return { left, top };\n}\n\nfunction getFallbackFlowchartNodePosition(index: number, count: number, direction: FlowchartDirection) {\n    const xStep = 180;\n    const yStep = 110;\n    switch (direction) {\n        case \"LR\":\n            return { left: 40 + index * xStep, top: 40 };\n        case \"RL\":\n            return { left: 40 + (count - index - 1) * xStep, top: 40 };\n        case \"BT\":\n            return { left: 40, top: 40 + (count - index - 1) * yStep };\n        case \"TD\":\n        case \"TB\":\n        default:\n            return { left: 40, top: 40 + index * yStep };\n    }\n}\n\nfunction readMermaidDocument(text: string): ParsedMermaidDocument {\n    const lines = getLineRecords(text);\n    if (lines[0]?.trimmed !== \"---\")\n        return { lines, title: \"\", frontmatter: \"\" };\n\n    let frontmatterEnd = -1;\n    for (let i = 1; i < lines.length; i++) {\n        if (lines[i].trimmed === \"---\") {\n            frontmatterEnd = i;\n            break;\n        }\n    }\n\n    if (frontmatterEnd < 0)\n        return { lines, title: \"\", frontmatter: \"\" };\n\n    const frontmatterLines = lines.slice(1, frontmatterEnd);\n\n    return {\n        lines: lines.filter((_, index) => index > frontmatterEnd),\n        title: readFrontmatterTitle(frontmatterLines),\n        frontmatter: readFrontmatterWithoutTitle(frontmatterLines),\n    };\n}\n\nfunction readFrontmatterTitle(lines: LineRecord[]) {\n    for (const line of lines) {\n        const separator = line.trimmed.indexOf(\":\");\n        if (separator < 0)\n            continue;\n        const key = line.trimmed.substring(0, separator).trim();\n        if (equalsIgnoreCase(key, \"title\"))\n            return unquoteLabel(line.trimmed.substring(separator + 1).trim());\n    }\n    return \"\";\n}\n\nfunction readFrontmatterWithoutTitle(lines: LineRecord[]) {\n    const remainingLines: string[] = [];\n    for (let i = 0; i < lines.length; i++) {\n        const line = lines[i];\n        const separator = line.trimmed.indexOf(\":\");\n        const key = separator >= 0 ? line.trimmed.substring(0, separator).trim() : \"\";\n        if (equalsIgnoreCase(key, \"title\"))\n            continue;\n        remainingLines.push(line.text);\n    }\n    return trimBlankLines(remainingLines).join(\"\\n\");\n}\n\nfunction trimBlankLines(lines: string[]) {\n    let start = 0;\n    let end = lines.length;\n    while (start < end && !lines[start].trim())\n        start++;\n    while (end > start && !lines[end - 1].trim())\n        end--;\n    return lines.slice(start, end);\n}\n\nfunction getLineRecords(text: string) {\n    const records: LineRecord[] = [];\n    let offset = 0;\n    for (const line of splitLines(text)) {\n        const leadingWhitespaceLength = countLeadingWhitespace(line);\n        records.push({ text: line, trimmed: line.trim(), indent: leadingWhitespaceLength, start: offset + leadingWhitespaceLength });\n        offset += line.length + 1;\n    }\n    return records;\n}\n\nfunction splitLines(text: string) {\n    const lines: string[] = [];\n    let start = 0;\n    for (let i = 0; i < text.length; i++) {\n        if (text[i] === \"\\n\") {\n            let end = i;\n            if (end > start && text[end - 1] === \"\\r\")\n                end--;\n            lines.push(text.substring(start, end));\n            start = i + 1;\n        }\n    }\n    lines.push(text.substring(start));\n    return lines;\n}\n\nfunction getDiagramDirection(designItems: IDesignItem[]) {\n    const rootDesignItem = getRootDesignItem(designItems);\n    const rootDirection = rootDesignItem?.getAttribute(mermaidFlowchartDirectionAttribute);\n    if (isFlowchartDirection(rootDirection))\n        return rootDirection;\n\n    for (const designItem of designItems) {\n        const direction = designItem.element.getAttribute(\"diagram-direction\");\n        if (isFlowchartDirection(direction))\n            return direction;\n    }\n    return inferDiagramDirection(designItems);\n}\n\nfunction inferDiagramDirection(designItems: IDesignItem[]): FlowchartDirection {\n    const nodes = designItems\n        .filter(designItem => designItem.element.localName === \"mermaid-node\")\n        .map(designItem => {\n            const element = designItem.element as HTMLElement;\n            return {\n                left: Number.parseFloat(element.style.left || \"0\"),\n                top: Number.parseFloat(element.style.top || \"0\"),\n            };\n        });\n\n    if (nodes.length < 2)\n        return \"TD\";\n\n    const first = nodes[0];\n    const last = nodes[nodes.length - 1];\n    const dx = last.left - first.left;\n    const dy = last.top - first.top;\n\n    if (Math.abs(dx) > Math.abs(dy))\n        return dx >= 0 ? \"LR\" : \"RL\";\n\n    return dy >= 0 ? \"TD\" : \"BT\";\n}\n\nfunction isFlowchartDirection(value: string): value is FlowchartDirection {\n    return value === \"TB\" || value === \"TD\" || value === \"BT\" || value === \"RL\" || value === \"LR\";\n}\n\nfunction isRequirementDirection(value: string): value is FlowchartDirection {\n    return value === \"TB\" || value === \"BT\" || value === \"RL\" || value === \"LR\";\n}\n\nfunction setMermaidDocumentAttributes(instanceServiceContainer: InstanceServiceContainer, diagramType: MermaidDocumentDiagramType, direction?: FlowchartDirection, title?: string, frontmatter?: string) {\n    const rootDesignItem = instanceServiceContainer?.designerCanvas?.rootDesignItem;\n    rootDesignItem?._withoutUndoSetAttribute(mermaidDiagramTypeAttribute, diagramType);\n    if (direction)\n        rootDesignItem?._withoutUndoSetAttribute(mermaidFlowchartDirectionAttribute, direction);\n    else\n        rootDesignItem?._withoutUndoRemoveAttribute(mermaidFlowchartDirectionAttribute);\n    if (title)\n        rootDesignItem?._withoutUndoSetAttribute(mermaidTitleAttribute, title);\n    else\n        rootDesignItem?._withoutUndoRemoveAttribute(mermaidTitleAttribute);\n    if (frontmatter)\n        rootDesignItem?._withoutUndoSetAttribute(mermaidFrontmatterAttribute, frontmatter);\n    else\n        rootDesignItem?._withoutUndoRemoveAttribute(mermaidFrontmatterAttribute);\n}\n\nfunction writeMermaidFrontmatter(textWriter: ITextWriter, designItems: IDesignItem[], rootDesignItem?: IDesignItem) {\n    const root = rootDesignItem ?? getRootDesignItem(designItems);\n    const title = getMermaidDocumentTitle(root);\n    const frontmatter = getMermaidDocumentFrontmatter(root);\n    if (!title && !frontmatter)\n        return;\n    textWriter.writeLine(\"---\");\n    if (title)\n        textWriter.writeLine(\"title: \" + title);\n    if (frontmatter) {\n        for (const line of splitLines(frontmatter))\n            textWriter.writeLine(line);\n    }\n    textWriter.writeLine(\"---\");\n}\n\nfunction getWritableDocumentDiagramType(designItems: IDesignItem[]): MermaidDocumentDiagramType {\n    const hasSequenceItems = designItems.some(designItem => designItem.element.localName === MermaidSequenceParticipant.is || designItem.element.localName === MermaidSequenceMessage.is);\n    const hasFlowchartItems = designItems.some(designItem => designItem.element.localName === MermaidNode.is || designItem.element.localName === MermaidEdge.is);\n    const hasMindmapItems = designItems.some(designItem => designItem.element.localName === MermaidMindmapNode.is);\n    const hasRequirementItems = designItems.some(designItem => designItem.element.localName === MermaidRequirementNode.is || designItem.element.localName === MermaidRequirementRelationship.is);\n    const rootDesignItem = getRootDesignItem(designItems);\n    const documentType = rootDesignItem ? getMermaidDocumentDiagramType(rootDesignItem) : null;\n\n    if (documentType === \"requirementDiagram\" && !hasFlowchartItems && !hasSequenceItems && !hasMindmapItems)\n        return \"requirementDiagram\";\n    if (documentType === \"mindmap\" && !hasFlowchartItems && !hasSequenceItems && !hasRequirementItems)\n        return \"mindmap\";\n    if (documentType === \"sequenceDiagram\" && !hasFlowchartItems && !hasMindmapItems && !hasRequirementItems)\n        return \"sequenceDiagram\";\n    if (documentType === \"flowchart\" && !hasSequenceItems && !hasMindmapItems && !hasRequirementItems)\n        return \"flowchart\";\n    if (hasRequirementItems)\n        return \"requirementDiagram\";\n    if (hasMindmapItems)\n        return \"mindmap\";\n    if (hasSequenceItems)\n        return \"sequenceDiagram\";\n    return \"flowchart\";\n}\n\nfunction getRootDesignItem(designItems: IDesignItem[]) {\n    return designItems[0]?.isRootItem ? designItems[0] : designItems[0]?.instanceServiceContainer?.designerCanvas?.rootDesignItem;\n}\n\nfunction getMermaidDesignItems(designItems: IDesignItem[]) {\n    if (designItems[0]?.isRootItem)\n        return Array.from(designItems[0].children());\n    return designItems;\n}\n\nfunction getMindmapSortValue(designItem: IDesignItem) {\n    const element = designItem.element as HTMLElement;\n    const top = Number.parseFloat(element.style.top || \"0\");\n    const left = Number.parseFloat(element.style.left || \"0\");\n    return top * 10000 + left;\n}\n\nfunction sortMindmapItems(items: IDesignItem[]) {\n    return [...items].sort((a, b) => getMindmapSortValue(a) - getMindmapSortValue(b));\n}\n\nfunction repeatText(value: string, count: number) {\n    let result = \"\";\n    for (let i = 0; i < count; i++)\n        result += value;\n    return result;\n}\n\nfunction writeDesignItemLine(textWriter: ITextWriter, designItem: IDesignItem, line: string, updatePositions?: boolean) {\n    const start = textWriter.position;\n    line = indentEmbeddedNewlines(line);\n    textWriter.writeLine(line);\n    if (updatePositions)\n        designItem.instanceServiceContainer.designItemDocumentPositionService?.setPosition(designItem, { start, length: line.length });\n}\n\nfunction indentEmbeddedNewlines(line: string) {\n    const newlineIndex = line.indexOf(\"\\n\");\n    if (newlineIndex < 0)\n        return line;\n    const indent = line.substring(0, countLeadingWhitespace(line));\n    return line.replaceAll(\"\\n\", \"\\n\" + indent);\n}\n\nasync function getMermaidLayout(code: string, nodes: Map<string, ParsedNode>) {\n    if (typeof document === \"undefined\" || typeof DOMParser === \"undefined\")\n        return null;\n\n    try {\n        const mermaid = await import(\"mermaid\");\n        const id = \"mermaid-layout-\" + Math.random().toString(36).slice(2);\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"strict\" });\n        const result = await mermaid.default.render(id, code);\n        const doc = new DOMParser().parseFromString(result.svg, \"image/svg+xml\");\n        const layoutNodes = new Map<string, { x: number; y: number; width: number; height: number }>();\n        const layoutSubgraphs = new Map<string, { x: number; y: number; width: number; height: number }>();\n        let minX = Number.POSITIVE_INFINITY;\n        let minY = Number.POSITIVE_INFINITY;\n\n        for (const nodeElement of Array.from(doc.querySelectorAll(\"g.node\"))) {\n            const nodeId = nodeElement.getAttribute(\"data-id\") ?? getFlowchartNodeIdFromDomId(nodeElement.id);\n            if (!nodeId || !nodes.has(nodeId))\n                continue;\n\n            const center = parseTranslate(nodeElement.getAttribute(\"transform\"));\n            const size = getSvgNodeSize(nodeElement);\n            const layout = {\n                x: center.x - size.width / 2,\n                y: center.y - size.height / 2,\n                width: size.width,\n                height: size.height,\n            };\n            layoutNodes.set(nodeId, layout);\n            minX = Math.min(minX, layout.x);\n            minY = Math.min(minY, layout.y);\n        }\n\n        for (const clusterElement of Array.from(doc.querySelectorAll(\"g.cluster\"))) {\n            const clusterId = getClusterIdFromDomId(clusterElement.id, id);\n            if (!clusterId)\n                continue;\n            const rect = clusterElement.querySelector(\"rect\");\n            if (!rect)\n                continue;\n            const translate = parseTranslate(clusterElement.getAttribute(\"transform\"));\n            const x = (Number.parseFloat(rect.getAttribute(\"x\") ?? \"0\") || 0) + translate.x;\n            const y = (Number.parseFloat(rect.getAttribute(\"y\") ?? \"0\") || 0) + translate.y;\n            const width = Number.parseFloat(rect.getAttribute(\"width\") ?? \"\");\n            const height = Number.parseFloat(rect.getAttribute(\"height\") ?? \"\");\n            if (!Number.isFinite(width) || !Number.isFinite(height))\n                continue;\n            const layout = { x, y, width, height };\n            layoutSubgraphs.set(clusterId, layout);\n            minX = Math.min(minX, layout.x);\n            minY = Math.min(minY, layout.y);\n        }\n\n        if (!layoutNodes.size && !layoutSubgraphs.size)\n            return null;\n\n        const offsetX = 40 - minX;\n        const offsetY = 40 - minY;\n        for (const layout of layoutNodes.values()) {\n            layout.x += offsetX;\n            layout.y += offsetY;\n        }\n        for (const layout of layoutSubgraphs.values()) {\n            layout.x += offsetX;\n            layout.y += offsetY;\n        }\n\n        return { nodes: layoutNodes, subgraphs: layoutSubgraphs };\n    } catch {\n        return null;\n    }\n}\n\nasync function getMermaidRequirementLayout(code: string, nodes: Map<string, ParsedRequirementNode>) {\n    if (typeof document === \"undefined\" || typeof DOMParser === \"undefined\")\n        return null;\n\n    try {\n        const mermaid = await import(\"mermaid\");\n        const id = \"mermaid-requirement-layout-\" + Math.random().toString(36).slice(2);\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"strict\" });\n        const result = await mermaid.default.render(id, code);\n        const doc = new DOMParser().parseFromString(result.svg, \"image/svg+xml\");\n        const layoutNodes = new Map<string, { x: number; y: number; width: number; height: number }>();\n        let minX = Number.POSITIVE_INFINITY;\n        let minY = Number.POSITIVE_INFINITY;\n\n        for (const nodeElement of Array.from(doc.querySelectorAll(\"g.node\"))) {\n            const nodeId = getRequirementNodeIdFromSvgId(nodeElement.id, id, nodes);\n            const node = nodeId ? nodes.get(nodeId) : null;\n            if (!node)\n                continue;\n            const center = parseTranslate(nodeElement.getAttribute(\"transform\"));\n            const size = getDefaultRequirementNodeSize(node);\n            const layout = {\n                x: center.x - size.width / 2,\n                y: center.y - size.height / 2,\n                width: size.width,\n                height: size.height,\n            };\n            layoutNodes.set(nodeId, layout);\n            minX = Math.min(minX, layout.x);\n            minY = Math.min(minY, layout.y);\n        }\n\n        if (!layoutNodes.size)\n            return null;\n\n        const offsetX = 40 - minX;\n        const offsetY = 40 - minY;\n        for (const layout of layoutNodes.values()) {\n            layout.x += offsetX;\n            layout.y += offsetY;\n        }\n\n        return { nodes: layoutNodes };\n    } catch {\n        return null;\n    }\n}\n\nasync function getMermaidMindmapLayout(code: string, nodes: ParsedMindmapNode[]) {\n    if (typeof document === \"undefined\" || typeof DOMParser === \"undefined\")\n        return null;\n\n    try {\n        const mermaid = await import(\"mermaid\");\n        const id = \"mermaid-mindmap-layout-\" + Math.random().toString(36).slice(2);\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"strict\" });\n        const result = await mermaid.default.render(id, code);\n        const doc = new DOMParser().parseFromString(result.svg, \"image/svg+xml\");\n        const layoutNodes = new Map<string, { x: number; y: number; width: number; height: number }>();\n        const candidateElements = Array.from(doc.querySelectorAll(\"g.node.mindmap-node\"));\n        let minX = Number.POSITIVE_INFINITY;\n        let minY = Number.POSITIVE_INFINITY;\n        let nextNodeIndex = 0;\n\n        for (const nodeElement of candidateElements) {\n            if (nextNodeIndex >= nodes.length)\n                break;\n            if (!nodeElement.querySelector(\"text, foreignObject\"))\n                continue;\n\n            const node = nodes[nextNodeIndex++];\n            const center = parseTranslateFromElementTree(nodeElement);\n            const size = getMindmapWidgetSize(getSvgNodeSize(nodeElement), node.shape);\n            const layout = {\n                x: center.x - size.width / 2,\n                y: center.y - size.height / 2,\n                width: size.width,\n                height: size.height,\n            };\n            layoutNodes.set(node.id, layout);\n            minX = Math.min(minX, layout.x);\n            minY = Math.min(minY, layout.y);\n        }\n\n        if (!layoutNodes.size)\n            return null;\n\n        const offsetX = 40 - minX;\n        const offsetY = 40 - minY;\n        for (const layout of layoutNodes.values()) {\n            layout.x += offsetX;\n            layout.y += offsetY;\n        }\n\n        return { nodes: layoutNodes };\n    } catch {\n        return null;\n    }\n}\n\nfunction parseTranslateFromElementTree(element: Element) {\n    let x = 0;\n    let y = 0;\n    let current: Element = element;\n    while (current) {\n        const translate = parseTranslate(current.getAttribute(\"transform\"));\n        x += translate.x;\n        y += translate.y;\n        current = current.parentElement;\n    }\n    return { x, y };\n}\n\nfunction parseTranslate(transform: string) {\n    const values = parseTranslateValues(transform);\n    return {\n        x: values.x,\n        y: values.y,\n    };\n}\n\nfunction getFlowchartNodeIdFromDomId(domId: string) {\n    const prefix = \"flowchart-\";\n    if (!domId?.startsWith(prefix))\n        return \"\";\n    const withoutPrefix = domId.substring(prefix.length);\n    const lastDash = withoutPrefix.lastIndexOf(\"-\");\n    if (lastDash < 0)\n        return withoutPrefix;\n    const suffix = withoutPrefix.substring(lastDash + 1);\n    return isAllDigits(suffix) ? withoutPrefix.substring(0, lastDash) : withoutPrefix;\n}\n\nfunction getRequirementNodeIdFromSvgId(svgId: string, renderId: string, nodes: Map<string, ParsedRequirementNode>) {\n    if (!svgId)\n        return \"\";\n    const prefix = renderId + \"-\";\n    if (svgId.startsWith(prefix)) {\n        const id = svgId.substring(prefix.length);\n        if (nodes.has(id))\n            return id;\n    }\n    let match = \"\";\n    for (const id of nodes.keys()) {\n        if (svgId.endsWith(\"-\" + id) && id.length > match.length)\n            match = id;\n    }\n    return match;\n}\n\nfunction getClusterIdFromDomId(domId: string, renderId: string) {\n    if (!domId)\n        return \"\";\n    const prefixes = [renderId + \"_flowchart-\", \"flowchart-\", renderId + \"-\", \"cluster-\"];\n    for (const prefix of prefixes) {\n        if (domId.startsWith(prefix))\n            return domId.substring(prefix.length);\n    }\n    return domId;\n}\n\nfunction parseTranslateValues(transform: string) {\n    const result = { x: 0, y: 0 };\n    if (!transform)\n        return result;\n    const name = \"translate(\";\n    const start = transform.indexOf(name);\n    if (start < 0)\n        return result;\n    const end = transform.indexOf(\")\", start + name.length);\n    if (end < 0)\n        return result;\n    const content = transform.substring(start + name.length, end).trim();\n    const separator = content.includes(\",\") ? content.indexOf(\",\") : content.indexOf(\" \");\n    if (separator < 0)\n        return result;\n    result.x = Number.parseFloat(content.substring(0, separator).trim()) || 0;\n    result.y = Number.parseFloat(content.substring(separator + 1).trim()) || 0;\n    return result;\n}\n\nfunction findTopLevelColon(text: string) {\n    let quote = \"\";\n    for (let i = 0; i < text.length; i++) {\n        const char = text[i];\n        if (quote) {\n            if (char === quote)\n                quote = \"\";\n            continue;\n        }\n        if (char === \"\\\"\" || char === \"'\") {\n            quote = char;\n            continue;\n        }\n        if (char === \":\")\n            return i;\n    }\n    return -1;\n}\n\nfunction isRequirementType(value: string) {\n    return equalsIgnoreCase(value, \"requirement\")\n        || equalsIgnoreCase(value, \"functionalRequirement\")\n        || equalsIgnoreCase(value, \"interfaceRequirement\")\n        || equalsIgnoreCase(value, \"performanceRequirement\")\n        || equalsIgnoreCase(value, \"physicalRequirement\")\n        || equalsIgnoreCase(value, \"designConstraint\");\n}\n\nfunction isRequirementRelationshipType(value: string) {\n    return equalsIgnoreCase(value, \"contains\")\n        || equalsIgnoreCase(value, \"copies\")\n        || equalsIgnoreCase(value, \"derives\")\n        || equalsIgnoreCase(value, \"satisfies\")\n        || equalsIgnoreCase(value, \"verifies\")\n        || equalsIgnoreCase(value, \"refines\")\n        || equalsIgnoreCase(value, \"traces\");\n}\n\nfunction isAllDigits(value: string) {\n    if (!value)\n        return false;\n    for (const char of value) {\n        if (!isAsciiDigit(char))\n            return false;\n    }\n    return true;\n}\n\nfunction getSvgNodeSize(nodeElement: Element) {\n    const foreignObject = nodeElement.querySelector(\"foreignObject\");\n    if (foreignObject) {\n        const width = Number.parseFloat(foreignObject.getAttribute(\"width\") ?? \"\");\n        const height = Number.parseFloat(foreignObject.getAttribute(\"height\") ?? \"\");\n        if (Number.isFinite(width) && Number.isFinite(height))\n            return { width, height };\n    }\n\n    const rect = nodeElement.querySelector(\"rect\");\n    if (rect) {\n        const width = Number.parseFloat(rect.getAttribute(\"width\") ?? \"\");\n        const height = Number.parseFloat(rect.getAttribute(\"height\") ?? \"\");\n        if (Number.isFinite(width) && Number.isFinite(height))\n            return { width, height };\n    }\n\n    const polygon = nodeElement.querySelector(\"polygon\");\n    if (polygon) {\n        const points = splitWhitespace(polygon.getAttribute(\"points\") ?? \"\").map(point => {\n            const [x, y] = point.split(\",\").map(Number);\n            return { x, y };\n        }).filter(point => Number.isFinite(point.x) && Number.isFinite(point.y));\n        if (points.length) {\n            const xs = points.map(point => point.x);\n            const ys = points.map(point => point.y);\n            return { width: Math.max(...xs) - Math.min(...xs), height: Math.max(...ys) - Math.min(...ys) };\n        }\n    }\n\n    return { width: 120, height: 54 };\n}\n\nfunction getMindmapWidgetSize(svgSize: { width: number; height: number }, shape: string) {\n    const defaultSize = getDefaultMindmapNodeSize(shape);\n    return {\n        width: Math.max(svgSize.width, defaultSize.width),\n        height: Math.max(svgSize.height, defaultSize.height),\n    };\n}\n\nfunction splitWhitespace(text: string) {\n    const parts: string[] = [];\n    let start = -1;\n    for (let i = 0; i < text.length; i++) {\n        if (isWhitespace(text[i])) {\n            if (start >= 0) {\n                parts.push(text.substring(start, i));\n                start = -1;\n            }\n        } else if (start < 0) {\n            start = i;\n        }\n    }\n    if (start >= 0)\n        parts.push(text.substring(start));\n    return parts;\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/MermaidPropertyGroupsService.ts",
    "content": "import { IDesignItem, IPropertiesService } from \"@node-projects/web-component-designer\";\nimport { MermaidDocumentPropertiesService } from \"./MermaidDocumentPropertiesService.js\";\n\nexport class MermaidPropertyGroupsService {\n    private readonly _documentPropertiesService = new MermaidDocumentPropertiesService();\n\n    getPropertygroups(designItems: IDesignItem[]): { name: string; propertiesService: IPropertiesService; }[] {\n        const designItem = designItems?.[0];\n        if (!designItem)\n            return [];\n\n        if (designItem.isRootItem)\n            return [{ name: \"mermaid\", propertiesService: this._documentPropertiesService }];\n\n        const propertiesService = designItem.serviceContainer.getLastServiceWhere(\"propertyService\", service => service.isHandledElement(designItem));\n        return propertiesService ? [{ name: \"properties\", propertiesService }] : [];\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/services/mermaidGeometry.ts",
    "content": "export type Point = {\n    x: number;\n    y: number;\n}\n\nexport type Rect = Point & {\n    width: number;\n    height: number;\n}\n\nexport type FlowchartDirection = \"TB\" | \"TD\" | \"BT\" | \"RL\" | \"LR\";\n\nexport function encodeWaypoints(waypoints: Point[]) {\n    return waypoints.map(point => `${Math.round(point.x)},${Math.round(point.y)}`).join(\" \");\n}\n\nexport function decodeWaypoints(value: string) {\n    if (!value)\n        return [];\n\n    return value.split(/\\s+/).map(part => {\n        const [x, y] = part.split(\",\").map(Number);\n        return { x, y };\n    }).filter(point => Number.isFinite(point.x) && Number.isFinite(point.y));\n}\n\nexport function boundsFromWaypoints(waypoints: Point[]) {\n    const xs = waypoints.map(point => point.x);\n    const ys = waypoints.map(point => point.y);\n    const minX = Math.min(...xs);\n    const maxX = Math.max(...xs);\n    const minY = Math.min(...ys);\n    const maxY = Math.max(...ys);\n    return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\nexport function pathDataFromWaypoints(waypoints: Point[]) {\n    if (!waypoints.length)\n        return \"\";\n\n    return waypoints.map((point, index) => `${index === 0 ? \"M\" : \"L\"} ${point.x} ${point.y}`).join(\" \");\n}\n\nexport function midpointFromWaypoints(waypoints: Point[]) {\n    if (!waypoints.length)\n        return { x: 0, y: 0 };\n    if (waypoints.length === 1)\n        return waypoints[0];\n\n    const segmentLengths: number[] = [];\n    let totalLength = 0;\n    for (let i = 1; i < waypoints.length; i++) {\n        const length = distance(waypoints[i - 1], waypoints[i]);\n        segmentLengths.push(length);\n        totalLength += length;\n    }\n\n    let remaining = totalLength / 2;\n    for (let i = 1; i < waypoints.length; i++) {\n        const segmentLength = segmentLengths[i - 1];\n        if (remaining <= segmentLength) {\n            const ratio = segmentLength === 0 ? 0 : remaining / segmentLength;\n            return {\n                x: waypoints[i - 1].x + (waypoints[i].x - waypoints[i - 1].x) * ratio,\n                y: waypoints[i - 1].y + (waypoints[i].y - waypoints[i - 1].y) * ratio,\n            };\n        }\n        remaining -= segmentLength;\n    }\n\n    return waypoints[waypoints.length - 1];\n}\n\nexport function routeBetweenBounds(source: Rect, target: Rect, direction?: FlowchartDirection) {\n    const sourceCenter = center(source);\n    const targetCenter = center(target);\n    if (direction === \"LR\" || direction === \"RL\")\n        return [getHorizontalAnchor(source, targetCenter), getHorizontalAnchor(target, sourceCenter)];\n    if (direction === \"TD\" || direction === \"TB\" || direction === \"BT\")\n        return [getVerticalAnchor(source, targetCenter), getVerticalAnchor(target, sourceCenter)];\n    return [getAnchor(source, targetCenter), getAnchor(target, sourceCenter)];\n}\n\nexport function routeBetweenPoints(source: Point, target: Point) {\n    return [source, target];\n}\n\nexport function getAnchor(rect: Rect, target: Point) {\n    const c = center(rect);\n    const dx = target.x - c.x;\n    const dy = target.y - c.y;\n\n    if (Math.abs(dx / rect.width) > Math.abs(dy / rect.height)) {\n        return {\n            x: dx >= 0 ? rect.x + rect.width : rect.x,\n            y: c.y,\n        };\n    }\n\n    return {\n        x: c.x,\n        y: dy >= 0 ? rect.y + rect.height : rect.y,\n    };\n}\n\nfunction center(rect: Rect) {\n    return {\n        x: rect.x + rect.width / 2,\n        y: rect.y + rect.height / 2,\n    };\n}\n\nfunction getHorizontalAnchor(rect: Rect, target: Point) {\n    const c = center(rect);\n    return {\n        x: target.x >= c.x ? rect.x + rect.width : rect.x,\n        y: c.y,\n    };\n}\n\nfunction getVerticalAnchor(rect: Rect, target: Point) {\n    const c = center(rect);\n    return {\n        x: c.x,\n        y: target.y >= c.y ? rect.y + rect.height : rect.y,\n    };\n}\n\nfunction distance(a: Point, b: Point) {\n    return Math.hypot(b.x - a.x, b.y - a.y);\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/setupMermaidServiceContainer.ts",
    "content": "import { AbsolutePlacementService, AltToEnterContainerExtensionProvider, BaseCustomWebComponentPropertiesService, CopyPasteContextMenu, DefaultEditorTypeService, DefaultHtmlParserService, DefaultInstanceService, DefaultModelCommandService, DefaultPropertyEditorTypesService, DeletionService, DesignItemDocumentPositionService, DesignItemService, DragDropService, ElementAtPointService, ElementDragTitleExtensionProvider, ExtensionType, GrayOutDragOverContainerExtensionProvider, GrayOutExtensionProvider, HighlightElementExtensionProvider, IDesignerCanvas, ItemsBelowContextMenu, MagicWandSelectorTool, MultipleItemsSelectedContextMenu, NamedTools, PanTool, PointerTool, PointerToolButtonProvider, PositionExtensionProvider, RectangleSelectorTool, ResizeExtensionProvider, SelectionDefaultExtensionProvider, SelectionService, SelectorToolButtonProvider, SeperatorToolProvider, ServiceContainer, SimpleToolButtonProvider, SnaplinesProviderService, TransformToolButtonProvider, UndoService, ZMoveContextMenu, ZoomTool, ZoomToolButtonProvider } from \"@node-projects/web-component-designer\";\nimport { MermaidLayoutCopyPasteService } from \"./services/MermaidLayoutCopyPasteService.js\";\nimport { MermaidLayoutPlacementService } from \"./services/MermaidLayoutPlacementService.js\";\nimport { MermaidParserService } from \"./services/MermaidParserService.js\";\nimport { rerouteConnectedMermaidEdges } from \"./services/MermaidConnectionRouting.js\";\nimport { ConnectMermaidNodesTool, mermaidFlowIcon } from \"./toolbar/ConnectMermaidNodesTool.js\";\nimport { MermaidDemoView } from \"./widgets/views/mermaid-demo-view.js\";\nimport { MermaidPropertyGroupsService } from \"./services/MermaidPropertyGroupsService.js\";\nimport { MermaidElementsService } from \"./services/MermaidElementsService.js\";\n\nexport function createMermaidDesignerServiceContainer() {\n    const serviceContainer = new ServiceContainer();\n    const parserService = new MermaidParserService();\n    const elementsService = new MermaidElementsService(\"mermaid\", new URL(\"./widgets/elements.json\", import.meta.url));\n\n    serviceContainer.register(\"instanceService\", new DefaultInstanceService());\n    serviceContainer.register(\"containerService\", new AbsolutePlacementService());\n    serviceContainer.register(\"containerService\", new MermaidLayoutPlacementService());\n    serviceContainer.register(\"snaplinesProviderService\", new SnaplinesProviderService());\n    serviceContainer.register(\"htmlParserService\", new DefaultHtmlParserService());\n    serviceContainer.register(\"htmlParserService\", parserService);\n    serviceContainer.register(\"htmlWriterService\", parserService);\n    serviceContainer.register(\"elementAtPointService\", new ElementAtPointService());\n    serviceContainer.register(\"dragDropService\", new DragDropService());\n    serviceContainer.register(\"copyPasteService\", new MermaidLayoutCopyPasteService());\n    serviceContainer.register(\"modelCommandService\", new DefaultModelCommandService());\n    serviceContainer.register(\"propertyEditorTypesService\", new DefaultPropertyEditorTypesService());\n    serviceContainer.register(\"editorTypeService\", new DefaultEditorTypeService());\n    serviceContainer.register(\"propertyGroupsService\", new MermaidPropertyGroupsService());\n    serviceContainer.register(\"propertyService\", new BaseCustomWebComponentPropertiesService(true));\n    serviceContainer.register(\"designItemService\", new DesignItemService());\n    serviceContainer.register(\"deletionService\", new DeletionService());\n\n    serviceContainer.register(\"undoService\", (designerCanvas: IDesignerCanvas) => new UndoService(designerCanvas));\n    serviceContainer.register(\"selectionService\", (designerCanvas: IDesignerCanvas) => new SelectionService(designerCanvas, false));\n    serviceContainer.register(\"designItemDocumentPositionService\", (designerCanvas: IDesignerCanvas) => new DesignItemDocumentPositionService(designerCanvas));\n\n    serviceContainer.instanceServiceContainerCreatedCallbacks.push(instanceServiceContainer => {\n        const designerCanvas = instanceServiceContainer.designerCanvas;\n        elementsService.setInstanceServiceContainer(instanceServiceContainer);\n        instanceServiceContainer.selectionService.setSelectedElements([designerCanvas.rootDesignItem]);\n        const previousRaiseDesignItemsChanged = designerCanvas.raiseDesignItemsChanged.bind(designerCanvas);\n        designerCanvas.raiseDesignItemsChanged = (designItems, action, operationFinished) => {\n            previousRaiseDesignItemsChanged(designItems, action, operationFinished);\n            if (action === \"place\" || action === \"resize\")\n                rerouteConnectedMermaidEdges(instanceServiceContainer, designItems, operationFinished);\n        };\n    });\n\n    serviceContainer.designerExtensions.set(ExtensionType.Permanent, []);\n    serviceContainer.designerExtensions.set(ExtensionType.PrimarySelection, [\n        new class extends ElementDragTitleExtensionProvider {\n            override shouldExtend(extensionManager: any, designerView: any, designItem: any) {\n                if (designItem.element?.localName === \"mermaid-edge\")\n                    return false;\n                return super.shouldExtend(extensionManager, designerView, designItem);\n            }\n        }(),\n        new class extends PositionExtensionProvider {\n            override shouldExtend(extensionManager: any, designerView: any, designItem: any) {\n                if (designItem.element?.localName === \"mermaid-edge\")\n                    return false;\n                return super.shouldExtend(extensionManager, designerView, designItem);\n            }\n        }(),\n        new class extends ResizeExtensionProvider {\n            override shouldExtend(extensionManager: any, designerView: any, designItem: any) {\n                if (designItem.element?.localName === \"mermaid-edge\")\n                    return false;\n                return super.shouldExtend(extensionManager, designerView, designItem);\n            }\n        }(true)\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.Selection, [\n        new SelectionDefaultExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.PrimarySelectionContainer, []);\n    serviceContainer.designerExtensions.set(ExtensionType.MouseOver, [\n        new HighlightElementExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerDrag, [\n        new GrayOutExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerDragOverAndCanBeEntered, [\n        new AltToEnterContainerExtensionProvider(),\n        new GrayOutDragOverContainerExtensionProvider(),\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerExternalDragOverAndCanBeEntered, [\n        new GrayOutDragOverContainerExtensionProvider(),\n    ]);\n\n    serviceContainer.designerTools.set(NamedTools.Pointer, new PointerTool());\n    serviceContainer.designerTools.set(NamedTools.DrawSelection, new RectangleSelectorTool());\n    serviceContainer.designerTools.set(NamedTools.Zoom, new ZoomTool());\n    serviceContainer.designerTools.set(NamedTools.Pan, new PanTool());\n    serviceContainer.designerTools.set(NamedTools.RectangleSelector, new RectangleSelectorTool());\n    serviceContainer.designerTools.set(NamedTools.MagicWandSelector, new MagicWandSelectorTool());\n    serviceContainer.designerTools.set(\"MermaidFlow\", new ConnectMermaidNodesTool());\n\n    serviceContainer.designerContextMenuExtensions = [\n        new CopyPasteContextMenu(),\n        new ZMoveContextMenu(),\n        new MultipleItemsSelectedContextMenu(),\n        new ItemsBelowContextMenu()\n    ];\n\n    serviceContainer.designViewToolbarButtons.push(\n        new PointerToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new SelectorToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new SimpleToolButtonProvider(\"MermaidFlow\", mermaidFlowIcon),\n        new SeperatorToolProvider(22),\n        new ZoomToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new TransformToolButtonProvider()\n    );\n\n    serviceContainer.config.demoViewWidget = MermaidDemoView;\n    serviceContainer.register(\"elementsService\", elementsService);\n\n    return serviceContainer;\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/toolbar/ConnectMermaidNodesTool.ts",
    "content": "import { DesignItem, EventNames, IDesignerCanvas, InsertAction, ITool, OverlayLayer, ServiceContainer } from \"@node-projects/web-component-designer\";\nimport { encodeWaypoints, FlowchartDirection, getAnchor, Rect, routeBetweenBounds, routeBetweenPoints, pathDataFromWaypoints } from \"../services/mermaidGeometry.js\";\nimport { MermaidNode } from \"../widgets/mermaid-node.js\";\nimport { MermaidRequirementNode } from \"../widgets/mermaid-requirement-node.js\";\nimport { getMermaidDocumentDiagramType } from \"../services/MermaidDocumentPropertiesService.js\";\n\nfunction createIcon(markup: string) {\n    return `data:image/svg+xml,${encodeURIComponent(`<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">${markup}</svg>`)}`;\n}\n\nfunction findNode(event: PointerEvent, currentElement: Element | null | undefined) {\n    if (currentElement instanceof HTMLElement && isConnectableNode(currentElement))\n        return currentElement;\n\n    for (const part of event.composedPath()) {\n        if (part instanceof HTMLElement && isConnectableNode(part))\n            return part;\n    }\n\n    return null;\n}\n\nfunction isConnectableNode(element: HTMLElement) {\n    return element.localName === MermaidNode.is || element.localName === MermaidRequirementNode.is;\n}\n\nfunction getElementBounds(designerCanvas: IDesignerCanvas, element: HTMLElement): Rect {\n    const canvasRect = designerCanvas.canvas.getBoundingClientRect();\n    const elementRect = element.getBoundingClientRect();\n    const zoom = designerCanvas.zoomFactor ?? 1;\n\n    return {\n        x: (elementRect.left - canvasRect.left) / zoom,\n        y: (elementRect.top - canvasRect.top) / zoom,\n        width: elementRect.width / zoom,\n        height: elementRect.height / zoom,\n    };\n}\n\nfunction getNodeId(element: HTMLElement) {\n    if (element.localName === MermaidRequirementNode.is) {\n        let id = element.getAttribute(\"label\");\n        if (!id) {\n            id = `Req_${Math.random().toString(36).slice(2, 8)}`;\n            element.setAttribute(\"label\", id);\n        }\n        return id;\n    }\n\n    let id = element.getAttribute(\"node-id\");\n    if (!id) {\n        id = `Node_${Math.random().toString(36).slice(2, 8)}`;\n        element.setAttribute(\"node-id\", id);\n    }\n    return id;\n}\n\nexport const mermaidFlowIcon = createIcon('<path d=\"M4 12h12\"/><path d=\"M12 8l4 4-4 4\"/>');\n\nexport class ConnectMermaidNodesTool implements ITool {\n    readonly cursor = \"crosshair\";\n\n    private _captureElement?: Element;\n    private _pointerId?: number;\n    private _previewPath?: SVGPathElement;\n    private _sourceElement?: HTMLElement;\n    private _sourceBounds?: Rect;\n\n    activated(_serviceContainer: ServiceContainer) {\n    }\n\n    dispose() {\n    }\n\n    pointerEventHandler(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n        switch (event.type) {\n            case EventNames.PointerDown:\n                this._begin(designerCanvas, event, currentElement);\n                break;\n            case EventNames.PointerMove:\n                this._update(designerCanvas, event, currentElement);\n                break;\n            case EventNames.PointerUp:\n                this._finish(designerCanvas, event, currentElement);\n                break;\n        }\n    }\n\n    keyboardEventHandler(designerCanvas: IDesignerCanvas, event: KeyboardEvent) {\n        if (event.key === \"Escape\")\n            this._cleanup(designerCanvas, true);\n    }\n\n    private _begin(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n        const source = findNode(event, currentElement);\n        if (!source)\n            return;\n\n        this._sourceElement = source;\n        this._sourceBounds = getElementBounds(designerCanvas, source);\n        this._captureElement = event.target as Element;\n        this._pointerId = event.pointerId;\n        this._captureElement?.setPointerCapture?.(this._pointerId);\n        designerCanvas.captureActiveTool(this);\n\n        this._previewPath = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n        this._previewPath.setAttribute(\"fill\", \"none\");\n        this._previewPath.setAttribute(\"stroke\", \"#333\");\n        this._previewPath.setAttribute(\"stroke-width\", \"2.5\");\n        designerCanvas.overlayLayer.addOverlay(this.constructor.name, this._previewPath, OverlayLayer.Foreground);\n        this._update(designerCanvas, event, currentElement);\n        event.preventDefault();\n        event.stopPropagation();\n    }\n\n    private _update(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n        if (!this._sourceBounds || !this._previewPath)\n            return;\n\n        const pointer = designerCanvas.getNormalizedEventCoordinates(event);\n        const target = findNode(event, currentElement);\n        const direction = this._getDirection();\n        const waypoints = target && target !== this._sourceElement\n            ? routeBetweenBounds(this._sourceBounds, getElementBounds(designerCanvas, target), direction)\n            : routeBetweenPoints(getAnchor(this._sourceBounds, pointer), pointer);\n\n        this._previewPath.setAttribute(\"d\", pathDataFromWaypoints(waypoints));\n        event.preventDefault();\n        event.stopPropagation();\n    }\n\n    private _finish(designerCanvas: IDesignerCanvas, event: PointerEvent, currentElement: Element) {\n        if (!this._sourceElement || !this._sourceBounds)\n            return;\n\n        const target = findNode(event, currentElement);\n        if (!target || target === this._sourceElement) {\n            this._cleanup(designerCanvas, true);\n            event.preventDefault();\n            event.stopPropagation();\n            return;\n        }\n\n        const direction = this._getDirection(target);\n        const waypoints = routeBetweenBounds(this._sourceBounds, getElementBounds(designerCanvas, target), direction);\n        const edge = document.createElement(this._getRelationshipTag(designerCanvas)) as HTMLElement;\n        edge.style.position = \"absolute\";\n        edge.style.zIndex = \"4\";\n        edge.setAttribute(\"from\", getNodeId(this._sourceElement));\n        edge.setAttribute(\"to\", getNodeId(target));\n        if (edge.localName === \"mermaid-edge\")\n            edge.setAttribute(\"edge-type\", \"arrow\");\n        else\n            edge.setAttribute(\"relationship-type\", \"satisfies\");\n        if (direction && edge.localName === \"mermaid-edge\")\n            edge.setAttribute(\"diagram-direction\", direction);\n        edge.setAttribute(\"waypoints\", encodeWaypoints(waypoints));\n\n        const designItem = DesignItem.createDesignItemFromInstance(edge, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\n        designerCanvas.instanceServiceContainer.undoService.execute(\n            new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, designItem)\n        );\n        this._cleanup(designerCanvas, true);\n        event.preventDefault();\n        event.stopPropagation();\n    }\n\n    private _cleanup(designerCanvas: IDesignerCanvas, finished: boolean) {\n        if (this._previewPath) {\n            designerCanvas.overlayLayer.removeOverlay(this._previewPath);\n            this._previewPath = undefined;\n        }\n        if (this._captureElement && this._pointerId !== undefined)\n            this._captureElement.releasePointerCapture?.(this._pointerId);\n\n        this._captureElement = undefined;\n        this._pointerId = undefined;\n        this._sourceElement = undefined;\n        this._sourceBounds = undefined;\n        designerCanvas.releaseActiveTool();\n        if (finished)\n            designerCanvas.serviceContainer.globalContext.finishedWithTool(this);\n    }\n\n    private _getDirection(target?: HTMLElement): FlowchartDirection {\n        const direction = this._sourceElement?.getAttribute(\"diagram-direction\") ?? target?.getAttribute(\"diagram-direction\");\n        if (direction === \"TB\" || direction === \"TD\" || direction === \"BT\" || direction === \"RL\" || direction === \"LR\")\n            return direction;\n        return undefined;\n    }\n\n    private _getRelationshipTag(designerCanvas: IDesignerCanvas) {\n        return getMermaidDocumentDiagramType(designerCanvas.rootDesignItem) === \"requirementDiagram\"\n            ? \"mermaid-requirement-relationship\"\n            : \"mermaid-edge\";\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/elements.json",
    "content": "{\n    \"elements\": [\n        {\n            \"name\": \"node\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"120px\",\n            \"defaultHeight\": \"54px\",\n            \"defaultAttributes\": {\n                \"node-id\": \"A\",\n                \"label\": \"Node\",\n                \"shape\": \"rectangle\"\n            }\n        },\n        {\n            \"name\": \"decision\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"120px\",\n            \"defaultHeight\": \"80px\",\n            \"defaultAttributes\": {\n                \"node-id\": \"Decision\",\n                \"label\": \"Decision\",\n                \"shape\": \"decision\"\n            }\n        },\n        {\n            \"name\": \"subgraph\",\n            \"tag\": \"mermaid-subgraph\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"360px\",\n            \"defaultHeight\": \"240px\",\n            \"defaultAttributes\": {\n                \"subgraph-id\": \"Group\",\n                \"title\": \"Group\"\n            }\n        },\n        {\n            \"name\": \"document\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"64px\",\n            \"defaultAttributes\": { \"node-id\": \"Document\", \"label\": \"Document\", \"shape\": \"doc\" }\n        },\n        {\n            \"name\": \"database\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"120px\",\n            \"defaultHeight\": \"64px\",\n            \"defaultAttributes\": { \"node-id\": \"Database\", \"label\": \"Database\", \"shape\": \"cylinder\" }\n        },\n        {\n            \"name\": \"cloud\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"64px\",\n            \"defaultAttributes\": { \"node-id\": \"Cloud\", \"label\": \"Cloud\", \"shape\": \"cloud\" }\n        },\n        {\n            \"name\": \"junction\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"80px\",\n            \"defaultHeight\": \"80px\",\n            \"defaultAttributes\": { \"node-id\": \"Junction\", \"label\": \"\", \"shape\": \"f-circ\" }\n        },\n        {\n            \"name\": \"fork/join\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"28px\",\n            \"defaultAttributes\": { \"node-id\": \"Fork\", \"label\": \"\", \"shape\": \"fork\" }\n        },\n        {\n            \"name\": \"manual input\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"60px\",\n            \"defaultAttributes\": { \"node-id\": \"ManualInput\", \"label\": \"Manual Input\", \"shape\": \"sl-rect\" }\n        },\n        {\n            \"name\": \"data store\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"60px\",\n            \"defaultAttributes\": { \"node-id\": \"DataStore\", \"label\": \"Data Store\", \"shape\": \"datastore\" }\n        },\n        {\n            \"name\": \"text block\",\n            \"tag\": \"mermaid-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"flowchart\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"54px\",\n            \"defaultAttributes\": { \"node-id\": \"Text\", \"label\": \"Text\", \"shape\": \"text\" }\n        },\n        {\n            \"name\": \"mindmap node\",\n            \"tag\": \"mermaid-mindmap-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"mindmap\"],\n            \"defaultWidth\": \"150px\",\n            \"defaultHeight\": \"44px\",\n            \"defaultAttributes\": {\n                \"mindmap-id\": \"Node\",\n                \"label\": \"Node\",\n                \"shape\": \"default\"\n            }\n        },\n        {\n            \"name\": \"sequence participant\",\n            \"tag\": \"mermaid-sequence-participant\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"sequenceDiagram\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"44px\",\n            \"defaultAttributes\": {\n                \"participant-id\": \"Alice\",\n                \"label\": \"Alice\",\n                \"participant-type\": \"participant\"\n            }\n        },\n        {\n            \"name\": \"sequence actor\",\n            \"tag\": \"mermaid-sequence-participant\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"sequenceDiagram\"],\n            \"defaultWidth\": \"140px\",\n            \"defaultHeight\": \"44px\",\n            \"defaultAttributes\": {\n                \"participant-id\": \"Actor\",\n                \"label\": \"Actor\",\n                \"participant-type\": \"actor\"\n            }\n        },\n        {\n            \"name\": \"sequence message\",\n            \"tag\": \"mermaid-sequence-message\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"sequenceDiagram\"],\n            \"defaultWidth\": \"220px\",\n            \"defaultHeight\": \"36px\",\n            \"defaultAttributes\": {\n                \"from\": \"Alice\",\n                \"to\": \"Bob\",\n                \"label\": \"Message\",\n                \"message-type\": \"solid\",\n                \"connector\": \"->>\"\n            }\n        },\n        {\n            \"name\": \"requirement\",\n            \"tag\": \"mermaid-requirement-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"requirementDiagram\"],\n            \"defaultWidth\": \"220px\",\n            \"defaultHeight\": \"160px\",\n            \"defaultAttributes\": {\n                \"node-kind\": \"requirement\",\n                \"label\": \"Req\",\n                \"requirement-type\": \"requirement\",\n                \"requirement-id\": \"1\",\n                \"text\": \"Requirement text\",\n                \"risk\": \"low\",\n                \"verify-method\": \"test\"\n            }\n        },\n        {\n            \"name\": \"element\",\n            \"tag\": \"mermaid-requirement-node\",\n            \"type\": \"polymer\",\n            \"diagramTypes\": [\"requirementDiagram\"],\n            \"defaultWidth\": \"170px\",\n            \"defaultHeight\": \"84px\",\n            \"defaultAttributes\": {\n                \"node-kind\": \"element\",\n                \"label\": \"Element\",\n                \"element-type\": \"simulation\"\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-edge.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { boundsFromWaypoints, decodeWaypoints, midpointFromWaypoints, pathDataFromWaypoints } from \"../services/mermaidGeometry.js\";\nimport { escapeLabel, sanitizeId } from \"./mermaid-node.js\";\n\nenum MermaidEdgeType {\n    arrow = \"arrow\",\n    open = \"open\",\n    dotted = \"dotted\",\n    thick = \"thick\",\n    invisible = \"invisible\",\n    circle = \"circle\",\n    cross = \"cross\",\n    multi = \"multi\",\n}\n\nexport class MermaidEdge extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    :host {\n        display: block;\n        overflow: visible;\n        pointer-events: auto;\n        position: absolute;\n        z-index: 4;\n    }\n\n    svg {\n        height: 100%;\n        overflow: visible;\n        width: 100%;\n    }\n\n    path {\n        fill: none;\n        stroke: #333;\n        stroke-linecap: round;\n        stroke-linejoin: round;\n        stroke-width: 2.5;\n        vector-effect: non-scaling-stroke;\n    }\n\n    #marker-shape {\n        fill: #333;\n    }\n\n    text {\n        fill: #333;\n        font-family: Arial, sans-serif;\n        font-size: 12px;\n        pointer-events: none;\n    }\n    `;\n\n    static override readonly template = html`\n    <svg id=\"svg\" viewBox=\"0 0 10 10\" preserveAspectRatio=\"none\">\n        <defs>\n            <marker id=\"marker\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"10.5\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <path id=\"marker-shape\" d=\"M 1 1 L 10.5 6 L 1 11 z\"></path>\n            </marker>\n            <marker id=\"marker-start\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"1.5\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <path id=\"marker-start-shape\" d=\"M 11 1 L 1.5 6 L 11 11 z\"></path>\n            </marker>\n            <marker id=\"circle-marker\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"10\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <circle cx=\"6\" cy=\"6\" r=\"4\" fill=\"white\" stroke=\"#333\" stroke-width=\"2\"></circle>\n            </marker>\n            <marker id=\"cross-marker\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"10\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <path d=\"M 3 3 L 9 9 M 9 3 L 3 9\" fill=\"none\" stroke=\"#333\" stroke-width=\"2\"></path>\n            </marker>\n        </defs>\n        <path id=\"path\"></path>\n        <text id=\"label\"></text>\n    </svg>`;\n\n    static readonly is = \"mermaid-edge\";\n\n    public from: string = \"A\";\n    public to: string = \"B\";\n    public label: string = \"\";\n    public edgeType: MermaidEdgeType = MermaidEdgeType.arrow;\n    public connector: string = \"-->\";\n    public edgeId: string = \"\";\n    public animation: string = \"\";\n    public waypoints: string = \"\";\n    private _previewWaypoints: string;\n\n    private _label: SVGTextElement;\n    private _path: SVGPathElement;\n    private _svg: SVGSVGElement;\n\n    static readonly properties = {\n        from: String,\n        to: String,\n        label: String,\n        edgeType: MermaidEdgeType,\n        connector: String,\n        edgeId: String,\n        animation: String,\n        waypoints: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._label = this._getDomElement<SVGTextElement>(\"label\");\n        this._path = this._getDomElement<SVGPathElement>(\"path\");\n        this._svg = this._getDomElement<SVGSVGElement>(\"svg\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    public setPreviewWaypoints(waypoints: string | null) {\n        this._previewWaypoints = waypoints;\n        this._render();\n    }\n\n    private _render() {\n        let waypoints = decodeWaypoints(this._previewWaypoints ?? this.getAttribute(\"waypoints\") ?? this.waypoints);\n        if (waypoints.length < 2)\n            waypoints = [{ x: 0, y: 12 }, { x: 180, y: 12 }];\n\n        const padding = 16;\n        const minCrossAxisSize = 24;\n        const bounds = boundsFromWaypoints(waypoints);\n        const width = Math.max(minCrossAxisSize, Math.round(bounds.width + padding * 2));\n        const height = Math.max(minCrossAxisSize, Math.round(bounds.height + padding * 2));\n        const offsetX = (width - bounds.width) / 2;\n        const offsetY = (height - bounds.height) / 2;\n\n        this.style.left = `${Math.round(bounds.x - offsetX)}px`;\n        this.style.top = `${Math.round(bounds.y - offsetY)}px`;\n        this.style.width = `${width}px`;\n        this.style.height = `${height}px`;\n\n        const shifted = waypoints.map(point => ({ x: point.x - bounds.x + offsetX, y: point.y - bounds.y + offsetY }));\n        const middle = midpointFromWaypoints(shifted);\n\n        this._svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n        this._path.setAttribute(\"d\", pathDataFromWaypoints(shifted));\n        const edgeType = this.getAttribute(\"edge-type\") as MermaidEdgeType ?? this.edgeType;\n        const connector = this.getAttribute(\"connector\") ?? this.connector;\n        const label = this.getAttribute(\"label\") ?? this.label ?? \"\";\n        this._path.setAttribute(\"marker-start\", connector.startsWith(\"<\") || edgeType === MermaidEdgeType.multi ? \"url(#marker-start)\" : \"\");\n        this._path.setAttribute(\"marker-end\", getMarkerEnd(edgeType, connector));\n        this._path.style.strokeDasharray = edgeType === MermaidEdgeType.dotted ? \"4 4\" : \"\";\n        this._path.style.strokeWidth = edgeType === MermaidEdgeType.thick ? \"4\" : \"2.5\";\n        this._path.style.opacity = edgeType === MermaidEdgeType.invisible ? \"0\" : \"\";\n        this._label.textContent = label;\n        this._label.style.display = label ? \"block\" : \"none\";\n        this._label.setAttribute(\"x\", middle.x.toString());\n        this._label.setAttribute(\"y\", (middle.y - 8).toString());\n        this._label.setAttribute(\"text-anchor\", \"middle\");\n    }\n\n    public createMermaid() {\n        const from = sanitizeId(this.getAttribute(\"from\") ?? this.from);\n        const to = sanitizeId(this.getAttribute(\"to\") ?? this.to);\n        const label = this.getAttribute(\"label\") ?? this.label;\n        const explicitConnector = this.getAttribute(\"connector\") ?? this.connector;\n        const connector = explicitConnector ? { plain: explicitConnector, label: explicitConnector } : getConnector(this.getAttribute(\"edge-type\") as MermaidEdgeType ?? this.edgeType);\n        const rawEdgeId = this.getAttribute(\"edge-id\") ?? this.edgeId;\n        const edgeId = rawEdgeId ? sanitizeId(rawEdgeId) : \"\";\n        const prefix = edgeId ? edgeId + \"@\" : \"\";\n\n        if (label)\n            return `${from} ${prefix}${connector.label}|${escapeLabel(label)}| ${to}`;\n\n        return `${from} ${prefix}${connector.plain} ${to}`;\n    }\n}\n\nfunction getMarkerEnd(edgeType: MermaidEdgeType, connector: string) {\n    if (edgeType === MermaidEdgeType.open || edgeType === MermaidEdgeType.invisible || connector.endsWith(\"---\") || connector.endsWith(\"===\") || connector.endsWith(\"-.-\"))\n        return \"\";\n    if (edgeType === MermaidEdgeType.circle || connector.includes(\"o\"))\n        return \"url(#circle-marker)\";\n    if (edgeType === MermaidEdgeType.cross || connector.includes(\"x\"))\n        return \"url(#cross-marker)\";\n    return \"url(#marker)\";\n}\n\nfunction getConnector(edgeType: MermaidEdgeType) {\n    switch (edgeType) {\n        case MermaidEdgeType.open:\n            return { plain: \"---\", label: \"---\" };\n        case MermaidEdgeType.dotted:\n            return { plain: \"-.->\", label: \"-.->\" };\n        case MermaidEdgeType.thick:\n            return { plain: \"==>\", label: \"==>\" };\n        case MermaidEdgeType.invisible:\n            return { plain: \"~~~\", label: \"~~~\" };\n        case MermaidEdgeType.circle:\n            return { plain: \"--o\", label: \"--o\" };\n        case MermaidEdgeType.cross:\n            return { plain: \"--x\", label: \"--x\" };\n        case MermaidEdgeType.multi:\n            return { plain: \"<-->\", label: \"<-->\" };\n        default:\n            return { plain: \"-->\", label: \"-->\" };\n    }\n}\n\ncustomElements.define(MermaidEdge.is, MermaidEdge);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-flowchart-directive.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\n\nexport class MermaidFlowchartDirective extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    :host {\n        background: #fffbe6;\n        border: 1px solid #d6b656;\n        color: #6b5600;\n        display: block;\n        font-family: Arial, sans-serif;\n        font-size: 11px;\n        overflow: hidden;\n        padding: 4px 6px;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n    }\n    `;\n\n    static override readonly template = html`<span id=\"text\"></span>`;\n\n    static readonly is = \"mermaid-flowchart-directive\";\n\n    public line: string = \"\";\n\n    private _text: HTMLSpanElement;\n\n    static readonly properties = {\n        line: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._text = this._getDomElement<HTMLSpanElement>(\"text\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    private _render() {\n        this._text.textContent = this.getAttribute(\"line\") ?? this.line;\n    }\n\n    public createMermaid() {\n        return this.getAttribute(\"line\") ?? this.line;\n    }\n}\n\ncustomElements.define(MermaidFlowchartDirective.is, MermaidFlowchartDirective);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-mindmap-node.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { formatMermaidLabel, renderMarkdownLabel, sanitizeId } from \"./mermaid-node.js\";\n\nenum MermaidMindmapNodeShape {\n    default = \"default\",\n    square = \"square\",\n    round = \"round\",\n    circle = \"circle\",\n    bang = \"bang\",\n    cloud = \"cloud\",\n    hexagon = \"hexagon\",\n}\n\nexport class MermaidMindmapNode extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    :host {\n        display: block;\n        overflow: visible;\n    }\n\n    #connections {\n        height: 100%;\n        left: 0;\n        overflow: visible;\n        pointer-events: none;\n        position: absolute;\n        top: 0;\n        width: 100%;\n        z-index: 0;\n    }\n\n    #node {\n        align-items: center;\n        background: white;\n        border: 2px solid #333;\n        color: #111;\n        display: flex;\n        font-family: Arial, sans-serif;\n        font-size: 14px;\n        height: 100%;\n        justify-content: center;\n        min-height: 34px;\n        min-width: 84px;\n        overflow: hidden;\n        padding: 6px 12px;\n        pointer-events: none;\n        position: relative;\n        text-align: center;\n        width: 100%;\n        z-index: 1;\n    }\n\n    ::slotted(mermaid-mindmap-node) {\n        z-index: 1;\n    }\n\n    #node[data-shape=\"circle\"] {\n        border-radius: 50%;\n    }\n\n    #node[data-shape=\"round\"] {\n        border-radius: 12px;\n    }\n\n    #node[data-shape=\"bang\"] {\n        clip-path: polygon(50% 0, 61% 34%, 98% 35%, 68% 56%, 79% 91%, 50% 70%, 21% 91%, 32% 56%, 2% 35%, 39% 34%);\n        padding: 16px;\n    }\n\n    #node[data-shape=\"cloud\"] {\n        border-radius: 999px;\n        box-shadow: 12px 0 0 -2px white, 13px 0 0 0 #333, -12px 0 0 -2px white, -13px 0 0 0 #333;\n    }\n\n    #node[data-shape=\"hexagon\"] {\n        clip-path: polygon(20% 0, 80% 0, 100% 50%, 80% 100%, 20% 100%, 0 50%);\n        padding: 8px 22px;\n    }\n    `;\n\n    static override readonly template = html`<svg id=\"connections\"></svg><div id=\"node\"></div><slot id=\"children\"></slot>`;\n\n    static readonly is = \"mermaid-mindmap-node\";\n\n    public mindmapId: string = \"Root\";\n    public label: string = \"Node\";\n    public shape: MermaidMindmapNodeShape = MermaidMindmapNodeShape.default;\n\n    private _node: HTMLDivElement;\n    private _connections: SVGSVGElement;\n    private _slot: HTMLSlotElement;\n    private _childObserver = new MutationObserver(() => this._renderConnections());\n    private _resizeObserver = new ResizeObserver(() => this._renderConnections());\n\n    static readonly properties = {\n        mindmapId: String,\n        label: String,\n        shape: MermaidMindmapNodeShape,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._node = this._getDomElement<HTMLDivElement>(\"node\");\n        this._connections = this._getDomElement<SVGSVGElement>(\"connections\");\n        this._slot = this._getDomElement<HTMLSlotElement>(\"children\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n        this._slot.addEventListener(\"slotchange\", () => this._observeChildren());\n        this._resizeObserver.observe(this);\n        this._observeChildren();\n        requestAnimationFrame(() => this._renderConnections());\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    private _render() {\n        const label = this.getAttribute(\"label\") ?? this.label ?? this.getAttribute(\"mindmap-id\") ?? this.mindmapId;\n        const shape = this.getAttribute(\"shape\") as MermaidMindmapNodeShape ?? this.shape;\n        renderMarkdownLabel(this._node, label);\n        this._node.dataset.shape = shape;\n        this._renderConnections();\n    }\n\n    private _observeChildren() {\n        this._childObserver.disconnect();\n        this._resizeObserver.disconnect();\n        this._resizeObserver.observe(this);\n        for (const child of this._getMindmapChildren()) {\n            this._childObserver.observe(child, { attributeFilter: [\"style\"], attributes: true });\n            this._resizeObserver.observe(child);\n        }\n        this._renderConnections();\n    }\n\n    private _renderConnections() {\n        this._connections.replaceChildren();\n        const children = this._getMindmapChildren();\n        if (!children.length)\n            return;\n\n        const hostWidth = this.offsetWidth;\n        const hostHeight = this.offsetHeight;\n        const source = { x: hostWidth / 2, y: hostHeight / 2 };\n        const strokeWidth = getConnectionStrokeWidth(this);\n\n        for (const child of children) {\n            const childLeft = Number.parseFloat(child.style.left || \"0\");\n            const childTop = Number.parseFloat(child.style.top || \"0\");\n            const childWidth = child.offsetWidth || Number.parseFloat(child.style.width || \"0\");\n            const childHeight = child.offsetHeight || Number.parseFloat(child.style.height || \"0\");\n            const target = { x: childLeft + childWidth / 2, y: childTop + childHeight / 2 };\n            const direction = target.x >= source.x ? 1 : -1;\n            const start = { x: source.x + direction * hostWidth / 2, y: source.y };\n            const end = { x: target.x - direction * childWidth / 2, y: target.y };\n            const controlDistance = Math.max(24, Math.abs(end.x - start.x) * 0.45);\n            const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n            path.setAttribute(\"d\", `M ${start.x} ${start.y} C ${start.x + direction * controlDistance} ${start.y}, ${end.x - direction * controlDistance} ${end.y}, ${end.x} ${end.y}`);\n            path.setAttribute(\"fill\", \"none\");\n            path.setAttribute(\"stroke\", \"#666\");\n            path.setAttribute(\"stroke-linecap\", \"round\");\n            path.setAttribute(\"stroke-width\", strokeWidth.toString());\n            this._connections.appendChild(path);\n        }\n    }\n\n    private _getMindmapChildren() {\n        return this._slot.assignedElements().filter((element): element is MermaidMindmapNode => element.localName === MermaidMindmapNode.is);\n    }\n\n    public createMermaid() {\n        const id = sanitizeId(this.getAttribute(\"mindmap-id\") ?? this.mindmapId);\n        const label = formatMermaidLabel((this.getAttribute(\"label\") ?? this.label) || id);\n        const shape = this.getAttribute(\"shape\") as MermaidMindmapNodeShape ?? this.shape;\n\n        switch (shape) {\n            case MermaidMindmapNodeShape.square:\n                return `${id}[${label}]`;\n            case MermaidMindmapNodeShape.round:\n                return `${id}(${label})`;\n            case MermaidMindmapNodeShape.circle:\n                return `${id}((${label}))`;\n            case MermaidMindmapNodeShape.bang:\n                return `${id}))${label}((`;\n            case MermaidMindmapNodeShape.cloud:\n                return `${id})${label}(`;\n            case MermaidMindmapNodeShape.hexagon:\n                return `${id}{{${label}}}`;\n            default:\n                return label;\n        }\n    }\n}\n\ncustomElements.define(MermaidMindmapNode.is, MermaidMindmapNode);\n\nfunction getConnectionStrokeWidth(element: Element) {\n    let depth = 0;\n    let current = element.parentElement;\n    while (current) {\n        if (current.localName === MermaidMindmapNode.is)\n            depth++;\n        current = current.parentElement;\n    }\n    return Math.max(1.5, 5 - depth * 1.2);\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-node.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\n\nenum MermaidNodeShape {\n    rectangle = \"rectangle\",\n    round = \"round\",\n    stadium = \"stadium\",\n    circle = \"circle\",\n    decision = \"decision\",\n    subroutine = \"subroutine\",\n    cylinder = \"cylinder\",\n    asymmetric = \"asymmetric\",\n    hexagon = \"hexagon\",\n    parallelogram = \"parallelogram\",\n    parallelogramAlt = \"parallelogramAlt\",\n    trapezoid = \"trapezoid\",\n    trapezoidAlt = \"trapezoidAlt\",\n    doubleCircle = \"doubleCircle\",\n    bang = \"bang\",\n    cloud = \"cloud\",\n    hourglass = \"hourglass\",\n    bolt = \"bolt\",\n    brace = \"brace\",\n    braceRight = \"brace-r\",\n    braces = \"braces\",\n    dataStore = \"datastore\",\n    delay = \"delay\",\n    horizontalCylinder = \"h-cyl\",\n    linedCylinder = \"lin-cyl\",\n    curvedTrapezoid = \"curv-trap\",\n    dividedRectangle = \"div-rect\",\n    document = \"doc\",\n    triangle = \"tri\",\n    fork = \"fork\",\n    windowPane = \"win-pane\",\n    filledCircle = \"f-circ\",\n    linedDocument = \"lin-doc\",\n    linedRectangle = \"lin-rect\",\n    notchedPentagon = \"notch-pent\",\n    flippedTriangle = \"flip-tri\",\n    slopedRectangle = \"sl-rect\",\n    stackedDocument = \"docs\",\n    stackedRectangle = \"st-rect\",\n    flag = \"flag\",\n    bowTieRectangle = \"bow-rect\",\n    framedCircle = \"fr-circ\",\n    crossedCircle = \"cross-circ\",\n    taggedDocument = \"tag-doc\",\n    taggedRectangle = \"tag-rect\",\n    text = \"text\",\n    card = \"notch-rect\",\n}\n\nexport class MermaidNode extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    #node {\n        align-items: center;\n        background: white;\n        border: 2px solid #333;\n        color: #111;\n        display: flex;\n        font-family: Arial, sans-serif;\n        font-size: 14px;\n        height: 100%;\n        justify-content: center;\n        min-height: 32px;\n        min-width: 64px;\n        overflow: hidden;\n        white-space: pre-line;\n        padding: 6px 10px;\n        pointer-events: none;\n        text-align: center;\n        width: 100%;\n    }\n\n    #node[data-shape=\"decision\"] {\n        clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);\n        padding: 10px 22px;\n    }\n\n    #node[data-shape=\"subroutine\"] {\n        box-shadow: inset 8px 0 0 white, inset 10px 0 0 #333, inset -8px 0 0 white, inset -10px 0 0 #333;\n    }\n\n    #node[data-shape=\"cylinder\"] {\n        border-radius: 50% / 14%;\n    }\n\n    #node[data-shape=\"asymmetric\"] {\n        clip-path: polygon(0 0, 86% 0, 100% 50%, 86% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"hexagon\"] {\n        clip-path: polygon(20% 0, 80% 0, 100% 50%, 80% 100%, 20% 100%, 0 50%);\n    }\n\n    #node[data-shape=\"parallelogram\"] {\n        clip-path: polygon(14% 0, 100% 0, 86% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"parallelogramAlt\"] {\n        clip-path: polygon(0 0, 86% 0, 100% 100%, 14% 100%);\n    }\n\n    #node[data-shape=\"trapezoid\"] {\n        clip-path: polygon(14% 0, 86% 0, 100% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"trapezoidAlt\"] {\n        clip-path: polygon(0 0, 100% 0, 86% 100%, 14% 100%);\n    }\n\n    #node[data-shape=\"doubleCircle\"] {\n        border-radius: 50%;\n        box-shadow: inset 0 0 0 4px white, inset 0 0 0 6px #333;\n    }\n\n    #node[data-shape=\"bang\"] {\n        clip-path: polygon(50% 0, 61% 34%, 98% 35%, 68% 56%, 79% 91%, 50% 70%, 21% 91%, 32% 56%, 2% 35%, 39% 34%);\n        padding: 14px;\n    }\n\n    #node[data-shape=\"cloud\"] {\n        border-radius: 999px;\n        box-shadow: 12px 0 0 -2px white, 13px 0 0 0 #333, -12px 0 0 -2px white, -13px 0 0 0 #333;\n    }\n\n    #node[data-shape=\"hourglass\"] {\n        clip-path: polygon(0 0, 100% 0, 62% 50%, 100% 100%, 0 100%, 38% 50%);\n    }\n\n    #node[data-shape=\"bolt\"] {\n        clip-path: polygon(44% 0, 78% 0, 56% 38%, 82% 38%, 31% 100%, 45% 52%, 20% 52%);\n    }\n\n    #node[data-shape=\"brace\"],\n    #node[data-shape=\"brace-r\"],\n    #node[data-shape=\"braces\"] {\n        border-color: transparent;\n        font-size: 15px;\n    }\n\n    #node[data-shape=\"brace\"]::before,\n    #node[data-shape=\"braces\"]::before {\n        content: \"{\";\n        font-size: 42px;\n        line-height: 1;\n        margin-right: 6px;\n    }\n\n    #node[data-shape=\"brace-r\"]::after,\n    #node[data-shape=\"braces\"]::after {\n        content: \"}\";\n        font-size: 42px;\n        line-height: 1;\n        margin-left: 6px;\n    }\n\n    #node[data-shape=\"datastore\"] {\n        border-left-color: transparent;\n        border-right-color: transparent;\n    }\n\n    #node[data-shape=\"delay\"] {\n        border-radius: 0 999px 999px 0;\n    }\n\n    #node[data-shape=\"h-cyl\"],\n    #node[data-shape=\"lin-cyl\"] {\n        border-radius: 22% / 50%;\n    }\n\n    #node[data-shape=\"curv-trap\"] {\n        clip-path: polygon(10% 0, 100% 0, 90% 100%, 0 100%);\n        border-radius: 45% 8% 45% 8% / 16%;\n    }\n\n    #node[data-shape=\"div-rect\"],\n    #node[data-shape=\"lin-rect\"],\n    #node[data-shape=\"win-pane\"] {\n        box-shadow: inset 0 -16px 0 rgba(0, 0, 0, 0.08);\n    }\n\n    #node[data-shape=\"doc\"],\n    #node[data-shape=\"lin-doc\"],\n    #node[data-shape=\"docs\"],\n    #node[data-shape=\"tag-doc\"] {\n        border-radius: 0 0 16px 16px / 0 0 8px 8px;\n    }\n\n    #node[data-shape=\"tri\"] {\n        clip-path: polygon(50% 0, 100% 100%, 0 100%);\n        padding-top: 20px;\n    }\n\n    #node[data-shape=\"flip-tri\"] {\n        clip-path: polygon(0 0, 100% 0, 50% 100%);\n        padding-bottom: 20px;\n    }\n\n    #node[data-shape=\"fork\"] {\n        min-height: 16px;\n        background: #333;\n        color: white;\n    }\n\n    #node[data-shape=\"f-circ\"] {\n        background: #333;\n        border-radius: 50%;\n        color: white;\n    }\n\n    #node[data-shape=\"notch-pent\"] {\n        clip-path: polygon(12% 0, 88% 0, 100% 50%, 88% 100%, 12% 100%, 0 50%);\n    }\n\n    #node[data-shape=\"sl-rect\"] {\n        clip-path: polygon(12% 0, 100% 0, 88% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"flag\"] {\n        clip-path: polygon(0 0, 100% 0, 86% 50%, 100% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"bow-rect\"] {\n        clip-path: polygon(0 0, 35% 0, 50% 50%, 65% 0, 100% 0, 100% 100%, 65% 100%, 50% 50%, 35% 100%, 0 100%);\n    }\n\n    #node[data-shape=\"fr-circ\"] {\n        border-radius: 50%;\n        box-shadow: inset 0 0 0 4px white, inset 0 0 0 6px #333;\n    }\n\n    #node[data-shape=\"cross-circ\"] {\n        border-radius: 50%;\n    }\n\n    #node[data-shape=\"cross-circ\"]::before {\n        content: \"×\";\n        font-size: 28px;\n        margin-right: 4px;\n    }\n\n    #node[data-shape=\"tag-rect\"],\n    #node[data-shape=\"tag-doc\"],\n    #node[data-shape=\"notch-rect\"] {\n        clip-path: polygon(10% 0, 100% 0, 100% 100%, 10% 100%, 0 50%);\n    }\n\n    #node[data-shape=\"text\"] {\n        background: transparent;\n        border-color: transparent;\n    }\n    `;\n\n    static override readonly template = html`<div id=\"node\"></div>`;\n\n    static readonly is = \"mermaid-node\";\n\n    public nodeId: string = \"A\";\n    public label: string = \"Node\";\n    public shape: MermaidNodeShape = MermaidNodeShape.rectangle;\n\n    private _node: HTMLDivElement;\n\n    static readonly properties = {\n        nodeId: String,\n        label: String,\n        shape: MermaidNodeShape,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._node = this._getDomElement<HTMLDivElement>(\"node\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    private _render() {\n        const label = this.getAttribute(\"label\") ?? this.label;\n        const nodeId = this.getAttribute(\"node-id\") ?? this.nodeId;\n        const shape = this.getAttribute(\"shape\") as MermaidNodeShape ?? this.shape;\n        renderMarkdownLabel(this._node, label || nodeId);\n        this._node.dataset.shape = shape;\n        this._node.style.clipPath = \"\";\n        this._node.style.boxShadow = \"\";\n        switch (shape) {\n            case MermaidNodeShape.round:\n                this._node.style.borderRadius = \"12px\";\n                break;\n            case MermaidNodeShape.stadium:\n                this._node.style.borderRadius = \"999px\";\n                break;\n            case MermaidNodeShape.circle:\n            case MermaidNodeShape.doubleCircle:\n            case MermaidNodeShape.framedCircle:\n            case MermaidNodeShape.crossedCircle:\n            case MermaidNodeShape.filledCircle:\n                this._node.style.borderRadius = \"50%\";\n                break;\n            case MermaidNodeShape.cylinder:\n            case MermaidNodeShape.horizontalCylinder:\n            case MermaidNodeShape.linedCylinder:\n                this._node.style.borderRadius = \"50% / 14%\";\n                break;\n            default:\n                this._node.style.borderRadius = \"0\";\n                break;\n        }\n    }\n\n    public createMermaid() {\n        const id = sanitizeId(this.getAttribute(\"node-id\") ?? this.nodeId);\n        const label = formatMermaidLabel((this.getAttribute(\"label\") ?? this.label) || id);\n        const shape = this.getAttribute(\"shape\") as MermaidNodeShape ?? this.shape;\n\n        switch (shape) {\n            case MermaidNodeShape.round:\n                return `${id}(${label})`;\n            case MermaidNodeShape.stadium:\n                return `${id}([${label}])`;\n            case MermaidNodeShape.circle:\n                return `${id}((${label}))`;\n            case MermaidNodeShape.decision:\n                return `${id}{${label}}`;\n            case MermaidNodeShape.subroutine:\n                return `${id}[[${label}]]`;\n            case MermaidNodeShape.cylinder:\n                return `${id}[(${label})]`;\n            case MermaidNodeShape.asymmetric:\n                return `${id}>${label}]`;\n            case MermaidNodeShape.hexagon:\n                return `${id}{{${label}}}`;\n            case MermaidNodeShape.parallelogram:\n                return `${id}[/${label}/]`;\n            case MermaidNodeShape.parallelogramAlt:\n                return `${id}[\\\\${label}\\\\]`;\n            case MermaidNodeShape.trapezoid:\n                return `${id}[/${label}\\\\]`;\n            case MermaidNodeShape.trapezoidAlt:\n                return `${id}[\\\\${label}/]`;\n            case MermaidNodeShape.doubleCircle:\n                return `${id}(((${label})))`;\n            default:\n                if (isExpandedShape(shape))\n                    return `${id}@{ shape: ${shape}, label: ${label} }`;\n                return `${id}[${label}]`;\n        }\n    }\n}\n\nfunction isExpandedShape(shape: string) {\n    return !!shape && shape !== MermaidNodeShape.rectangle;\n}\n\nexport function sanitizeId(id: string) {\n    return (id || \"A\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n}\n\nexport function escapeLabel(label: string) {\n    return (label || \"\").replaceAll(\"\\\"\", \"#quot;\");\n}\n\nexport function formatMermaidLabel(label: string) {\n    const escaped = escapeLabel(label);\n    if (shouldWriteMarkdownLabel(escaped))\n        return \"\\\"`\" + escaped + \"`\\\"\";\n    return escaped.includes(\"`\") ? `\"${escaped}\"` : escaped;\n}\n\nexport function stripMarkdownLabel(label: string) {\n    return normalizeLabelLineBreaks((label || \"\").replace(/^`|`$/g, \"\"));\n}\n\nexport function renderMarkdownLabel(container: HTMLElement | SVGTextElement, label: string) {\n    container.replaceChildren();\n    const normalizedLabel = stripMarkdownLabel(label);\n    const lines = splitMarkdownLines(normalizedLabel);\n    for (let i = 0; i < lines.length; i++) {\n        if (i > 0)\n            container.appendChild(document.createElement(\"br\"));\n        renderInlineMarkdown(container, lines[i]);\n    }\n}\n\nfunction renderInlineMarkdown(container: Element, text: string) {\n    let position = 0;\n    while (position < text.length) {\n        const nextDelimiter = findNextMarkdownDelimiter(text, position);\n        if (!nextDelimiter) {\n            appendText(container, text.substring(position));\n            return;\n        }\n\n        if (nextDelimiter.index > position)\n            appendText(container, text.substring(position, nextDelimiter.index));\n\n        const end = text.indexOf(nextDelimiter.token, nextDelimiter.index + nextDelimiter.token.length);\n        if (end < 0) {\n            appendText(container, text.substring(nextDelimiter.index, nextDelimiter.index + nextDelimiter.token.length));\n            position = nextDelimiter.index + nextDelimiter.token.length;\n            continue;\n        }\n\n        const element = document.createElement(nextDelimiter.token.length === 2 ? \"strong\" : \"em\");\n        renderInlineMarkdown(element, text.substring(nextDelimiter.index + nextDelimiter.token.length, end));\n        container.appendChild(element);\n        position = end + nextDelimiter.token.length;\n    }\n}\n\nfunction findNextMarkdownDelimiter(text: string, start: number) {\n    const tokens = [\"**\", \"__\", \"*\", \"_\"];\n    let result: { token: string; index: number } = null;\n    for (const token of tokens) {\n        const index = text.indexOf(token, start);\n        if (index >= 0 && (!result || index < result.index || (index === result.index && token.length > result.token.length)))\n            result = { token, index };\n    }\n    return result;\n}\n\nfunction appendText(container: Element, text: string) {\n    if (text)\n        container.appendChild(document.createTextNode(text));\n}\n\nfunction splitMarkdownLines(text: string) {\n    const lines: string[] = [];\n    let start = 0;\n    for (let i = 0; i < text.length; i++) {\n        if (text[i] === \"\\n\") {\n            lines.push(text.substring(start, i));\n            start = i + 1;\n        }\n    }\n    lines.push(text.substring(start));\n    return lines;\n}\n\nfunction shouldWriteMarkdownLabel(label: string) {\n    return label.includes(\"\\n\") || label.includes(\"**\") || label.includes(\"_\") || label.includes(\"*\");\n}\n\nexport function normalizeLabelLineBreaks(label: string) {\n    return label.replaceAll(\"<br>\", \"\\n\").replaceAll(\"<br/>\", \"\\n\").replaceAll(\"<br />\", \"\\n\");\n}\n\ncustomElements.define(MermaidNode.is, MermaidNode);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-requirement-node.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { escapeLabel, renderMarkdownLabel, sanitizeId } from \"./mermaid-node.js\";\n\ntype RequirementNodeKind = \"requirement\" | \"element\";\n\nexport class MermaidRequirementNode extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    :host {\n        background: #fff;\n        border: 2px solid #333;\n        color: #111;\n        display: block;\n        font-family: Arial, sans-serif;\n        font-size: 12px;\n        overflow: hidden;\n    }\n\n    #header {\n        border-bottom: 1px solid #666;\n        padding: 6px 8px;\n        text-align: center;\n    }\n\n    #stereotype {\n        display: block;\n        font-size: 11px;\n        line-height: 1.25;\n        margin-bottom: 2px;\n    }\n\n    #name {\n        font-weight: 600;\n        line-height: 1.25;\n    }\n\n    #body {\n        display: grid;\n        gap: 4px;\n        padding: 8px;\n    }\n\n    .row {\n        display: grid;\n        grid-template-columns: max-content 1fr;\n        gap: 6px;\n        min-width: 0;\n    }\n\n    .label {\n        color: #555;\n        font-weight: 600;\n        white-space: nowrap;\n    }\n\n    .value {\n        min-width: 0;\n        overflow-wrap: anywhere;\n        white-space: pre-line;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"header\">\n        <span id=\"stereotype\"></span>\n        <div id=\"name\"></div>\n    </div>\n    <div id=\"body\"></div>`;\n\n    static readonly is = \"mermaid-requirement-node\";\n\n    public nodeKind: RequirementNodeKind = \"requirement\";\n    public requirementType: string = \"requirement\";\n    public requirementId: string = \"Req\";\n    public label: string = \"Req\";\n    public text: string = \"\";\n    public risk: string = \"low\";\n    public verifyMethod: string = \"test\";\n    public elementType: string = \"\";\n    public docRef: string = \"\";\n\n    private _body: HTMLDivElement;\n    private _name: HTMLDivElement;\n    private _stereotype: HTMLSpanElement;\n\n    static readonly properties = {\n        nodeKind: String,\n        requirementType: String,\n        requirementId: String,\n        label: String,\n        text: String,\n        risk: String,\n        verifyMethod: String,\n        elementType: String,\n        docRef: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._body = this._getDomElement<HTMLDivElement>(\"body\");\n        this._name = this._getDomElement<HTMLDivElement>(\"name\");\n        this._stereotype = this._getDomElement<HTMLSpanElement>(\"stereotype\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    private _render() {\n        const kind = this.getAttribute(\"node-kind\") as RequirementNodeKind ?? this.nodeKind;\n        const name = this.getAttribute(\"label\") ?? this.label;\n        this._stereotype.textContent = kind === \"element\" ? \"<<Element>>\" : \"<<\" + getRequirementTypeLabel(this.getAttribute(\"requirement-type\") ?? this.requirementType) + \">>\";\n        renderMarkdownLabel(this._name, name);\n        this._body.replaceChildren();\n\n        if (kind === \"element\") {\n            this._appendRow(\"Type\", this.getAttribute(\"element-type\") ?? this.elementType);\n            this._appendRow(\"DocRef\", this.getAttribute(\"doc-ref\") ?? this.docRef);\n        } else {\n            this._appendRow(\"Id\", this.getAttribute(\"requirement-id\") ?? this.requirementId);\n            this._appendRow(\"Text\", this.getAttribute(\"text\") ?? this.text);\n            this._appendRow(\"Risk\", this.getAttribute(\"risk\") ?? this.risk);\n            this._appendRow(\"Verification\", this.getAttribute(\"verify-method\") ?? this.verifyMethod);\n        }\n    }\n\n    private _appendRow(label: string, value: string) {\n        if (!value)\n            return;\n\n        const row = document.createElement(\"div\");\n        row.className = \"row\";\n        const labelElement = document.createElement(\"span\");\n        labelElement.className = \"label\";\n        labelElement.textContent = label + \":\";\n        const valueElement = document.createElement(\"span\");\n        valueElement.className = \"value\";\n        renderMarkdownLabel(valueElement, value);\n        row.append(labelElement, valueElement);\n        this._body.appendChild(row);\n    }\n\n    public createMermaid() {\n        const kind = this.getAttribute(\"node-kind\") as RequirementNodeKind ?? this.nodeKind;\n        const name = sanitizeId(this.getAttribute(\"label\") ?? this.label);\n        if (kind === \"element\") {\n            const lines = [`element ${name} {`];\n            const type = this.getAttribute(\"element-type\") ?? this.elementType;\n            const docRef = this.getAttribute(\"doc-ref\") ?? this.docRef;\n            if (type)\n                lines.push(\"type: \" + formatRequirementValue(type));\n            if (docRef)\n                lines.push(\"docRef: \" + formatRequirementValue(docRef));\n            lines.push(\"}\");\n            return lines.join(\"\\n\");\n        }\n\n        const requirementType = this.getAttribute(\"requirement-type\") ?? this.requirementType;\n        const lines = [`${requirementType} ${name} {`];\n        lines.push(\"id: \" + formatRequirementValue(this.getAttribute(\"requirement-id\") ?? this.requirementId));\n        lines.push(\"text: \" + formatRequirementValue(this.getAttribute(\"text\") ?? this.text));\n        lines.push(\"risk: \" + formatRequirementValue(this.getAttribute(\"risk\") ?? this.risk));\n        lines.push(\"verifymethod: \" + formatRequirementValue(this.getAttribute(\"verify-method\") ?? this.verifyMethod));\n        lines.push(\"}\");\n        return lines.join(\"\\n\");\n    }\n}\n\ncustomElements.define(MermaidRequirementNode.is, MermaidRequirementNode);\n\nfunction getRequirementTypeLabel(type: string) {\n    switch (type) {\n        case \"functionalRequirement\":\n            return \"Functional Requirement\";\n        case \"interfaceRequirement\":\n            return \"Interface Requirement\";\n        case \"performanceRequirement\":\n            return \"Performance Requirement\";\n        case \"physicalRequirement\":\n            return \"Physical Requirement\";\n        case \"designConstraint\":\n            return \"Design Constraint\";\n        default:\n            return \"Requirement\";\n    }\n}\n\nfunction formatRequirementValue(value: string) {\n    return escapeLabel(value ?? \"\");\n}\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-requirement-relationship.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { boundsFromWaypoints, decodeWaypoints, midpointFromWaypoints, pathDataFromWaypoints } from \"../services/mermaidGeometry.js\";\nimport { sanitizeId } from \"./mermaid-node.js\";\n\nexport class MermaidRequirementRelationship extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    :host {\n        display: block;\n        overflow: visible;\n        pointer-events: auto;\n        position: absolute;\n        z-index: 4;\n    }\n\n    svg {\n        height: 100%;\n        overflow: visible;\n        width: 100%;\n    }\n\n    path {\n        fill: none;\n        stroke: #333;\n        stroke-linecap: round;\n        stroke-linejoin: round;\n        stroke-width: 2;\n        vector-effect: non-scaling-stroke;\n    }\n\n    #marker-shape {\n        fill: #333;\n    }\n\n    text {\n        fill: #333;\n        font-family: Arial, sans-serif;\n        font-size: 12px;\n        pointer-events: none;\n    }\n    `;\n\n    static override readonly template = html`\n    <svg id=\"svg\" viewBox=\"0 0 10 10\" preserveAspectRatio=\"none\">\n        <defs>\n            <marker id=\"marker\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"10.5\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <path id=\"marker-shape\" d=\"M 1 1 L 10.5 6 L 1 11 z\"></path>\n            </marker>\n        </defs>\n        <path id=\"path\" marker-end=\"url(#marker)\"></path>\n        <text id=\"label\"></text>\n    </svg>`;\n\n    static readonly is = \"mermaid-requirement-relationship\";\n\n    public from: string = \"source\";\n    public to: string = \"target\";\n    public relationshipType: string = \"satisfies\";\n    public syntaxDirection: string = \"forward\";\n    public waypoints: string = \"\";\n    private _previewWaypoints: string;\n\n    private _label: SVGTextElement;\n    private _path: SVGPathElement;\n    private _svg: SVGSVGElement;\n\n    static readonly properties = {\n        from: String,\n        to: String,\n        relationshipType: String,\n        syntaxDirection: String,\n        waypoints: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._label = this._getDomElement<SVGTextElement>(\"label\");\n        this._path = this._getDomElement<SVGPathElement>(\"path\");\n        this._svg = this._getDomElement<SVGSVGElement>(\"svg\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    public setPreviewWaypoints(waypoints: string | null) {\n        this._previewWaypoints = waypoints;\n        this._render();\n    }\n\n    private _render() {\n        let waypoints = decodeWaypoints(this._previewWaypoints ?? this.getAttribute(\"waypoints\") ?? this.waypoints);\n        if (waypoints.length < 2)\n            waypoints = [{ x: 0, y: 12 }, { x: 180, y: 12 }];\n\n        const padding = 16;\n        const bounds = boundsFromWaypoints(waypoints);\n        const width = Math.max(24, Math.round(bounds.width + padding * 2));\n        const height = Math.max(24, Math.round(bounds.height + padding * 2));\n        const offsetX = (width - bounds.width) / 2;\n        const offsetY = (height - bounds.height) / 2;\n\n        this.style.left = `${Math.round(bounds.x - offsetX)}px`;\n        this.style.top = `${Math.round(bounds.y - offsetY)}px`;\n        this.style.width = `${width}px`;\n        this.style.height = `${height}px`;\n\n        const shifted = waypoints.map(point => ({ x: point.x - bounds.x + offsetX, y: point.y - bounds.y + offsetY }));\n        const middle = midpointFromWaypoints(shifted);\n        const relationshipType = this.getAttribute(\"relationship-type\") ?? this.relationshipType;\n\n        this._svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n        this._path.setAttribute(\"d\", pathDataFromWaypoints(shifted));\n        this._label.textContent = relationshipType;\n        this._label.setAttribute(\"x\", middle.x.toString());\n        this._label.setAttribute(\"y\", (middle.y - 8).toString());\n        this._label.setAttribute(\"text-anchor\", \"middle\");\n    }\n\n    public createMermaid() {\n        const from = sanitizeId(this.getAttribute(\"from\") ?? this.from);\n        const to = sanitizeId(this.getAttribute(\"to\") ?? this.to);\n        const relationshipType = this.getAttribute(\"relationship-type\") ?? this.relationshipType;\n        const syntaxDirection = this.getAttribute(\"syntax-direction\") ?? this.syntaxDirection;\n        if (syntaxDirection === \"reverse\")\n            return `${to} <- ${relationshipType} - ${from}`;\n        return `${from} - ${relationshipType} -> ${to}`;\n    }\n}\n\ncustomElements.define(MermaidRequirementRelationship.is, MermaidRequirementRelationship);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-sequence-message.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { escapeLabel, sanitizeId } from \"./mermaid-node.js\";\n\nenum MermaidSequenceMessageType {\n    solid = \"solid\",\n    dotted = \"dotted\",\n    open = \"open\",\n    cross = \"cross\",\n    async = \"async\",\n}\n\nexport class MermaidSequenceMessage extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    :host {\n        display: block;\n        overflow: visible;\n        pointer-events: auto;\n        position: absolute;\n        z-index: 4;\n    }\n\n    svg {\n        height: 100%;\n        overflow: visible;\n        width: 100%;\n    }\n\n    path {\n        fill: none;\n        stroke: #333;\n        stroke-linecap: round;\n        stroke-linejoin: round;\n        stroke-width: 2;\n        vector-effect: non-scaling-stroke;\n    }\n\n    #marker-shape {\n        fill: #333;\n    }\n\n    text {\n        fill: #333;\n        font-family: Arial, sans-serif;\n        font-size: 12px;\n        pointer-events: none;\n    }\n    `;\n\n    static override readonly template = html`\n    <svg id=\"svg\" viewBox=\"0 0 10 10\" preserveAspectRatio=\"none\">\n        <defs>\n            <marker id=\"marker\" viewBox=\"0 0 12 12\" markerWidth=\"10\" markerHeight=\"10\" refX=\"10.5\" refY=\"6\" orient=\"auto\" markerUnits=\"userSpaceOnUse\">\n                <path id=\"marker-shape\" d=\"M 1 1 L 10.5 6 L 1 11 z\"></path>\n            </marker>\n        </defs>\n        <path id=\"path\"></path>\n        <text id=\"label\"></text>\n    </svg>`;\n\n    static readonly is = \"mermaid-sequence-message\";\n\n    public from: string = \"Alice\";\n    public to: string = \"Bob\";\n    public label: string = \"Message\";\n    public messageType: MermaidSequenceMessageType = MermaidSequenceMessageType.solid;\n    public connector: string = \"->>\";\n\n    private _label: SVGTextElement;\n    private _path: SVGPathElement;\n    private _svg: SVGSVGElement;\n\n    static readonly properties = {\n        from: String,\n        to: String,\n        label: String,\n        messageType: MermaidSequenceMessageType,\n        connector: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._label = this._getDomElement<SVGTextElement>(\"label\");\n        this._path = this._getDomElement<SVGPathElement>(\"path\");\n        this._svg = this._getDomElement<SVGSVGElement>(\"svg\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    private _render() {\n        const width = Math.max(1, parseFloat(this.style.width || \"220\"));\n        const height = Math.max(28, parseFloat(this.style.height || \"36\"));\n        const y = height / 2;\n        const messageType = this.getAttribute(\"message-type\") as MermaidSequenceMessageType ?? this.messageType;\n        const label = this.getAttribute(\"label\") ?? this.label ?? \"\";\n\n        this._svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n        this._path.setAttribute(\"d\", `M 0 ${y} L ${width} ${y}`);\n        this._path.setAttribute(\"marker-end\", messageType === MermaidSequenceMessageType.open ? \"\" : \"url(#marker)\");\n        this._path.style.strokeDasharray = messageType === MermaidSequenceMessageType.dotted ? \"4 4\" : \"\";\n        this._label.textContent = label;\n        this._label.setAttribute(\"x\", (width / 2).toString());\n        this._label.setAttribute(\"y\", (y - 8).toString());\n        this._label.setAttribute(\"text-anchor\", \"middle\");\n    }\n\n    public createMermaid() {\n        const from = sanitizeId(this.getAttribute(\"from\") ?? this.from);\n        const to = sanitizeId(this.getAttribute(\"to\") ?? this.to);\n        const label = this.getAttribute(\"label\") ?? this.label ?? \"\";\n        const connector = this.getAttribute(\"connector\") ?? getConnector(this.getAttribute(\"message-type\") as MermaidSequenceMessageType ?? this.messageType);\n        return `${from}${connector}${to}: ${escapeLabel(label)}`;\n    }\n}\n\nfunction getConnector(messageType: MermaidSequenceMessageType) {\n    switch (messageType) {\n        case MermaidSequenceMessageType.dotted:\n            return \"-->>\";\n        case MermaidSequenceMessageType.open:\n            return \"->\";\n        case MermaidSequenceMessageType.cross:\n            return \"-x\";\n        case MermaidSequenceMessageType.async:\n            return \"-)\";\n        default:\n            return \"->>\";\n    }\n}\n\ncustomElements.define(MermaidSequenceMessage.is, MermaidSequenceMessage);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-sequence-participant.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { escapeLabel, sanitizeId } from \"./mermaid-node.js\";\n\nenum MermaidSequenceParticipantType {\n    participant = \"participant\",\n    actor = \"actor\",\n}\n\nexport class MermaidSequenceParticipant extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    * {\n        box-sizing: border-box;\n    }\n\n    #participant {\n        align-items: center;\n        background: white;\n        border: 2px solid #333;\n        color: #111;\n        display: flex;\n        font-family: Arial, sans-serif;\n        font-size: 14px;\n        height: 100%;\n        justify-content: center;\n        min-height: 42px;\n        min-width: 120px;\n        overflow: hidden;\n        padding: 6px 10px;\n        pointer-events: none;\n        text-align: center;\n        width: 100%;\n    }\n\n    #participant[data-participant-type=\"actor\"] {\n        border-radius: 999px;\n    }\n    `;\n\n    static override readonly template = html`<div id=\"participant\"></div>`;\n\n    static readonly is = \"mermaid-sequence-participant\";\n\n    public participantId: string = \"Alice\";\n    public label: string = \"Alice\";\n    public participantType: MermaidSequenceParticipantType = MermaidSequenceParticipantType.participant;\n\n    private _participant: HTMLDivElement;\n\n    static readonly properties = {\n        participantId: String,\n        label: String,\n        participantType: MermaidSequenceParticipantType,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._participant = this._getDomElement<HTMLDivElement>(\"participant\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    private _render() {\n        this._participant.textContent = this.getAttribute(\"label\") ?? this.label ?? this.getAttribute(\"participant-id\") ?? this.participantId;\n        this._participant.dataset.participantType = this.getAttribute(\"participant-type\") ?? this.participantType;\n    }\n\n    public createMermaid() {\n        const id = sanitizeId(this.getAttribute(\"participant-id\") ?? this.participantId);\n        const label = this.getAttribute(\"label\") ?? this.label ?? id;\n        const type = this.getAttribute(\"participant-type\") ?? this.participantType;\n        const keyword = type === MermaidSequenceParticipantType.actor ? \"actor\" : \"participant\";\n        return label && label !== id ? `${keyword} ${id} as ${escapeLabel(label)}` : `${keyword} ${id}`;\n    }\n}\n\ncustomElements.define(MermaidSequenceParticipant.is, MermaidSequenceParticipant);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/mermaid-subgraph.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { escapeLabel, sanitizeId } from \"./mermaid-node.js\";\n\nexport class MermaidSubgraph extends BaseCustomWebComponentConstructorAppend {\n    static override readonly style = css`\n    :host {\n        background: rgba(245, 245, 245, 0.55);\n        border: 2px dashed #777;\n        color: #222;\n        display: block;\n        font-family: Arial, sans-serif;\n        font-size: 13px;\n        overflow: visible;\n        padding-top: 28px;\n    }\n\n    #title {\n        background: rgba(255, 255, 255, 0.85);\n        border-bottom: 1px solid #aaa;\n        font-weight: 600;\n        left: 0;\n        overflow: hidden;\n        padding: 5px 8px;\n        pointer-events: none;\n        position: absolute;\n        right: 0;\n        text-overflow: ellipsis;\n        top: 0;\n        white-space: nowrap;\n    }\n    `;\n\n    static override readonly template = html`<div id=\"title\"></div><slot></slot>`;\n\n    static readonly is = \"mermaid-subgraph\";\n\n    public subgraphId: string = \"\";\n    public override title: string = \"Subgraph\";\n    public direction: string = \"\";\n\n    private _titleElement: HTMLDivElement;\n\n    static readonly properties = {\n        subgraphId: String,\n        title: String,\n        direction: String,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._titleElement = this._getDomElement<HTMLDivElement>(\"title\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._render();\n    }\n\n    attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {\n        if (oldValue !== newValue) {\n            this._parseAttributesToProperties();\n            this._render();\n        }\n    }\n\n    private _render() {\n        this._titleElement.textContent = this.getAttribute(\"title\") ?? this.title;\n    }\n\n    public createMermaidStart() {\n        const id = this.getAttribute(\"subgraph-id\") ?? this.subgraphId;\n        const title = this.getAttribute(\"title\") ?? this.title;\n        if (id && title && id !== title)\n            return `subgraph ${sanitizeId(id)}[${escapeLabel(title)}]`;\n        return `subgraph ${escapeLabel(title || id || \"Subgraph\")}`;\n    }\n}\n\ncustomElements.define(MermaidSubgraph.is, MermaidSubgraph);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/src/widgets/views/mermaid-demo-view.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { IUiCommand, InstanceServiceContainer, ServiceContainer } from \"@node-projects/web-component-designer\";\nimport { IDemoView } from \"@node-projects/web-component-designer/src/elements/widgets/demoView/IDemoView.js\";\n\nexport class MermaidDemoView extends BaseCustomWebComponentConstructorAppend implements IDemoView {\n    static override readonly template = html`<div id=\"preview\"></div>`;\n\n    static override readonly style = css`\n        :host {\n            background: white;\n            display: block;\n            height: 100%;\n            overflow: auto;\n            position: relative;\n            width: 100%;\n        }\n\n        #preview {\n            box-sizing: border-box;\n            min-height: 100%;\n            padding: 24px;\n            width: 100%;\n        }\n    `;\n\n    executeCommand(command: IUiCommand) {\n    }\n\n    canExecuteCommand(command: IUiCommand) {\n        return false;\n    }\n\n    dispose(): void {\n    }\n\n    async display(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string) {\n        const preview = this._getDomElement<HTMLDivElement>(\"preview\");\n        const mermaid = await import(\"mermaid\");\n        const id = \"mermaid-preview-\" + crypto.randomUUID();\n\n        mermaid.default.initialize({ startOnLoad: false, securityLevel: \"strict\" });\n        const result = await mermaid.default.render(id, code || \"flowchart TD\\n    A[Node]\");\n        preview.innerHTML = result.svg;\n    }\n}\n\ncustomElements.define(\"node-projects-mermaid-demo-view\", MermaidDemoView);\n"
  },
  {
    "path": "packages/web-component-designer-mermaid/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"outDir\": \"./dist\",\n    \"rootDir\": \"./src\",\n    \"noEmit\": false,\n    \"resolveJsonModule\": true\n  },\n  \"include\": [\n    \"src/**/*.ts\",\n    \"src/**/*.js\",\n    \"src/**/*.json\"\n  ],\n  \"references\": [\n    {\n      \"path\": \"../web-component-designer/tsconfig.json\"\n    }\n  ]\n}\n"
  },
  {
    "path": "packages/web-component-designer-stylesheetservice-css-parser/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-stylesheetservice-css-parser/README.md",
    "content": "# web-component-designer-stylesheetservice-css-tools\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-stylesheetservice-css-parser\r\n\r\n     npm i @node-projects/web-component-designer-stylesheetservice-css-parser\r\n\r\n## Description\r\n\r\nThis is a Stylesheetparser using @node-projects/css-parser\r\n\r\n## Usage\r\n\r\n    import { CssParserStylesheetService } from '@node-projects/web-component-designer-stylesheetservice-css-parser';\r\n    serviceContainer.register(\"stylesheetService\", designerCanvas => new CssParserStylesheetService(designerCanvas));"
  },
  {
    "path": "packages/web-component-designer-stylesheetservice-css-parser/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Stylesheet Parser Service using @node-projects/css-parser\",\n  \"name\": \"@node-projects/web-component-designer-stylesheetservice-css-parser\",\n  \"version\": \"0.1.5\",\n  \"type\": \"module\",\n  \"main\": \"./dist/service/stylesheetservice/CssParserStylesheetService.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@node-projects/css-parser\": \"^5.0.0\",\n    \"@node-projects/web-component-designer\": \">=0.1.224\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}\n"
  },
  {
    "path": "packages/web-component-designer-stylesheetservice-css-parser/src/service/stylesheetservice/CssParserStylesheetService.ts",
    "content": "import { CssAtRuleAST, CssDeclarationAST, CssRuleAST, CssStylesheetAST, CssWhitespaceAST, parse, stringify } from \"@node-projects/css-parser\";\nimport { AbstractStylesheetService, IDocumentStylesheet, IStyleRule, IStylesheet, IStyleDeclaration, IDesignerCanvas, IDesignItem } from \"@node-projects/web-component-designer\";\nimport { Specificity } from \"@node-projects/web-component-designer\";\n\ninterface IRuleWithAST extends IStyleRule {\n    ast: CssRuleAST,\n    declarations: IDeclarationWithAST[],\n    stylesheet: IStylesheet;\n    stylesheetName: string;\n    specificity: Specificity;\n}\n\ninterface IDeclarationWithAST extends IStyleDeclaration {\n    ast: CssDeclarationAST\n}\n\nexport class CssParserStylesheetService extends AbstractStylesheetService {\n    public keepFormatting: boolean;\n\n    constructor(designerCanvas: IDesignerCanvas, keepFormatting: boolean = true) {\n        super(designerCanvas);\n        this.keepFormatting = keepFormatting;\n    }\n\n    async internalParse(style: string) {\n        return parse(style, this.keepFormatting ? { preserveFormatting: true } : undefined);\n    }\n\n    public getAppliedRules(designItem: IDesignItem): IRuleWithAST[] {\n        let rules: IRuleWithAST[] = [];\n        for (let item of this._allStylesheets.entries()) {\n            if (!item[1].ast?.stylesheet?.rules) continue;\n            let rs = Array.from(this.getRulesFromAst(item[1].ast?.stylesheet?.rules, item[1].stylesheet, designItem))\n                .map(x => (<IRuleWithAST>{\n                    selector: x[2].join(', '),\n                    declarations: x[0].declarations.filter(y => y.type == 'declaration').map(y => ({\n                        name: (<CssDeclarationAST>y).property,\n                        value: (<CssDeclarationAST>y).value.endsWith('!important') ? (<CssDeclarationAST>y).value.substring(0, (<CssDeclarationAST>y).value.length - 10).trimEnd() : (<CssDeclarationAST>y).value,\n                        important: (<CssDeclarationAST>y).value.endsWith('!important'),\n                        parent: null,\n                        ast: <CssDeclarationAST>y,\n                        stylesheet: item[1].stylesheet\n                    })),\n                    specificity: x[1],\n                    stylesheetName: item[0],\n                    stylesheet: item[1].stylesheet,\n                    ast: x[0],\n                }));\n            rs.forEach(x => x.declarations.forEach(y => y.parent = x));\n            rules.push(...rs);\n        }\n        return rules;\n    }\n\n    getRules(selector: string): IStyleRule[] {\n        const rules = this.getAppliedRules(null);\n        return rules.filter(x => x.selector == selector);\n    }\n\n    async addRule(stylesheet: IStylesheet, selector: string): Promise<IStyleRule> {\n        await this.updateCompleteStylesheet(stylesheet.name, stylesheet.content + '\\n' + selector + ' {}');\n        const rules = this.getRules(selector).filter(x => x.stylesheetName == stylesheet.name);\n        return rules[rules.length - 1];\n    }\n\n    private resolveNestedSelectors(parentSelectors: string[], nestedSelectors: string[]): string[] {\n        const parentRef = parentSelectors.length === 1\n            ? parentSelectors[0]\n            : `:is(${parentSelectors.join(', ')})`;\n        const resolved: string[] = [];\n        for (const nested of nestedSelectors) {\n            if (nested.includes('&')) {\n                resolved.push(nested.replace(/&/g, parentRef));\n            } else {\n                resolved.push(parentRef + ' ' + nested);\n            }\n        }\n        return resolved;\n    }\n\n    private *getRulesFromAst(cssAtRuleAst: (CssDeclarationAST | CssAtRuleAST | CssWhitespaceAST)[], stylesheet: IStylesheet, designItem: IDesignItem, parentSelectors?: string[]): IterableIterator<[CssRuleAST, Specificity, string[]]> {\n        for (const atRule of cssAtRuleAst) {\n            if (atRule.type == 'media') {\n                yield* this.getRulesFromAst(atRule.rules, stylesheet, designItem, parentSelectors);\n            } else if (atRule.type == 'supports') {\n                yield* this.getRulesFromAst(atRule.rules, stylesheet, designItem, parentSelectors);\n            } else if (atRule.type == 'rule') {\n                const resolvedSelectors = parentSelectors\n                    ? this.resolveNestedSelectors(parentSelectors, atRule.selectors)\n                    : atRule.selectors;\n                let spec = this.elementMatchesASelector(designItem, resolvedSelectors);\n                if (spec)\n                    yield [atRule, spec, resolvedSelectors];\n                yield* this.getRulesFromAst(atRule.declarations, stylesheet, designItem, resolvedSelectors);\n            }\n        }\n        return null;\n    }\n\n    public getDeclarations(designItem: IDesignItem, styleName: string): IStyleDeclaration[] {\n        return this.getAppliedRules(designItem).flatMap(x => x.declarations).filter(x => x.name == styleName);\n    }\n\n    public updateDeclarationValueWithoutUndo(declaration: IDeclarationWithAST, value: string, important: boolean) {\n        declaration.ast.value = important ? value + ' !important' : value;\n        if (declaration.ast.rawValue)\n            delete declaration.ast.rawValue\n        let ss = this._allStylesheets.get(declaration.parent.stylesheetName);\n        this.updateStylesheet(ss);\n    }\n\n    public insertDeclarationIntoRule(rule: IRuleWithAST, property: string, value: string, important: boolean): boolean {\n        rule.ast.declarations.push(<CssDeclarationAST>{\n            type: 'declaration',\n            property: property,\n            value: important ? value + ' !important' : value\n        });\n        this.updateStylesheet(this._allStylesheets.get(rule.stylesheetName));\n        return true;\n    }\n\n    removeDeclarationFromRule(rule: IRuleWithAST, property: string): boolean {\n        let idx = rule.ast.declarations.findIndex(x => (<CssDeclarationAST>x).property == property);\n        if (idx == -1) return false;\n        rule.ast.declarations.splice(idx, 1);\n        this.updateStylesheet(this._allStylesheets.get(rule.stylesheetName));\n        return true;\n    }\n\n    private updateStylesheet(ss: { stylesheet: IStylesheet, ast: CssStylesheetAST }) {\n        const old = ss.stylesheet.content;\n        ss.stylesheet.content = this.keepFormatting\n            ? stringify(ss.ast, { identity: true })\n            : stringify(ss.ast, { indent: '    ', compress: false });\n        if ((<IDocumentStylesheet>ss.stylesheet).designItem) {\n            (<IDocumentStylesheet>ss.stylesheet).designItem.content = ss.stylesheet.content;\n        } else\n            this.stylesheetChanged.emit({ name: ss.stylesheet.name, newStyle: ss.stylesheet.content, oldStyle: old, changeSource: 'styleupdate' });\n    }\n\n    async updateCompleteStylesheet(name: string, newStyle: string) {\n        this.updateCompleteStylesheetInternal(name, newStyle, 'styleupdate');\n    }\n\n    async updateCompleteStylesheetWithoutUndo(name: string, newStyle: string, noUndo = false) {\n        this.updateCompleteStylesheetInternal(name, newStyle, 'undo');\n    }\n\n    private async updateCompleteStylesheetInternal(name: string, newStyle: string, changeSource: 'undo' | 'styleupdate') {\n        const ss = this._allStylesheets.get(name);\n        if (ss.stylesheet.content != newStyle) {\n            const old = ss.stylesheet.content;\n            ss.stylesheet.content = newStyle;\n            if (changeSource == 'styleupdate') {\n                ss.ast = await this.internalParse(ss.stylesheet.content);\n            }\n            if ((<IDocumentStylesheet>ss.stylesheet).designItem) {\n                (<IDocumentStylesheet>ss.stylesheet).designItem.content = ss.stylesheet.content;\n            } else\n                this.stylesheetChanged.emit({ name: ss.stylesheet.name, newStyle: ss.stylesheet.content, oldStyle: old, changeSource });\n        }\n    }\n}"
  },
  {
    "path": "packages/web-component-designer-stylesheetservice-css-parser/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/README.md",
    "content": "# web-component-designer-visualization-addons\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-visualization-addons\r\n\r\n     npm i @node-projects/web-component-designer-visualization-addons\r\n\r\n## Description\r\n\r\nThis package contains basic elements for a custom visualization, like a script system, a bindings system, ...\r\nIt is used for example by the iobroker.webui.\r\n\r\n## Dependencies\r\n\r\n- long\r\n- blockly\r\n- @node-projects/css-parser ( only if you use Bindingshelper.parseCssBindings() )\r\n\r\n## Usage\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/package.json",
    "content": "{\r\n  \"description\": \"web-component-designer addon for visualizations\",\r\n  \"name\": \"@node-projects/web-component-designer-visualization-addons\",\r\n  \"version\": \"0.1.144\",\r\n  \"type\": \"module\",\r\n  \"main\": \"./dist/index.js\",\r\n  \"author\": \"jochen.kuehner@gmx.de\",\r\n  \"license\": \"MIT\",\r\n  \"scripts\": {\r\n    \"tsc\": \"tsc\",\r\n    \"build\": \"tsc\",\r\n    \"link\": \"npm link\",\r\n    \"prepublishOnly\": \"npm run build && npm run bundle\",\r\n    \"bundle\": \"esbuild ./dist/index.js --format=esm --minify --external:@node-projects/css-parser --external:@blockly/* --external:blockly --external:long --external:wunderbaum --external:@node-projects/* --platform=neutral --bundle --outfile=./dist/index-min.js\"\r\n  },\r\n  \"dependencies\": {\r\n    \"@blockly/zoom-to-fit\": \">=6.0.9\",\r\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\r\n    \"@node-projects/css-parser\": \"^5.2.0\",\r\n    \"@node-projects/propertygrid.webcomponent\": \">=1.2.3\",\r\n    \"@node-projects/splitview.webcomponent\": \">=1.0.1\",\r\n    \"@node-projects/web-component-designer\": \">=0.1.224\",\r\n    \"@node-projects/web-component-designer-codeview-monaco\": \">=0.1.32\",\r\n    \"@node-projects/web-component-designer-widgets-wunderbaum\": \">=0.1.29\",\r\n    \"blockly\": \">=11.1.1\",\r\n    \"long\": \">=5.2.3\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"esbuild\": \"^0.25.10\"\r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/BlocklyJavascriptHelper.ts",
    "content": "//import Blockly from 'blockly';\nimport { VisualizationHandler } from '../interfaces/VisualizationHandler.js';\nimport './components/components.js';\n\n//TODO: remove imports, only leave Runtime\nconst prefix = `function extractPart(obj, propertyPath) {\n    let retVal = obj;\n    for (let p of propertyPath.split('.')) {\n        retVal = retVal?.[p];\n    }\n    return retVal;\n}\n\nfunction delay(ms) {\n    return new Promise(res => {\n        setTimeout(() => res(), ms);\n    });\n}\n\nexport async function run(eventData, shadowRoot, parameters, relativeSignalsPath, visualizationHandler, context) {\n`;\nconst postfix = `}`;\n\nexport async function generateEventCodeFromBlockly(data: any): Promise<(event: Event, shadowRoot: ShadowRoot, parameters: Record<string, any>, relativeSignalsPath: string, visualizationHandler: VisualizationHandler, context: any) => void> {\n    //@ts-ignore\n    const workspace = new Blockly.Workspace();\n    //@ts-ignore\n    Blockly.serialization.workspaces.load(data, workspace);\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('eventData');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('shadowRoot');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('extractPart');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('parameters');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('relativeSignalsPath');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('delay');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('visualizationHandler');\n    //@ts-ignore\n    Blockly.JavaScript.addReservedWords('context');\n    //@ts-ignore\n    let code = Blockly.JavaScript.workspaceToCode(workspace);\n    const scriptUrl = URL.createObjectURL(new Blob([prefix + code + postfix], { type: 'application/javascript' }));\n    const scripObj = await import(scriptUrl);\n    return scripObj.run;\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/BlocklyScriptEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, html, css } from '@node-projects/base-custom-webcomponent';\r\n//import Blockly from 'blockly';\r\n\r\nexport class BlocklyScriptEditor extends BaseCustomWebComponentConstructorAppend {\r\n\r\n    static override readonly template = html`\r\n        <div id=\"blocklyDiv\" style=\"position: absolute; width: 100%; height: 100%;\"></div>\r\n    `;\r\n\r\n    static override readonly style = css`\r\n        :host {\r\n            box-sizing: border-box;\r\n            position: absolute;\r\n            height: 100%;\r\n            width: 100%;\r\n            display: block;\r\n        }`;\r\n\r\n    static readonly is = 'node-projects-blockly-script-editor';\r\n\r\n    blocklyDiv: HTMLDivElement;\r\n    workspace: any;\r\n    static blocklyStyle1: CSSStyleSheet;\r\n    static blocklyStyle2: CSSStyleSheet;\r\n    resizeObserver: ResizeObserver;\r\n    private _toolbox: any;\r\n\r\n    constructor(toolbox: any) {\r\n        super();\r\n        super._restoreCachedInititalValues();\r\n\r\n        this._toolbox = toolbox;\r\n        this.blocklyDiv = this._getDomElement<HTMLDivElement>('blocklyDiv');\r\n\r\n        this._assignEvents();\r\n        this.createBlockly();\r\n    }\r\n\r\n    createBlockly() {\r\n        const renderer = 'zelos';\r\n        const themename = 'webui';\r\n\r\n        //@ts-ignore\r\n        const theme = Blockly.Theme.defineTheme(themename, {\r\n            //@ts-ignore\r\n            'base': Blockly.Themes.Zelos,\r\n            'blockStyles': {\r\n                \"hat_blocks\": {\r\n                    \"colourPrimary\": \"#4a148c\"\r\n                }\r\n            },\r\n            'categoryStyles': {\r\n                'start_category': {\r\n                    colour: '#4a148c'\r\n                },\r\n                'system_category': {\r\n                    colour: '#01579b',\r\n                }\r\n            },\r\n        });\r\n\r\n        //@ts-ignore\r\n        this.workspace = Blockly.inject(this.blocklyDiv, {\r\n            theme: theme,\r\n            toolbox: this._toolbox,\r\n            renderer: renderer,\r\n            trashcan: true,\r\n            zoom: {\r\n                controls: true,\r\n                wheel: false,\r\n                startScale: 0.7,\r\n                maxScale: 3,\r\n                minScale: 0.3,\r\n                scaleSpeed: 1.2,\r\n                pinch: false\r\n            },\r\n            move: {\r\n                scrollbars: {\r\n                    horizontal: true,\r\n                    vertical: true\r\n                },\r\n                drag: true,\r\n                wheel: true\r\n            },\r\n            maxInstances: { 'start_event': 1 },\r\n        });\r\n\r\n        if (!BlocklyScriptEditor.blocklyStyle1) {\r\n          BlocklyScriptEditor.blocklyStyle1 = new CSSStyleSheet();\r\n            //@ts-ignore\r\n            BlocklyScriptEditor.blocklyStyle1.replaceSync(<HTMLStyleElement>document.getElementById('blockly-renderer-style-' + renderer + '-' + themename).innerText);\r\n            BlocklyScriptEditor.blocklyStyle2 = new CSSStyleSheet();\r\n            //@ts-ignore\r\n            BlocklyScriptEditor.blocklyStyle2.replaceSync(<HTMLStyleElement>document.getElementById('blockly-common-style').innerText);\r\n        }\r\n        this.shadowRoot.adoptedStyleSheets = [BlocklyScriptEditor.blocklyStyle1, BlocklyScriptEditor.blocklyStyle2, BlocklyScriptEditor.style];\r\n\r\n\r\n\r\n        //@ts-ignore\r\n        const zoomToFit = new ZoomToFitControl(this.workspace);\r\n        zoomToFit.init();\r\n    }\r\n\r\n    ready() {\r\n        //@ts-ignore\r\n        Blockly.svgResize(this.workspace);\r\n\r\n        this.resizeObserver = new ResizeObserver((entries) => {\r\n            //@ts-ignore\r\n            Blockly.svgResize(this.workspace)\r\n        });\r\n        this.resizeObserver.observe(this);\r\n    }\r\n\r\n    public save(): any {\r\n        //@ts-ignore\r\n        const state = Blockly.serialization.workspaces.save(this.workspace);\r\n        return state;\r\n    }\r\n\r\n    public load(data: any) {\r\n        //@ts-ignore\r\n        Blockly.serialization.workspaces.load(data, this.workspace);\r\n    }\r\n}\r\ncustomElements.define(BlocklyScriptEditor.is, BlocklyScriptEditor);\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/BlocklyToolbox.ts",
    "content": "import './components/components.js';\n\nexport default {\n    kind: 'categoryToolbox',\n    contents: [\n        {\n            kind: 'category',\n            name: 'Start',\n            categorystyle: 'start_category',\n            contents: [\n                {\n                    kind: 'block',\n                    type: 'start_event',\n                }]\n        },\n        {\n            kind: 'category',\n            name: 'System',\n            categorystyle: 'system_category',\n            contents: [\n                {\n                    kind: 'block',\n                    type: 'console',\n                },\n                {\n                    kind: 'block',\n                    type: 'debugger',\n                },\n                {\n                    kind: 'block',\n                    type: 'delay',\n                    inputs: {\n                        DELAY: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1000,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'open_screen',\n                    inputs: {\n                        SCREEN: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'set_state',\n                    inputs: {\n                        OID: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'get_state',\n                    inputs: {\n                        OID: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'get_parameter',\n                    inputs: {\n                        NAME: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'get_sub_property',\n                    inputs: {\n                        PROPERTYPATH: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'query_selector',\n                    inputs: {\n                        SELECTOR: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'query_selector_all',\n                    inputs: {\n                        SELECTOR: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    kind: 'block',\n                    type: 'set_element',inputs: {\n                        NAME: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n            ]\n        },\n        {\n            kind: 'category',\n            name: 'Logic',\n            categorystyle: 'logic_category',\n            contents: [\n\n                {\n                    type: 'controls_if',\n                    kind: 'block',\n                },\n                {\n                    type: 'logic_compare',\n                    kind: 'block',\n                    fields: {\n                        OP: 'EQ',\n                    },\n                },\n                {\n                    type: 'logic_operation',\n                    kind: 'block',\n                    fields: {\n                        OP: 'AND',\n                    },\n                },\n                {\n                    type: 'logic_negate',\n                    kind: 'block',\n                },\n                {\n                    type: 'logic_boolean',\n                    kind: 'block',\n                    fields: {\n                        BOOL: 'TRUE',\n                    },\n                },\n                {\n                    type: 'logic_null',\n                    kind: 'block'\n                },\n                {\n                    type: 'logic_ternary',\n                    kind: 'block',\n                },\n            ],\n        },\n        {\n            kind: 'category',\n            name: 'Loops',\n            categorystyle: 'loop_category',\n            contents: [\n                {\n                    type: 'controls_repeat_ext',\n                    kind: 'block',\n                    inputs: {\n                        TIMES: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 10,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'controls_whileUntil',\n                    kind: 'block',\n                    fields: {\n                        MODE: 'WHILE',\n                    },\n                },\n                {\n                    type: 'controls_for',\n                    kind: 'block',\n                    fields: {\n                        VAR: {\n                            name: 'i',\n                        },\n                    },\n                    inputs: {\n                        FROM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                        TO: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 10,\n                                },\n                            },\n                        },\n                        BY: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'controls_forEach',\n                    kind: 'block',\n                    fields: {\n                        VAR: {\n                            name: 'j',\n                        },\n                    },\n                },\n                {\n                    type: 'controls_flow_statements',\n                    kind: 'block',\n                    fields: {\n                        FLOW: 'BREAK',\n                    },\n                },\n            ],\n        },\n        {\n            kind: 'category',\n            name: 'Math',\n            categorystyle: 'math_category',\n            contents: [\n                {\n                    type: 'math_number',\n                    kind: 'block',\n                    fields: {\n                        NUM: 123,\n                    },\n                },\n                {\n                    type: 'math_arithmetic',\n                    kind: 'block',\n                    fields: {\n                        OP: 'ADD',\n                    },\n                    inputs: {\n                        A: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                        B: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_single',\n                    kind: 'block',\n                    fields: {\n                        OP: 'ROOT',\n                    },\n                    inputs: {\n                        NUM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 9,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_trig',\n                    kind: 'block',\n                    fields: {\n                        OP: 'SIN',\n                    },\n                    inputs: {\n                        NUM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 45,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_constant',\n                    kind: 'block',\n                    fields: {\n                        CONSTANT: 'PI',\n                    },\n                },\n                {\n                    type: 'math_number_property',\n                    kind: 'block',\n                    fields: {\n                        PROPERTY: 'EVEN',\n                    },\n                    inputs: {\n                        NUMBER_TO_CHECK: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 0,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_round',\n                    kind: 'block',\n                    fields: {\n                        OP: 'ROUND',\n                    },\n                    inputs: {\n                        NUM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 3.1,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_on_list',\n                    kind: 'block',\n                    fields: {\n                        OP: 'SUM',\n                    },\n                },\n                {\n                    type: 'math_modulo',\n                    kind: 'block',\n                    inputs: {\n                        DIVIDEND: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 64,\n                                },\n                            },\n                        },\n                        DIVISOR: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 10,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_constrain',\n                    kind: 'block',\n                    inputs: {\n                        VALUE: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 50,\n                                },\n                            },\n                        },\n                        LOW: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                        HIGH: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 100,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_random_int',\n                    kind: 'block',\n                    inputs: {\n                        FROM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                        TO: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 100,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'math_random_float',\n                    kind: 'block',\n                },\n                {\n                    type: 'math_atan2',\n                    kind: 'block',\n                    inputs: {\n                        X: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                        Y: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 1,\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        },\n        {\n            kind: 'category',\n            name: 'Text',\n            categorystyle: 'text_category',\n            contents: [\n                {\n                    type: 'text',\n                    kind: 'block',\n                    fields: {\n                        TEXT: '',\n                    },\n                },\n                {\n                    type: 'text_multiline',\n                    kind: 'block',\n                    fields: {\n                        TEXT: '',\n                    },\n                },\n                {\n                    type: 'text_join',\n                    kind: 'block',\n                },\n                {\n                    type: 'text_append',\n                    kind: 'block',\n                    fields: {\n                        name: 'item',\n                    },\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_length',\n                    kind: 'block',\n                    inputs: {\n                        VALUE: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_isEmpty',\n                    kind: 'block',\n                    inputs: {\n                        VALUE: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_indexOf',\n                    kind: 'block',\n                    fields: {\n                        END: 'FIRST',\n                    },\n                    inputs: {\n                        VALUE: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'text',\n                                    },\n                                },\n                            },\n                        },\n                        FIND: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_charAt',\n                    kind: 'block',\n                    fields: {\n                        WHERE: 'FROM_START',\n                    },\n                    inputs: {\n                        VALUE: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'text',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_getSubstring',\n                    kind: 'block',\n                    fields: {\n                        WHERE1: 'FROM_START',\n                        WHERE2: 'FROM_START',\n                    },\n                    inputs: {\n                        STRING: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'text',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_changeCase',\n                    kind: 'block',\n                    fields: {\n                        CASE: 'UPPERCASE',\n                    },\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_trim',\n                    kind: 'block',\n                    fields: {\n                        MODE: 'BOTH',\n                    },\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_count',\n                    kind: 'block',\n                    inputs: {\n                        SUB: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_replace',\n                    kind: 'block',\n                    inputs: {\n                        FROM: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                        TO: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_reverse',\n                    kind: 'block',\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: '',\n                                },\n                            },\n                        },\n                    },\n                },\n\n                {\n                    type: 'text_print',\n                    kind: 'block',\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'text_prompt_ext',\n                    kind: 'block',\n                    fields: {\n                        TYPE: 'TEXT',\n                    },\n                    inputs: {\n                        TEXT: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: 'abc',\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        },\n        {\n            kind: 'category',\n            name: 'Lists',\n            categorystyle: 'list_category',\n            contents: [\n                {\n                    type: 'lists_create_with',\n                    kind: 'block',\n                },\n                {\n                    type: 'lists_create_with',\n                    kind: 'block',\n                },\n                {\n                    type: 'lists_repeat',\n                    kind: 'block',\n                    inputs: {\n                        NUM: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 5,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_length',\n                    kind: 'block',\n                },\n                {\n                    type: 'lists_isEmpty',\n                    kind: 'block',\n                },\n                {\n                    type: 'lists_indexOf',\n                    kind: 'block',\n\n                    fields: {\n                        END: 'FIRST',\n                    },\n                    inputs: {\n                        VALUE: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'list',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_getIndex',\n                    kind: 'block',\n                    fields: {\n                        MODE: 'GET',\n                        WHERE: 'FROM_START',\n                    },\n                    inputs: {\n                        VALUE: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'list',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_setIndex',\n                    kind: 'block',\n                    fields: {\n                        MODE: 'SET',\n                        WHERE: 'FROM_START',\n                    },\n                    inputs: {\n                        LIST: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'list',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_getSublist',\n                    kind: 'block',\n                    fields: {\n                        WHERE1: 'FROM_START',\n                        WHERE2: 'FROM_START',\n                    },\n                    inputs: {\n                        LIST: {\n                            block: {\n                                type: 'variables_get',\n                                fields: {\n                                    VAR: {\n                                        name: 'list',\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_split',\n                    kind: 'block',\n\n                    fields: {\n                        MODE: 'SPLIT',\n                    },\n                    inputs: {\n                        DELIM: {\n                            shadow: {\n                                type: 'text',\n                                fields: {\n                                    TEXT: ',',\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'lists_sort',\n                    kind: 'block',\n\n                    fields: {\n                        TYPE: 'NUMERIC',\n                        DIRECTION: '1',\n                    },\n                },\n                {\n                    type: 'lists_reverse',\n                    kind: 'block',\n                },\n            ],\n        },\n        {\n            kind: 'category',\n            categorystyle: 'colour_category',\n            name: 'Color',\n            contents: [\n                {\n                    type: 'colour_picker',\n                    kind: 'block',\n                    fields: {\n                        COLOUR: '#ff0000',\n                    },\n                },\n                {\n                    type: 'colour_random',\n                    kind: 'block',\n                },\n                {\n                    type: 'colour_rgb',\n                    kind: 'block',\n                    inputs: {\n                        RED: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 100,\n                                },\n                            },\n                        },\n                        GREEN: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 50,\n                                },\n                            },\n                        },\n                        BLUE: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 0,\n                                },\n                            },\n                        },\n                    },\n                },\n                {\n                    type: 'colour_blend',\n                    kind: 'block',\n                    inputs: {\n                        COLOUR1: {\n                            shadow: {\n                                type: 'colour_picker',\n                                fields: {\n                                    COLOUR: '#ff0000',\n                                },\n                            },\n                        },\n                        COLOUR2: {\n                            shadow: {\n                                type: 'colour_picker',\n                                fields: {\n                                    COLOUR: '#3333ff',\n                                },\n                            },\n                        },\n                        RATIO: {\n                            shadow: {\n                                type: 'math_number',\n                                fields: {\n                                    NUM: 0.5,\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        },\n        {\n            kind: 'sep',\n        },\n        {\n            kind: 'category',\n            name: 'Variables',\n            custom: 'VARIABLE',\n            categorystyle: 'variable_category',\n        },\n        {\n            kind: 'category',\n            name: 'Functions',\n            custom: 'PROCEDURE',\n            categorystyle: 'procedure_category',\n        },\n    ],\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/Console.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['console'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField('console')\n            //@ts-ignore\n            .appendField(new Blockly.FieldDropdown([[\"debug\", \"DEBUG\"], [\"error\", \"ERROR\"], [\"info\", \"INFO\"], [\"log\", \"LOG\"], [\"warn\", \"WARN\"]]), \"LEVEL\");\n\n        this.appendValueInput('VALUE')\n            .setCheck(null)\n            .appendField('value');\n\n        this.setInputsInline(true);\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['console'] = function(block, generator) {\n    var level = block.getFieldValue('LEVEL');\n    //@ts-ignore\n    const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC);\n    let code = `console['${(<string>level).toLowerCase()}'](${value});\n`;\n    return code;\n  }"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/Debugger.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['debugger'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField(\"debugger\");\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['debugger'] = function (block) {\n    return 'debugger;\\n';\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/Delay.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['delay'] = {\n    init: function () {\n        this.appendValueInput('DELAY')\n            .setCheck(\"Number\")\n            .appendField('delay');\n\n        this.setInputsInline(true);\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['delay'] = function (block) {\n    //@ts-ignore\n    const delay = Blockly.JavaScript.valueToCode(block, 'DELAY', Blockly.JavaScript.ORDER_ATOMIC);\n    let code = 'await delay(' + delay + ');\\n';\n    return code;\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/GetParameter.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['get_parameter'] = {\n    init: function () {\n        this.appendValueInput('NAME')\n            .setCheck(\"String\")\n            //@ts-ignore\n            .appendField('get_parameter');\n\n        this.setInputsInline(true);\n        this.setOutput(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['get_parameter'] = function (block, generator) {\n    //@ts-ignore\n    const name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);\n    const code = `context.parameters[${name}]`;\n    //@ts-ignore\n    return [code, Blockly.JavaScript.ORDER_NONE];\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/GetState.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['get_state'] = {\n    init: function () {\n        this.appendValueInput('OID')\n            .setCheck(\"String\")\n            //@ts-ignore\n            .appendField('get_state');\n\n        this.setInputsInline(true);\n        this.setOutput(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['get_state'] = function (block, generator) {\n    //@ts-ignore\n    const id = Blockly.JavaScript.valueToCode(block, 'OID', Blockly.JavaScript.ORDER_ATOMIC);\n    const code = `(await visualizationHandler.getState((${id}[0] === '.' ? relativeSignalsPath : '') + ${id})).val`;\n    //@ts-ignore\n    return [code, Blockly.JavaScript.ORDER_NONE];\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/GetSubProperty.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['get_sub_property'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField(\"getSubProperty\");\n\n        this.appendValueInput('OBJECT')\n            .setCheck('Object')\n            .appendField('object');\n\n        this.appendValueInput('PROPERTYPATH')\n            .setCheck(\"String\")\n            .appendField('propertypath');\n\n        this.setInputsInline(true);\n        this.setOutput(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['get_sub_property'] = function (block, generator) {\n    //@ts-ignore\n    const obj = Blockly.JavaScript.valueToCode(block, 'OBJECT', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const propertypath = Blockly.JavaScript.valueToCode(block, 'PROPERTYPATH', Blockly.JavaScript.ORDER_ATOMIC);\n    const code = `extractPart(${obj}, ${propertypath})`;\n    //@ts-ignore\n    return [code, Blockly.JavaScript.ORDER_NONE];\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/OpenScreen.ts",
    "content": "\"use strict\";\n//@ts-ignore\nBlockly.Blocks['open_screen'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField('open screen');\n        this.appendValueInput('SCREEN')\n            .setCheck('String')\n            .appendField('screen');\n        this.appendValueInput('RELATIVESIGNALSPATH')\n            .setCheck(\"String\")\n            .appendField('relativeSignalsPath');\n        this.appendValueInput('NOHISTORY')\n            .setCheck(\"Boolean\")\n            .appendField('noHistory');\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n//@ts-ignore\nBlockly.JavaScript.forBlock['open_screen'] = function (block) {\n    //@ts-ignore\n    const screen = Blockly.JavaScript.valueToCode(block, 'SCREEN', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const relativeSignalsPath = Blockly.JavaScript.valueToCode(block, 'RELATIVESIGNALSPATH', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const noHistory = Blockly.JavaScript.valueToCode(block, 'NOHISTORY', Blockly.JavaScript.ORDER_ATOMIC);\n    let code = `RUNTIME.openScreen({screen: ${screen}, relativeSignalsPath: ${relativeSignalsPath}, noHistory: ${noHistory}})`;\n    return code;\n};\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/QuerySelector.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['query_selector'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField('querySelector')\n            //@ts-ignore\n            .appendField(new Blockly.FieldDropdown([[\"currentScreen\", \"CURRENTSCREEN\"], [\"parentScreen\", \"PARENTSCREEN\"]]), \"SOURCE\");\n\n        this.appendValueInput('SELECTOR')\n            .setCheck(\"String\")\n            //@ts-ignore\n            .appendField('selector');\n\n        this.setInputsInline(true);\n        this.setOutput(true, 'Element');\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['query_selector'] = function (block, generator) {\n    var dropdown_source = block.getFieldValue('SOURCE');\n    //@ts-ignore\n    const selector = Blockly.JavaScript.valueToCode(block, 'SELECTOR', Blockly.JavaScript.ORDER_ATOMIC);\n\n    let code;\n    if (dropdown_source === 'CURRENTSCREEN')\n        code = `shadowRoot.querySelector(${selector})`;\n    else if (dropdown_source === 'PARENTSCREEN')\n        code = `shadowRoot.host.getRootNode().querySelector(${selector})`;\n    //@ts-ignore\n    return [code, Blockly.JavaScript.ORDER_NONE];\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/QuerySelectorAll.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['query_selector_all'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField('querySelectorAll')\n            //@ts-ignore\n            .appendField(new Blockly.FieldDropdown([[\"currentScreen\", \"CURRENTSCREEN\"], [\"parentScreen\", \"PARENTSCREEN\"]]), \"SOURCE\");\n\n        this.appendValueInput('SELECTOR')\n            .setCheck(\"String\")\n            //@ts-ignore\n            .appendField('selector');\n\n        this.setInputsInline(true);\n        this.setOutput(true, 'Array');\n        this.setColour(230);\n    }\n};\n\n//TODO helper for current & parent screen queryselector\n//TODO also a query selector for screen directly\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['query_selector_all'] = function (block, generator) {\n    var dropdown_source = block.getFieldValue('SOURCE');\n    //@ts-ignore\n    const selector = Blockly.JavaScript.valueToCode(block, 'SELECTOR', Blockly.JavaScript.ORDER_ATOMIC);\n\n    let code;\n    if (dropdown_source === 'CURRENTSCREEN')\n        code = `shadowRoot.querySelectorall(${selector})`;\n    else if (dropdown_source === 'PARENTSCREEN')\n        code = `shadowRoot.querySelectorall(${selector})`;\n    //@ts-ignore\n    return [code, Blockly.JavaScript.ORDER_NONE];\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/Return.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['return'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField(\"return\");\n        this.appendValueInput(\"VALUE\")\n            .setCheck(null)\n            .appendField(\"value\");\n        this.setInputsInline(true);\n        this.setPreviousStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['return'] = function (block) {\n    //@ts-ignore\n    const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC);\n    let code = 'return ' + value + ';';\n    return code;\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/SetElement.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['set_element'] = {\n    init: function () {\n        this.appendDummyInput()\n            .appendField('setElement')\n            //@ts-ignore\n            .appendField(new Blockly.FieldDropdown([[\"property\", \"PROPERTY\"], [\"attribute\", \"ATTRIBUTE\"], [\"style\", \"STYLE\"]]), \"TARGET\");\n\n        this.appendValueInput('ELEMENT')\n            .setCheck('Element')\n            .appendField('element');\n\n        this.appendValueInput('NAME')\n            .setCheck(\"String\")\n            .appendField('name');\n\n\n        this.appendValueInput('VALUE')\n            .setCheck(null)\n            .appendField('value');\n\n        this.setInputsInline(true);\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['set_element'] = function (block) {\n    var dropdown_target = block.getFieldValue('TARGET');\n    //@ts-ignore\n    const element = Blockly.JavaScript.valueToCode(block, 'ELEMENT', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC);\n    let code = '';\n    if (dropdown_target === 'PROPERTY') {\n        code += element + '[' + name + '] = ' + value + ';\\n';\n    } else if (dropdown_target === 'ATTRIBUTE') {\n        code += element + '.setAttribute(' + name + ', ' + value + ');\\n';\n    } else if (dropdown_target === 'STYLE') {\n        code += element + '.style[' + name + '] = ' + value + ';\\n';\n    }\n\n    return code;\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/SetState.ts",
    "content": "//@ts-ignore\nBlockly.Blocks['set_state'] = {\n    init: function () {\n        this.appendValueInput('OID')\n            .setCheck(\"String\")\n            //@ts-ignore\n            .appendField('set_state');\n\n        this.appendValueInput('VALUE')\n            .setCheck(null)\n            .appendField('with');\n\n        this.setInputsInline(true);\n        this.setPreviousStatement(true, null);\n        this.setNextStatement(true, null);\n        this.setColour(230);\n    }\n};\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['set_state'] = function (block) {\n    //@ts-ignore\n    const id = Blockly.JavaScript.valueToCode(block, 'OID', Blockly.JavaScript.ORDER_ATOMIC);\n    //@ts-ignore\n    const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ATOMIC);\n    const code = `await visualizationHandler.setState((${id}[0] === '.' ? relativeSignalsPath : '') + ${id}, ${value});\\n`;\n\n    return code;\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/StartEvent.ts",
    "content": "//@ts-ignore\nBlockly.defineBlocksWithJsonArray([\n    {\n        'type': 'start_event',\n        'message0': 'Event %1',\n        'nextStatement': null,\n        'style': 'hat_blocks',\n        'args0': [\n            {\n              'type': 'field_variable',\n              'name': 'EVENTVAR',\n              'variable': 'event'\n            }\n          ],\n    },\n]);\n\n//@ts-ignore\nBlockly.JavaScript.forBlock['start_event'] = function (block) {\n    //@ts-ignore\n    let name = Blockly.JavaScript.getVariableName(block.getField('EVENTVAR').variable.name);\n    return name + ' = ' + 'eventData;\\n';\n};\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/blockly/components/components.ts",
    "content": "import './Console.js';\nimport './Debugger.js';\nimport './Delay.js';\nimport './GetParameter.js';\nimport './GetState.js';\nimport './GetSubProperty.js';\nimport './OpenScreen.js';\nimport './QuerySelector.js';\nimport './QuerySelectorAll.js';\nimport './SetElement.js';\nimport './SetState.js';\nimport './StartEvent.js';"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/BindingsEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, html, css } from '@node-projects/base-custom-webcomponent';\r\nimport { BindingMode, BindingTarget, IBinding, InstanceServiceContainer, IProperty, ServiceContainer } from '@node-projects/web-component-designer';\r\nimport { CodeViewMonaco } from '@node-projects/web-component-designer-codeview-monaco';\r\nimport { VisualizationShell } from '../interfaces/VisualizationShell.js';\r\nimport { BindingsEditorHistoric } from './BindingsEditorHistoric.js';\r\nimport { VisualizationBinding } from '../interfaces/VisualizationBinding.js';\r\n\r\nexport class BindingsEditor extends BaseCustomWebComponentConstructorAppend {\r\n\r\n  static override readonly template = html`\r\n        <div id=\"root\">\r\n            <div class=\"vertical-grid\">\r\n                <div style=\"grid-column: 1/3\">\r\n                    <div style=\"display: flex; flex-direction: column;\">\r\n                        <div class=\"row\">\r\n                            <span style=\"cursor: pointer;\" title=\"to use multiple objects, seprate them with semicolon (;).&#010;Access signal objects in properties via ?propertyName, access the propertyValue via ??propertyName.&#010;Access signal objects in properties of the target via #propertyName, access a propertyValue of the target via ##propertyName.&#010;Bind to signal configurations via $objectId.&#010;Bind to special values via §name (if supported by your framework).&#010;You could also use signals inside of a Signal Name via {name}\">objects</span>\r\n                        </div>\r\n                        <div id=\"groupObjectName\" style=\"display:flex;align-items: flex-end;\">\r\n                            <input id=\"objectName\" class=\"row\" value=\"{{?this.objectNames::change}}\" style=\"flex-grow: 1;\">\r\n                            <button @click=\"_clear\" style=\"height: 22px\">X</button>\r\n                            <button @click=\"_select\" style=\"height: 22px\">...</button>\r\n                        </div>\r\n                        <div id=\"groupObjectType\" class=\"row\">\r\n                            <label style=\"white-space: nowrap; margin-right: 4px;\" title=\"if set, the value is converted to the type before binding is applied\">type :</label>\r\n                            <select class=\"row\" value=\"{{?this.objectValueType}}\">\r\n                                <option selected value=\"\">ignore</option>\r\n                                <option value=\"number\">number</option>\r\n                                <option value=\"boolean\">boolean</option>\r\n                                <option value=\"string\">string</string>\r\n                            </select>\r\n                        </div>\r\n                        <div id=\"groupBindingMode\" class=\"row\">\r\n                            <input type=\"checkbox\" disabled=\"[[!this.twoWayPossible]]\" checked=\"{{this.twoWay::change}}\" @change=\"_refresh\">\r\n                            <span>two way binding</span>\r\n                            <span css:display=\"[[this.twoWay ? 'inline' : 'none']]\" style=\"margin-left: 15px\">events:&nbsp;</span>\r\n                            <input css:display=\"[[this.twoWay ? 'inline-block' : 'none']]\" title=\"to use multiple events, seprate them with semicolon (;)\" class=\"row\" value=\"{{?this.events::change}}\" style=\"flex-grow: 1;\">\r\n                        </div>\r\n                        <div id=\"groupinvert\" class=\"row\" style=\"position: relative\">\r\n                            <input type=\"checkbox\" checked=\"{{this.invert::change}}\">\r\n                            <span>invert logic</span>\r\n                            <button css:border=\"[[this.historic ? 'solid lime 5px' : 'none']]\" style=\"position: absolute; right: 1px; top: 5px; padding: 10px;\" @click=\"showHistoric\">historic</button>\r\n                        </div>\r\n                        <div class=\"row\">\r\n                            <span style=\"cursor: pointer;\" title=\"javascript expression. access context with __ctx, result with __res, objects with __0, __1, ...\">formula</span>\r\n                        </div>\r\n                        <div class=\"row\">\r\n                            <node-projects-code-view-monaco id=\"expression\" single-row language=\"javascript\" style=\"width: 100%; min-height: 17px; height: 17px; position: relative; overflow: hidden; resize: vertical;\" .code=\"{{?this.expression}}\" @code-changed=\"_refresh\"></node-projects-code-view-monaco>\r\n                        </div>\r\n                        <div class=\"row\">\r\n                          <span style=\"text-wrap: nowrap\" style=\"cursor: pointer;\" title=\"write back the value build by a formula to a signal. maybe only usefull when a formula is used.\">write back signal :</span>\r\n                          <input style=\"width: 100%; margin-left: 5px;\" .disabled=\"[[!this.expression]]\" value=\"{{?this.writeBackSignal::change}}\">\r\n                        </div>\r\n                        <div class=\"row\">\r\n                            <span style=\"cursor: pointer;\" title=\"javascript expression. access property with 'value'\">formula write back (two way)</span>\r\n                        </div>\r\n                        <div class=\"row\">\r\n                            <node-projects-code-view-monaco id=\"expression2way\" .read-only=\"[[!this.twoWay]]\" $readonly=\"[[!this.twoWay]]\" single-row language=\"javascript\" style=\"width: 100%; min-height: 17px; height: 17px; position: relative; overflow: hidden; resize: vertical;\" .code=\"{{?this.expressionTwoWay}}\"></node-projects-code-view-monaco>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            <div class=\"vertical-grid\" style=\"margin-top: 10px;\">\r\n                <div>\r\n                    <div class=\"input-headline\" style=\"display: flex;align-items: center;\">\r\n                        <span>converter</span>:<input id=\"namedConverterInput\" style=\"width: 100%; margin-left: 15px;\" value=\"{{?this.convertersString::change}}\">\r\n                    </div>\r\n                </div>\r\n            </div>\r\n            <div class=\"vertical-grid\" style=\"border: solid 1px black; padding: 10px; overflow-y: auto;\">\r\n                <div class=\"bottomleft\">\r\n                    <div id=\"converterGrid\" style=\"height: 100%;\">\r\n                        <div style=\"width: 100%; height: 20px; display: flex;\">\r\n                            <div style=\"width: 39%\">condition</div>\r\n                            <div style=\"width: 59%\">value</div>\r\n                        </div>\r\n                        <template repeat:item=\"[[this.converters]]\">\r\n                            <div css:background-color=\"[[item.activeRow ? 'gray' : '']]\" style=\"width: 100%; display: flex; height: 26px; justify-content: center; align-items: center; gap: 5px;\">\r\n                                <input type=\"text\" value=\"{{item.key}}\" @focus=\"[[this._focusRow(index)]]\" style=\"width: 39%\">\r\n                                <input type=\"[[this._property.type == 'color' ? 'color' : 'text']]\" value=\"{{item.value}}\" @focus=\"[[this._focusRow(index)]]\" style=\"width: 59%\">\r\n                            </div>\r\n                        </template>\r\n                    </div>\r\n                </div>\r\n                <div class=\"controlbox\" id=\"grid-controls\">\r\n                    <button type=\"button\" id=\"add-row-button\" value=\"add\" @click=\"addConverter\">\r\n                        <span>add</span>\r\n                    </button>\r\n                    <button id=\"remove-row-button\" value=\"remove\" style=\"margin-top: 6px;\" @click=\"removeConverter\">\r\n                        <span>remove</span>\r\n                    </button>\r\n                </div>\r\n            </div>\r\n        </div>`;\r\n\r\n  static override readonly style = css`\r\n        :host {\r\n            box-sizing: border-box;\r\n        }\r\n        \r\n        #converterGrid {\r\n            display: flex;\r\n            gap: 2px;\r\n            flex-direction: column;\r\n        }\r\n        #converterGrid input {\r\n            height: 20px;\r\n            box-sizing: border-box;\r\n        }\r\n\r\n        .padding_top {\r\n            padding-top: 30px;\r\n        }\r\n\r\n        .row{\r\n            margin-top: 3px;\r\n            display: flex;\r\n            align-items: center;\r\n        }\r\n\r\n        .controlbox {\r\n            display: flex;\r\n            flex-direction: column;\r\n        }\r\n\r\n        .input-headline {\r\n            height: 30px;\r\n        }\r\n\r\n        input[type=\"checkbox\"] {\r\n            margin-right: 15px;\r\n            width: 15px;\r\n            height: 15px;\r\n        }\r\n\r\n        select {\r\n            width: 100%;\r\n        }\r\n\r\n        #root {\r\n            padding: 2px 10px;\r\n            display: grid;\r\n            grid-template-rows: min-content min-content;\r\n            overflow: auto;\r\n            height: calc(100% - 4px)\r\n        }\r\n\r\n        .vertical-grid {\r\n            display: grid;\r\n            grid-template-columns: calc((100% - 150px) - 30px) 150px;\r\n            gap: 30px;\r\n        }\r\n\r\n        #grid input, #list input { \r\n            border:0px;\r\n        }\r\n\r\n        #tagdata_type {\r\n            height: 24px;\r\n            font-size: inherit;\r\n        }\r\n        \r\n        node-projects-code-view-monaco:not([readonly]) {\r\n            border: 1px black solid;\r\n        }\r\n        \r\n        node-projects-code-view-monaco[readonly] {\r\n            border: 1px lightgray solid;\r\n        }`;\r\n\r\n  static readonly is: string = 'node-projects-visualization-bindings-editor';\r\n\r\n  static readonly properties = {\r\n    twoWayPossible: Boolean,\r\n    twoWay: Boolean,\r\n    expression: String,\r\n    objectNames: String,\r\n    events: String,\r\n    invert: Boolean,\r\n    converters: Array,\r\n    historic: Object\r\n  }\r\n\r\n  public twoWayPossible: boolean = false;\r\n  public twoWay: boolean = false;\r\n  public expression: string = '';\r\n  public writeBackSignal: string = '';\r\n  public expressionTwoWay: string = '';\r\n  public historic: any;\r\n  public objectNames: string = '';\r\n  public events: string = '';\r\n  public invert: boolean = false;\r\n  public converters: { key: string, value: any }[] = [];\r\n  public convertersString: string;\r\n  public objectValueType: string;\r\n\r\n  protected _property: IProperty;\r\n  protected _binding: IBinding & { converter: Record<string, any> | string };\r\n  protected _bindingTarget: BindingTarget;\r\n  protected _serviceContainer: ServiceContainer;\r\n  protected _instanceServiceContainer: InstanceServiceContainer;\r\n  protected _shell: VisualizationShell\r\n  protected _activeRow: number = -1;\r\n  protected _objNmInput: HTMLInputElement;\r\n\r\n  constructor(property: IProperty, binding: IBinding & { converter: Record<string, any> }, bindingTarget: BindingTarget, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, shell: VisualizationShell, config = { namedConverters: true }) {\r\n    super();\r\n    super._restoreCachedInititalValues();\r\n\r\n\r\n    //TODOS:\r\n    //typed values for converters\r\n    //default value for converter\r\n\r\n\r\n    this._objNmInput = this._getDomElement<HTMLInputElement>('objectName');\r\n\r\n    this._property = property;\r\n    this._binding = binding;\r\n    this._bindingTarget = bindingTarget;\r\n    this._serviceContainer = serviceContainer;\r\n    this._instanceServiceContainer = instanceServiceContainer;\r\n    this._shell = shell;\r\n\r\n    if (!config?.namedConverters) {\r\n      this._getDomElement<HTMLInputElement>('namedConverterInput').style.display = 'none';\r\n    }\r\n  }\r\n\r\n  ready() {\r\n    this._parseAttributesToProperties();\r\n    this._assignEvents();\r\n\r\n    this.twoWayPossible = false;\r\n    if (this._bindingTarget == BindingTarget.property || this._bindingTarget == BindingTarget.attribute)\r\n      this.twoWayPossible = true;\r\n\r\n    if (this._binding) {\r\n      this.twoWay = this._binding.mode == BindingMode.twoWay;\r\n      this.expression = this._binding.expression;\r\n      this.writeBackSignal = (<VisualizationBinding><unknown>this._binding).writeBackSignal;\r\n      this.expressionTwoWay = (<any>this._binding).expressionTwoWay;\r\n      this.historic = (<any>this._binding).historic;\r\n      this.invert = this._binding.invert;\r\n      this.objectValueType = this._binding.type;\r\n      if (this._binding.bindableObjectNames)\r\n        this.objectNames = this._binding.bindableObjectNames.join(';');\r\n      if (this._binding.converter) {\r\n        if (typeof this._binding.converter === 'string') {\r\n          this.convertersString = this._binding.converter;\r\n        } else {\r\n          for (let c in this._binding.converter) {\r\n            this.converters.push({ key: c, value: this._binding.converter[c] });\r\n          }\r\n        }\r\n      }\r\n      if (this._binding.changedEvents && this._binding.changedEvents.length)\r\n        this.events = this._binding.changedEvents.join(';');\r\n    }\r\n\r\n    if (this.expression) {\r\n      let edt = this._getDomElement<CodeViewMonaco>('expression');\r\n      if (this.expression.indexOf('\\n') >= 0) {\r\n        edt.style.height = (3 * 17) + 'px';\r\n      }\r\n    }\r\n\r\n    if (this.expressionTwoWay) {\r\n      let edt = this._getDomElement<CodeViewMonaco>('expression2way');\r\n      if (this.expressionTwoWay.indexOf('\\n') >= 0) {\r\n        edt.style.height = (3 * 17) + 'px';\r\n      }\r\n    }\r\n\r\n    this._bindingsParse();\r\n\r\n    this._objNmInput.focus();\r\n  }\r\n\r\n  _focusRow(index: number) {\r\n    this._activeRow = index;\r\n    this._updatefocusedRow();\r\n  }\r\n\r\n  _updatefocusedRow() {\r\n    let g = this._getDomElement('converterGrid');\r\n    g.querySelectorAll('div').forEach(x => x.style.background = '');\r\n    if (this._activeRow >= 0)\r\n      (<HTMLDivElement>g.children[this._activeRow + 1]).style.background = 'gray';\r\n  }\r\n\r\n  _clear() {\r\n    this.objectNames = '';\r\n    this._bindingsRefresh();\r\n  }\r\n\r\n  _refresh() {\r\n    requestAnimationFrame(() => {\r\n      this._bindingsRefresh();\r\n    });\r\n  }\r\n\r\n  async _select() {\r\n    let b = this._shell.createBindableObjectBrowser();\r\n    b.initialize(this._serviceContainer, this._instanceServiceContainer, 'binding');\r\n    b.title = 'select signal...';\r\n    const abortController = new AbortController();\r\n    b.objectDoubleclicked.on(() => {\r\n      abortController.abort();\r\n      if (this.objectNames != '')\r\n        this.objectNames += ';'\r\n      if (b.selectedObject.specialType == 'signalProperty') {\r\n        this.objectNames += '?';\r\n      } else if (b.selectedObject.bindabletype === 'property') {\r\n        this.objectNames += '??';\r\n      }\r\n      this.objectNames += b.selectedObject.fullName;\r\n      this._bindingsRefresh();\r\n    });\r\n    let res = await this._shell.openConfirmation(b, { x: 100, y: 100, width: 400, height: 300, parent: this, abortSignal: abortController.signal });\r\n    if (res) {\r\n      if (this.objectNames != '')\r\n        this.objectNames += ';'\r\n      this.objectNames += b.selectedObject.fullName;\r\n      this._bindingsRefresh();\r\n    }\r\n  }\r\n\r\n  async showHistoric() {\r\n    let h = new BindingsEditorHistoric(this.historic);\r\n    const abortController = new AbortController();\r\n    h.title = \"Edit historic binding to: \" + this._property.name;\r\n    let res = await this._shell.openConfirmation(h, { x: 100, y: 100, width: 420, height: 510, parent: this, abortSignal: abortController.signal, disableResize: true, cancelText: 'Remove' });\r\n    if (!res) {\r\n      this.historic = null;\r\n    } else {\r\n      this.historic = h.historic;\r\n    }\r\n    this._bindingsRefresh();\r\n  }\r\n\r\n  addConverter() {\r\n    this.converters.push({ key: '', value: '' });\r\n    this._activeRow = this.converters.length - 1;\r\n    this._bindingsRefresh();\r\n    this._updatefocusedRow();\r\n  }\r\n\r\n  removeConverter() {\r\n    this.converters.splice(this._activeRow, 1);\r\n    this._activeRow = -1;\r\n    this._bindingsRefresh();\r\n    this._updatefocusedRow();\r\n  }\r\n}\r\ncustomElements.define(BindingsEditor.is, BindingsEditor)"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/BindingsEditorHistoric.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, html, css } from '@node-projects/base-custom-webcomponent';\r\n\r\nexport class BindingsEditorHistoric extends BaseCustomWebComponentConstructorAppend {\r\n\r\n    static override readonly template = html`\r\n        <span style=\"position:absolute;left:30px;top:26px;\">from</span>\r\n        <span style=\"position:absolute;left:30px;top:77px;\">count</span>\r\n        <span style=\"position:absolute;left:30px;top:123px;\">limit</span>\r\n        <span style=\"position:absolute;left:30px;top:150px;\">round</span>\r\n        <span style=\"position:absolute;left:55px;top:181px;\">return newest entries</span>\r\n        <span style=\"position:absolute;left:55px;top:201px;\">remove border values</span>\r\n        <span style=\"position:absolute;left:30px;top:301px;\">step</span>\r\n        <span style=\"position:absolute;left:30px;top:270px;\">aggregate</span>\r\n        <span style=\"position:absolute;left:30px;top:227px;\">ignore null</span>\r\n        <input type=\"datetime-local\" value=\"{{this.historic.from}}\" style=\"position:absolute;left:75.9219px;top:26px;width:184px;\">\r\n        <span style=\"position:absolute;left:31px;top:52px;\">to</span>\r\n        <input type=\"datetime-local\" value=\"{{this.historic.to}}\" style=\"position:absolute;left:76px;top:51.4375px;width:184px;\">\r\n        <input type=\"number\" value=\"{{this.historic.count}}\" style=\"position:absolute;left:76px;top:78.4415px;width:184px;\">\r\n        <input type=\"number\" value=\"{{this.historic.limit}}\" style=\"position:absolute;left:76px;top:125.5px;width:184px;\">\r\n        <input type=\"number\" value=\"{{this.historic.round}}\" style=\"position:absolute;left:76px;top:152px;width:184px;\">\r\n        <input type=\"number\" value=\"{{this.historic.step}}\" style=\"position:absolute;left:75.921875px;top:301px;width:184px;\">\r\n        <select value=\"{{?this.historic.aggregate}}\" @change=\"[[this.refresh()]]\" style=\"position:absolute;left:142px;top:273px;width:118px;height:21px;\">\r\n            <option>none</option>\r\n            <option>minmax</option>\r\n            <option>max</option>\r\n            <option>min</option>\r\n            <option>average</option>\r\n            <option>total</option>\r\n            <option>count</option>\r\n            <option>percentile</option>\r\n            <option>quantile</option>\r\n            <option>integral</option>\r\n        </select>\r\n        <select value=\"{{this.historic.ignoreNull}}\" style=\"position:absolute;left:142px;top:230px;width:119px;height:21px;\">\r\n            <option>false</option>\r\n            <option>true</option>\r\n            <option>0</option>\r\n        </select>\r\n        <input type=\"checkbox\" checked=\"{{this.historic.returnNewestEntries}}\" style=\"position:absolute;left:35px;top:184px;\">\r\n        <input type=\"checkbox\" checked=\"{{this.historic.removeBorderValues}}\" style=\"position:absolute;left:35px;top:204px;\">\r\n        <div style=\"position:absolute;left:266px;top:26px;width:135px;height:201px;border:1px solid black;\">\r\n            <div style=\"position:absolute;left:25px;top:36px;width:117px;height:154px;grid-template-columns:20px 1fr;display:grid;\">\r\n                <input type=\"checkbox\" checked=\"{{this.historic.from}}\" style=\"grid-column:1;grid-row:1;\">\r\n                <span>from</span>\r\n                <input type=\"checkbox\" checked=\"{{this.historic.ack}}\">\r\n                <span>ack</span>\r\n                <input type=\"checkbox\" checked=\"{{this.historic.q}}\">\r\n                <span>q</span>\r\n                <input type=\"checkbox\" checked=\"{{this.historic.user}}\">\r\n                <span>user</span>\r\n                <input type=\"checkbox\" checked=\"{{this.historic.comment}}\">\r\n                <span>comment</span>\r\n                <input type=\"checkbox\" checked=\"{{this.historic.id}}\">\r\n                <span>id</span>\r\n            </div>\r\n            <span style=\"position:absolute;left:8px;top:6px;\">include fields</span>\r\n        </div>\r\n        <span style=\"position:absolute;left:31.8203px;top:390px;\">update all (ms)</span>\r\n        <input value=\"{{this.historic.reloadInterval}}\" type=\"number\" style=\"position:absolute;left:142px;top:390px;width:119px;\">\r\n        <span css:visibility=\"[[this.historic.aggregate == 'percentile' ? 'visible' : 'collapse']]\" id=\"lblPercentile\" style=\"position:absolute;left:30px;top:325px;\">percentile</span>\r\n        <input css:visibility=\"[[this.historic.aggregate == 'percentile' ? 'visible' : 'collapse']]\" value=\"{{this.historic.percentile}}\" type=\"number\" id=\"percentile\" style=\"position:absolute;left:109px;top:325px;width:151px;\">\r\n        <span css:visibility=\"[[this.historic.aggregate == 'integral' ? 'visible' : 'collapse']]\" id=\"lblIntegral\" style=\"position:absolute;left:30px;top:325px;\">integral unit</span>\r\n        <input css:visibility=\"[[this.historic.aggregate == 'integral' ? 'visible' : 'collapse']]\" value=\"{{this.historic.integralUnit}}\" type=\"number\" id=\"integralUnit\" style=\"position:absolute;left:126px;top:325px;width:134px;\">\r\n        <select css:visibility=\"[[this.historic.aggregate == 'integral' ? 'visible' : 'collapse']]\" value=\"{{this.historic.integralInterpolation}}\" style=\"position:absolute;left:178.93px;top:349px;width:81px;height:21px;\">\r\n            <option>none</option>\r\n            <option>linear</option>\r\n        </select>\r\n        <span css:visibility=\"[[this.historic.aggregate == 'integral' ? 'visible' : 'collapse']]\" style=\"position:absolute;left:29.9219px;top:346px;\">integral interpolation</span>\r\n        <input css:visibility=\"[[this.historic.aggregate == 'quantile' ? 'visible' : 'collapse']]\" value=\"{{this.historic.quantile}}\" type=\"number\" id=\"quantile\" style=\"position:absolute;left:107px;top:325px;width:153px;\">\r\n        <span css:visibility=\"[[this.historic.aggregate == 'quantile' ? 'visible' : 'collapse']]\" id=\"lblQuantile\" style=\"position:absolute;left:30px;top:325px;\">quantile</span>\r\n        <span style=\"position:absolute;left:34px;top:417px;\">instance</span>\r\n        <input type=\"text\" value=\"{{?this.historic.instance}}\" style=\"position:absolute;left:104px;top:419px;width:155px;\">`;\r\n\r\n    static override readonly style = css`\r\n        :host {\r\n            box-sizing: border-box;\r\n        }`;\r\n\r\n    static readonly is = 'node-projects-visualization-bindings-editor-historic';\r\n\r\n    historic: any;\r\n\r\n    static readonly properties = {\r\n        historic: Object\r\n    }\r\n\r\n    constructor(historic: any) {\r\n        super();\r\n        this._restoreCachedInititalValues();\r\n\r\n        this.historic = historic ?? { reloadInterval: 2000 };\r\n    }\r\n\r\n    ready() {\r\n        this._bindingsParse();\r\n    }\r\n\r\n    #refreshing = false;\r\n    refresh() {\r\n        if (!this.#refreshing) {\r\n            this.#refreshing = true;\r\n            this._bindingsRefresh(null, true);\r\n            this.#refreshing = false;\r\n        }\r\n    }\r\n}\r\n\r\ncustomElements.define(BindingsEditorHistoric.is, BindingsEditorHistoric)"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/EventAssignment.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, Disposable, css, html } from \"@node-projects/base-custom-webcomponent\"\nimport { ContextMenu, copyTextToClipboard, getTextFromClipboard, IContextMenuItem, IDesignItem, IEvent, InstanceServiceContainer, PropertiesHelper } from \"@node-projects/web-component-designer\";\nimport { ParameterEditor } from \"./ParameterEditor.js\";\nimport { VisualizationShell } from \"../interfaces/VisualizationShell.js\";\nimport { VisualizationHandler } from \"../interfaces/VisualizationHandler.js\";\nimport { BlocklyScriptEditor } from \"../blockly/BlocklyScriptEditor.js\";\nimport { SimpleScriptEditor } from \"./SimpleScriptEditor.js\";\n\ntype eventWithDesignItem = IEvent & { designItem: IDesignItem };\n\ntype scriptType = 'jsdirect' | 'js' | 'script' | 'blockly' | 'none' | 'empty';\nexport class EventAssignment extends BaseCustomWebComponentConstructorAppend {\n\n    static override style = css`\n        :host {\n            display: grid;\n            grid-template-columns: 20px 1fr auto;\n            overflow-y: auto;\n            align-content: start;\n            height: 100%;\n        }\n        .rect {\n            width: 7px;\n            height: 7px;\n            border: 1px solid black;\n            justify-self: center;\n            cursor: pointer;\n            align-self: center;\n            white-space: nowrap;\n        }\n        input.mth {\n            width: 100%;\n            box-sizing: border-box;\n        }\n        input::placeholder {\n            font-size: 8px;\n        }\n        a {\n            cursor: pointer;\n            white-space: nowrap;\n        }\n        a:hover {\n            cursor: pointer;\n            text-decoration: underline;\n        }\n        button {\n            cursor: pointer;\n        }`;\n\n    static override template = html`\n        <template repeat:item=\"[[this.events]]\">\n            <div @click=\"[[this._ctxMenu(event, item)]]\" @contextmenu=\"[[this._ctxMenu(event, item)]]\" class=\"rect\" title=\"[[this._getScriptType(item)]]\" css:background-color=\"[[this._getScriptTypeColor(item)]]\"></div>\n            <a @click=\"[[this._showContextMenuAssignScript(event, item, false)]]\" @contextmenu=\"[[this._ctxMenu(event, item)]]\" title=\"[[item.name]]\">[[item.name]]</a>\n            <div>[[this._createControlsForScript(item)]]</div>\n        </template>\n        <span style=\"grid-column: 1 / span 3; margin-top: 8px; margin-left: 3px;\">add event:</span>\n        <input id=\"addEventInput\" style=\"grid-column: 1 / span 3; margin: 5px;\" @keypress=\"[[this._addEvent(event)]]\" type=\"text\">`;\n\n    static editRowTemplate = html`\n        <div style=\"display: flex; justify-content: flex-end;\">\n            <input value=\"[[this._getScriptName(item)]]\" @keypress=\"[[this._changeScriptName(event, item)]]\" hidden=\"[[this._getScriptType(item) !== 'js']]\" placeholder=\"name\" title=\"name\" style=\"min-width: 50px; flex-basis: 30px; flex-grow: 2;\" class=\"mth\" type=\"text\">\n            <input value=\"[[this._getRelativeSignalsPath(item)]]\" @keypress=\"[[this._changeRelativeSignalsPath(event, item)]]\" placeholder=\"relative signals path\" title=\"relative signals path\" style=\"min-width: 50px; flex-basis: 20px; flex-grow: 1;\" class=\"mth\" type=\"text\">\n            <button css:background=\"[[this._hasParameters(item) ? 'lime' : '']]\" style=\"display: flex; padding: 0; flex-grow: 0;\" title=\"parameter\" @click=\"[[this._editParameter(event, item)]]\">p</button>\n        </div>`;\n\n    static scriptTypeColors = {\n        'js': 'purple',\n        'blockly': 'yellow',\n        'script': 'lightgreen',\n        'empty': 'pink'\n    }\n\n    static readonly is: string = 'node-projects-visualization-event-assignment';\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n    }\n\n    ready() {\n        this._bindingsParse();\n    }\n\n    private _blocklyToolbox: any;\n    protected _instanceServiceContainer: InstanceServiceContainer;\n    private _selectionChangedHandler: Disposable;\n    private _selectedItems: IDesignItem[];\n    private _visualizationHandler: VisualizationHandler;\n    private _visualizationShell: VisualizationShell;\n    private _scriptCommandsTypeInfo: any;\n    private _propertiesTypeInfo: any\n\n    public events: (IEvent & { designItem: IDesignItem })[];\n\n    public initialize(visualizationHandler: VisualizationHandler, visualizationShell: VisualizationShell, scriptCommandsTypeInfo: any, propertiesTypeInfo: any, blocklyToolbox: any) {\n        this._visualizationHandler = visualizationHandler;\n        this._visualizationShell = visualizationShell;\n        this._scriptCommandsTypeInfo = scriptCommandsTypeInfo;\n        this._propertiesTypeInfo = propertiesTypeInfo;\n        this._blocklyToolbox = blocklyToolbox;\n    }\n\n    public set instanceServiceContainer(value: InstanceServiceContainer) {\n        this._instanceServiceContainer = value;\n        this._selectionChangedHandler?.dispose()\n        this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(e => {\n            this.selectedItems = e.selectedElements;\n        });\n        this.selectedItems = this._instanceServiceContainer.selectionService.selectedElements;\n    }\n\n    protected _createControlsForScript(eventItem: eventWithDesignItem) {\n        const st = this._getScriptType(eventItem);\n        switch (st) {\n            case 'none':\n                return '';\n        }\n        //@ts-ignore\n        const ctl = this.constructor.editRowTemplate.content.cloneNode(true);\n        return ctl;\n    }\n\n    protected _getScriptName(eventItem: IEvent) {\n        if (this.selectedItems[0].hasAttribute('@' + eventItem.name)) {\n            const val = this.selectedItems[0].getAttribute('@' + eventItem.name);\n            if (val[0] === '{') {\n                if (val.includes('name')) {\n                    const parsed = JSON.parse(val);\n                    return parsed.name;\n                }\n            } else {\n                return val;\n            }\n        }\n        return null;\n    }\n\n    protected async _changeScriptName(e: KeyboardEvent, eventItem: IEvent) {\n        if (e.key == 'Enter') {\n            if (this.selectedItems && this.selectedItems.length) {\n                if (this.selectedItems[0].hasAttribute('@' + eventItem.name)) {\n                    const newValue = (<HTMLInputElement>e.target).value;\n                    const val = this.selectedItems[0].getAttribute('@' + eventItem.name);\n                    if (val.startsWith('{')) {\n                        const parsed = JSON.parse(val);\n                        parsed.name = newValue;\n                        this._selectedItems[0].setAttribute('@' + eventItem.name, JSON.stringify(parsed));\n                    } else {\n                        this._selectedItems[0].setAttribute('@' + eventItem.name, newValue);\n                    }\n                }\n            }\n        }\n    }\n\n    protected _getRelativeSignalsPath(eventItem: IEvent) {\n        if (this.selectedItems[0].hasAttribute('@' + eventItem.name)) {\n            const val = this.selectedItems[0].getAttribute('@' + eventItem.name);\n            if (val[0] === '{' && val.includes('relativeSignalsPath')) {\n                const parsed = JSON.parse(val);\n                return parsed.relativeSignalsPath;\n            }\n        }\n        return null;\n    }\n\n    protected async _changeRelativeSignalsPath(e: KeyboardEvent, eventItem: IEvent) {\n        if (e.key == 'Enter') {\n            if (this.selectedItems && this.selectedItems.length) {\n                if (this.selectedItems[0].hasAttribute('@' + eventItem.name)) {\n                    const newValue = (<HTMLInputElement>e.target).value;\n                    const val = this.selectedItems[0].getAttribute('@' + eventItem.name);\n                    if (val.startsWith('{')) {\n                        const parsed = JSON.parse(val);\n                        parsed.relativeSignalsPath = newValue;\n                        this._selectedItems[0].setAttribute('@' + eventItem.name, JSON.stringify(parsed));\n                    } else {\n                        let obj = { name: val, relativeSignalsPath: newValue };\n                        this._selectedItems[0].setAttribute('@' + eventItem.name, JSON.stringify(obj));\n                    }\n                }\n            }\n        }\n    }\n\n    protected _hasParameters(eventItem: IEvent) {\n        if (this.selectedItems[0].hasAttribute('@' + eventItem.name)) {\n            const val = this.selectedItems[0].getAttribute('@' + eventItem.name);\n            return val.includes('parameters')\n        }\n        return false;\n    }\n\n    protected _getScriptTypeColor(eventItem: eventWithDesignItem) {\n        const st = this._getScriptType(eventItem);\n        const color = EventAssignment.scriptTypeColors[st];\n        return color ?? 'white';\n    }\n\n    protected _getScriptType(eventItem: eventWithDesignItem): scriptType {\n        if (eventItem.designItem.hasAttribute('@' + eventItem.name)) {\n            const val = eventItem.designItem.getAttribute('@' + eventItem.name);\n            if (val.startsWith('{')) {\n                const parsed = JSON.parse(val);\n                if ('blocks' in parsed)\n                    return 'blockly';\n                if ('commands' in parsed)\n                    return 'script';\n                return 'js';\n            } else if (val == '')\n                return 'empty';\n            else\n                return 'js';\n        }\n        return 'none';\n    }\n\n    protected _getEventMethodname(eventItem: IEvent): string {\n        if (this.selectedItems.length)\n            return this.selectedItems[0].getAttribute('@' + eventItem.name);\n        return '';\n    }\n\n    protected _inputMthName(event: InputEvent, eventItem: IEvent) {\n        let el = event.target as HTMLInputElement\n        this.selectedItems[0].setAttribute('@' + eventItem.name, el.value);\n    }\n\n    protected _ctxMenu(e: MouseEvent, eventItem: eventWithDesignItem) {\n        e.preventDefault();\n        const evtType = this._getScriptType(eventItem);\n        if (evtType == 'empty')\n            this._showContextMenuAssignScript(e, eventItem, true)\n        else if (evtType != 'none') {\n            let ctxMenu = [\n                {\n                    title: 'remove',\n                    action: () => { this.selectedItems[0].removeAttribute('@' + eventItem.name); this._bindingsRefresh(); }\n                }, {\n                    title: '-'\n                }, {\n                    title: 'copy',\n                    action: () => { copyTextToClipboard(this.selectedItems[0].getAttribute('@' + eventItem.name)); }\n                }, {\n                    title: 'paste',\n                    action: async () => { this.selectedItems[0].setAttribute('@' + eventItem.name, await getTextFromClipboard()); this._bindingsRefresh(); }\n                }];\n            ContextMenu.show(ctxMenu, e);\n        } else\n            this._showContextMenuAssignScript(e, eventItem, true)\n    }\n\n    protected async _addEvent(e: KeyboardEvent) {\n        if (e.key == 'Enter') {\n            let ip = this._getDomElement<HTMLInputElement>('addEventInput');\n            this._selectedItems[0].setAttribute('@' + PropertiesHelper.camelToDashCase(ip.value.replaceAll(' ', '-')), '');\n            ip.value = '';\n            this.scrollTop = 0;\n            this.refresh();\n        }\n    }\n\n    protected _createAssignScriptContextMenu(event: MouseEvent, eventItem: eventWithDesignItem) {\n        let ctxMenu: IContextMenuItem[] = [\n            {\n                title: 'Simple Script',\n                action: () => {\n                    this._editEvent('script', event, eventItem);\n                }\n            },\n            {\n                title: 'Javascript',\n                action: () => {\n                    let nm = prompt('name of function ?');\n                    if (nm) {\n                        this._selectedItems[0].setAttribute('@' + eventItem.name, nm);\n                        this.refresh();\n                        this._editEvent('js', null, eventItem);\n                    }\n                }\n            },\n            {\n                title: 'Blockly',\n                action: () => {\n                    this._editBlockly(null, eventItem);\n                }\n            }\n        ];\n\n        const evtType = this._getScriptType(eventItem);\n        if (evtType != 'none') {\n            ctxMenu.push({\n                title: '-'\n            });\n            ctxMenu.push({\n                title: 'remove',\n                action: () => { this.selectedItems[0].removeAttribute('@' + eventItem.name); this._bindingsRefresh(); }\n            });\n        }\n        if (evtType != 'empty') {\n            ctxMenu.push({\n                title: '-'\n            }, {\n                title: 'copy',\n                action: () => { copyTextToClipboard(this.selectedItems[0].getAttribute('@' + eventItem.name)); }\n            }, {\n                title: 'paste',\n                action: async () => { this.selectedItems[0].setAttribute('@' + eventItem.name, await getTextFromClipboard()); this._bindingsRefresh(); }\n            });\n        }\n        return ctxMenu;\n    }\n\n    protected async _showContextMenuAssignScript(event: MouseEvent, eventItem: eventWithDesignItem, isCtxMenu: boolean) {\n        event.preventDefault();\n        const evtType = this._getScriptType(eventItem);\n        if (evtType != 'none' && evtType != 'empty' && !isCtxMenu) {\n            this._editEvent(evtType, event, eventItem);\n        } else {\n            let ctxMenu = this._createAssignScriptContextMenu(event, eventItem)\n            ContextMenu.show(ctxMenu, event);\n        }\n    }\n\n    protected async _editParameter(e: MouseEvent, eventItem: IEvent & { designItem: IDesignItem }) {\n        const edt = new ParameterEditor();\n        let existingParameter = {};\n        edt.title = \"ParameterEditor for '\" + eventItem.name + \"' of '\" + eventItem.designItem.name + \"'\";\n        let data = eventItem.designItem.getAttribute('@' + eventItem.name);\n        if (data && data[0] == '{') {\n            try {\n                const parsed = JSON.parse(data);\n                existingParameter = parsed.parameters;\n            }\n            catch { }\n        }\n        edt.setParametersObject(existingParameter);\n        const result = await this._visualizationShell.openConfirmation(edt, { x: 100, y: 100, width: 700, height: 500 });\n        if (result) {\n            const par = edt.getParametersObject();\n            let newObj = {\n                name: data,\n                parameters: par\n            }\n            if (data && data[0] == '{') {\n                newObj = JSON.parse(data);\n                newObj.parameters = par;\n            }\n            if (par == null)\n                delete newObj.parameters;\n\n            const newData = JSON.stringify(newObj);\n            eventItem.designItem.setAttribute('@' + eventItem.name, newData);\n            this._bindingsRefresh();\n        }\n    }\n\n    protected async _editBlockly(e: MouseEvent, eventItem: eventWithDesignItem) {\n        const edt = new BlocklyScriptEditor(this._blocklyToolbox);\n        edt.title = \"Blockly Script for '\" + eventItem.name + \"' of '\" + eventItem.designItem.name + \"'\";\n        let data = eventItem.designItem.getAttribute('@' + eventItem.name);\n        let parameters = null;\n        let relativeSignalsPath = null;\n        if (data) {\n            const parsed = JSON.parse(data);\n            parameters = parsed.parameters;\n            relativeSignalsPath = parsed.relativeSignalsPath;\n            edt.load(parsed);\n        }\n        const result = await this._visualizationShell.openConfirmation(edt, { x: 100, y: 100, width: 700, height: 500 });\n        if (result) {\n            const blockObj = edt.save();\n            if (parameters) {\n                blockObj.parameters = parameters;\n            }\n            if (relativeSignalsPath) {\n                blockObj.relativeSignalsPath = relativeSignalsPath;\n            }\n            eventItem.designItem.setAttribute('@' + eventItem.name, JSON.stringify(blockObj));\n            this._bindingsRefresh();\n        }\n    }\n\n    protected async _editJavascript(e: MouseEvent, eventItem: eventWithDesignItem) {\n        // todo ?\n    }\n\n    protected async _editSimpleScript(e: MouseEvent, eventItem: eventWithDesignItem) {\n        let scriptString = <string>eventItem.designItem.getAttribute('@' + eventItem.name);\n        if (!scriptString || scriptString.startsWith('{')) {\n            let script = { commands: [] };\n            let parameters = null;\n            let relativeSignalsPath = null;\n            if (scriptString) {\n                script = JSON.parse(scriptString);\n                //@ts-ignore\n                parameters = script.parameters;\n                //@ts-ignore\n                relativeSignalsPath = script.relativeSignalsPath;\n            }\n            let sc = new SimpleScriptEditor();\n            sc.serviceContainer = eventItem.designItem.serviceContainer;\n            sc.instanceServiceContainer = eventItem.designItem.instanceServiceContainer;\n            sc.scriptCommandsTypeInfo = this._scriptCommandsTypeInfo;\n            sc.propertiesTypeInfo = this._propertiesTypeInfo;\n            sc.visualizationShell = this._visualizationShell;\n            sc.visualizationHandler = this._visualizationHandler;\n\n            sc.loadScript(script);\n            sc.title = \"Script '\" + eventItem.name + \"' on \" + eventItem.designItem.name;\n\n            let res = await this._visualizationShell.openConfirmation(sc, { x: 100, y: 100, width: 600, height: 500 });\n            if (res) {\n                let scriptCommands = sc.getScriptCommands();\n                if (scriptCommands && scriptCommands.length) {\n                    const sc = { commands: scriptCommands };\n                    if (parameters) {\n                        //@ts-ignore\n                        sc.parameters = parameters;\n                    }\n                    if (relativeSignalsPath) {\n                        //@ts-ignore\n                        sc.relativeSignalsPath = relativeSignalsPath;\n                    }\n                    let json = JSON.stringify(sc);\n                    eventItem.designItem.setAttribute('@' + eventItem.name, json);\n                    this._bindingsRefresh();\n                }\n            }\n        }\n    }\n\n    public async _editEvent(evtType: scriptType, e: MouseEvent, eventItem: eventWithDesignItem) {\n        if (evtType == 'js') {\n            this._editJavascript(e, eventItem);\n        } else if (evtType == 'blockly') {\n            this._editBlockly(e, eventItem);\n        } else {\n            this._editSimpleScript(e, eventItem);\n        }\n    }\n\n    public refresh() {\n        if (this._selectedItems != null && this._selectedItems.length) {\n            this.events = this._selectedItems[0].serviceContainer.getLastServiceWhere('eventsService', x => x.isHandledElementFromEventsService(this._selectedItems[0])).getPossibleEvents(this._selectedItems[0]).map(x => ({ ...x, designItem: this._selectedItems[0] }));\n        } else {\n            this.events = [];\n        }\n        this._bindingsRefresh();\n    }\n\n    get selectedItems() {\n        return this._selectedItems;\n    }\n    set selectedItems(items: IDesignItem[]) {\n        if (this._selectedItems != items) {\n            this._selectedItems = items;\n            this.refresh();\n        }\n    }\n}\n\ncustomElements.define(EventAssignment.is, EventAssignment);"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/ParameterEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\"\n\nexport class ParameterEditor extends BaseCustomWebComponentConstructorAppend {\n\n    static override style = css`\n        :host {\n            display: grid;\n            grid-template-columns: 80px 80px 80px auto;\n            overflow-y: auto;\n            align-content: start;\n            height: 100%;\n            padding: 10px;\n            gap: 5px;\n            box-sizing: border-box;\n        }\n        \n        span {\n            font-size: 10px;\n        }`;\n\n    static override template = html`\n        <span></span>\n        <span>name</span>\n        <span>type</span>\n        <span>value</span>\n        <template repeat:item=\"[[this._parameterArray]]\">\n            <button @click=\"[[this._remove(index)]]\" style=\"width: 40px; height: 20px; align-self: center;\">del</button>\n            <input type=\"text\" value=\"{{?item.key}}\">\n            <select value=\"{{item.type}}\" @change=\"[[this._bindingsRefresh()]]\">\n                <option>null</string>\n                <option>string</string>\n                <option>number</string>\n                <option>boolean</string>\n            </select>\n            <input hidden=\"[[item.type !== 'string']]\" type=\"text\" value=\"{{?item.value}}\">\n            <input hidden=\"[[item.type !== 'number']]\" type=\"number\" value=\"{{item.value}}\">\n            <input hidden=\"[[item.type !== 'boolean']]\" type=\"checkbox\" checked=\"{{item.value}}\">\n            <div hidden=\"[[item.type !== 'null']]\">-null-</div>\n        </template>\n        <button @click=\"[[this._add()]]\" style=\"width: 40px; height: 20px; align-self: center;\">add</button>`;\n\n    public static is = 'node-projects-visualization-parameter-editor';\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n    }\n\n    ready() {\n        this._bindingsParse();\n    }\n\n    protected _parameterArray: { key: string, type: string, value: any }[];\n\n    setParametersObject(value: Record<string, any>) {\n        if (value != null)\n            this._parameterArray = Object.keys(value).map(x => ({ key: x, type: typeof value[x] === 'object' ? 'null' : typeof value[x], value: value[x] }));\n        else\n            this._parameterArray = [];\n        this._bindingsRefresh();\n    }\n\n    getParametersObject() {\n        let hasPar = false;\n        const obj = {};\n        for (const p of this._parameterArray) {\n            if (p.key) {\n                hasPar = true;\n                obj[p.key] = null;\n                switch (p.type) {\n                    case 'string':\n                        obj[p.key] = p.value.toString();\n                        break;\n                    case 'number':\n                        const f = parseFloat(p.value);\n                        obj[p.key] = isNaN(f) ? 0 : f;\n                        break;\n                    case 'boolean':\n                        obj[p.key] = !!p.value;\n                        break;\n                }\n            }\n        }\n        return hasPar ? obj : null;\n    }\n\n    _remove(index: number) {\n        this._parameterArray.splice(index, 1);\n        this._bindingsRefresh();\n    }\n\n    _add() {\n        this._parameterArray.push(<any>{ type: 'null' });\n        this._bindingsRefresh();\n    }\n}\n\ncustomElements.define(ParameterEditor.is, ParameterEditor);"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/SimpleScriptEditor.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\r\nimport { Script } from \"../scripting/Script.js\";\r\nimport { ScriptCommands } from \"../scripting/ScriptCommands.js\";\r\nimport { ContextMenu, InstanceServiceContainer, ServiceContainer } from \"@node-projects/web-component-designer\";\r\nimport { IProperty, typeInfoFromJsonSchema } from \"@node-projects/propertygrid.webcomponent\";\r\nimport { defaultOptions } from \"@node-projects/web-component-designer-widgets-wunderbaum\";\r\nimport { Wunderbaum } from 'wunderbaum';\r\n//@ts-ignore\r\nimport wunderbaumStyle from 'wunderbaum/dist/wunderbaum.css' with { type: 'css' };\r\nimport { WbRenderEventType } from \"types\";\r\nimport { VisualizationPropertyGrid } from \"./VisualizationPropertyGrid.js\";\r\nimport { VisualizationHandler } from \"../interfaces/VisualizationHandler.js\";\r\nimport { VisualizationShell } from \"../interfaces/VisualizationShell.js\";\r\nimport '@node-projects/splitview.webcomponent';\r\nimport { ScriptUpgrades } from \"../scripting/ScriptUpgrader.js\";\r\n\r\nexport class SimpleScriptEditor extends BaseCustomWebComponentConstructorAppend {\r\n  static override readonly style = css`\r\n        :host {\r\n            background: white;\r\n        }\r\n        .list{\r\n            display: grid;\r\n            grid-template-columns: 1fr 40px;\r\n            width: calc(100% - 6px);\r\n            box-sizing: border-box;\r\n            margin: 3px;\r\n        }\r\n\r\n        .list button{\r\n            padding: 5px 10px;\r\n            box-sizing: border-box;\r\n            display: flex;\r\n            justify-content: center;\r\n            margin-right: 5px;\r\n            margin-left: 5px;\r\n        }\r\n\r\n        node-projects-visualization-property-grid {\r\n            width: 100%;\r\n            height: 100%;\r\n        }\r\n    `;\r\n\r\n  static override readonly template = html`\r\n        <div style=\"width:100%; height:100%; overflow: hidden;\">\r\n            <node-projects-split-view style=\"height: 100%; width: 100%; position: relative;\" orientation=\"horizontal\">\r\n                <div style=\"width: 40%;  position: relative;\">\r\n                    <div style=\"width:calc(100% - 4px); height:calc(100% - 4px)\">\r\n                        <div id=\"commandList\" style=\"overflow-x: hidden; overflow-y: auto; width:100%; height: calc(100% - 34px);\"></div>\r\n                        <div class=\"list\">\r\n                            <select id=\"possibleCommands\" style=\"width: 100%\"></select>  \r\n                            <button @click=\"[[this.addItem()]]\">Add</button>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n                <div style=\"width: 60%; position: relative;\">\r\n                    <node-projects-visualization-property-grid id=\"propertygrid\"></node-projects-visualization-property-grid>\r\n                </div>\r\n            </node-projects-split-view>\r\n        </div>\r\n    `;\r\n\r\n  static readonly is = 'node-projects-visualization-simple-script-editor';\r\n\r\n  public serviceContainer: ServiceContainer;\r\n  public instanceServiceContainer: InstanceServiceContainer;\r\n  public visualizationHandler: VisualizationHandler;\r\n  public visualizationShell: VisualizationShell;\r\n\r\n  public scriptCommandsTypeInfo: any;\r\n  public propertiesTypeInfo: any;\r\n\r\n  private _script: Script;\r\n\r\n  private _commandListDiv: HTMLDivElement;\r\n  private _commandListFancyTree: Wunderbaum;\r\n\r\n  private _possibleCommands: HTMLSelectElement;\r\n  private _propertygrid: VisualizationPropertyGrid;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n\r\n    this._commandListDiv = this._getDomElement<HTMLDivElement>('commandList');\r\n    this._possibleCommands = this._getDomElement<HTMLSelectElement>('possibleCommands');\r\n    this._propertygrid = this._getDomElement<VisualizationPropertyGrid>('propertygrid');\r\n\r\n    this.shadowRoot.adoptedStyleSheets = [wunderbaumStyle, SimpleScriptEditor.style];\r\n  }\r\n\r\n  async ready() {\r\n    this.addPossibleCommands();\r\n\r\n    this._parseAttributesToProperties();\r\n    this._bindingsParse(null, true);\r\n    this._assignEvents();\r\n\r\n    let editComplex = async (data: { value: any, propertyPath: string }) => {\r\n      let pg = new VisualizationPropertyGrid();\r\n      pg.visualizationHandler = this.visualizationHandler;\r\n      pg.visualizationShell = this.visualizationShell;\r\n      pg.serviceContainer = this.serviceContainer;\r\n      pg.instanceServiceContainer = this.instanceServiceContainer;\r\n      pg.bindableObjectsTarget = 'script';\r\n\r\n      pg.getTypeInfo = (obj, type) => typeInfoFromJsonSchema(this.propertiesTypeInfo, obj, type);\r\n      pg.showHead = false;\r\n      pg.typeName = 'IScriptMultiplexValue';\r\n      pg.title = 'Complex for \"' + data.propertyPath + '\"';\r\n      if (typeof data.value === 'object')\r\n        pg.selectedObject = data.value ?? {};\r\n      else\r\n        pg.selectedObject = {};\r\n\r\n      pg.bindingDoubleClicked = (b) => {\r\n        if (b.bindabletype == 'property') {\r\n          pg.setPropertyValue('source', 'property');\r\n        } else if (b.bindabletype == 'context') {\r\n          pg.setPropertyValue('source', 'context');\r\n        } else {\r\n          pg.setPropertyValue('source', 'signal');\r\n        }\r\n        pg.setPropertyValue('name', b.fullName)\r\n        pg.refresh();\r\n      }\r\n      let res = await this.visualizationShell.openConfirmation(pg, { x: 100, y: 100, width: 400, height: 500, parent: this });\r\n      if (res) {\r\n        this._propertygrid.setPropertyValue(data.propertyPath, pg.selectedObject);\r\n        this._propertygrid.refresh();\r\n      }\r\n    }\r\n\r\n    this._propertygrid.visualizationHandler = this.visualizationHandler;\r\n    this._propertygrid.visualizationShell = this.visualizationShell;\r\n    this._propertygrid.serviceContainer = this.serviceContainer;\r\n    this._propertygrid.instanceServiceContainer = this.instanceServiceContainer;\r\n    this._propertygrid.bindableObjectsTarget = 'script';\r\n\r\n    this._propertygrid.getTypeInfo = (obj, type) => typeInfoFromJsonSchema(this.scriptCommandsTypeInfo, obj, type);\r\n    this._propertygrid.getSpecialEditorForType = async (property: IProperty, currentValue, propertyPath: string, wbRender: WbRenderEventType, additionalInfo?: any) => {\r\n      //@ts-ignore\r\n      if (!property.specialAllreadyAdded) {\r\n        //@ts-ignore\r\n        property.specialAllreadyAdded = true;\r\n        if (property.format === 'collection') {\r\n          //TODO: create a collection edt. in property grid control used\r\n        } else if ((typeof currentValue === 'object' && currentValue !== null) || property.format === 'complex') {\r\n          let rB = document.createElement('button');\r\n          rB.style.height = 'calc(100% - 6px)';\r\n          rB.style.position = 'relative';\r\n          rB.style.display = 'flex';\r\n          rB.style.justifyContent = 'center';\r\n          rB.style.width = '20px';\r\n          rB.style.boxSizing = 'content-box';\r\n          rB.innerText = 'del';\r\n          rB.onclick = () => {\r\n            this._propertygrid.setPropertyValue(propertyPath, undefined);\r\n            this._propertygrid.refresh();\r\n          }\r\n          wbRender.nodeElem.insertAdjacentElement('afterbegin', rB);\r\n\r\n          let d = document.createElement('div');\r\n          d.style.display = 'flex';\r\n          let sp = document.createElement('span');\r\n          if (currentValue)\r\n            sp.innerText = (currentValue.source ?? '') + ': ' + (currentValue.name ?? '');\r\n          else\r\n            sp.innerText = '';\r\n          sp.style.overflow = 'hidden';\r\n          sp.style.whiteSpace = 'nowrap';\r\n          sp.style.textOverflow = 'ellipsis';\r\n          sp.style.flexGrow = '1';\r\n          sp.title = JSON.stringify(currentValue);\r\n          d.appendChild(sp);\r\n          let b = document.createElement('button');\r\n          b.innerText = '...';\r\n          b.onclick = () => {\r\n            editComplex({ value: currentValue, propertyPath })\r\n          }\r\n          d.appendChild(b);\r\n          wbRender.nodeElem.style.display = 'flex';\r\n          return d;\r\n        } else {\r\n          let b = document.createElement('button');\r\n          b.style.height = 'calc(100% - 6px)';\r\n          b.style.position = 'relative';\r\n          b.style.display = 'flex';\r\n          b.style.justifyContent = 'center';\r\n          b.style.width = '20px';\r\n          b.style.boxSizing = 'content-box';\r\n          b.title = 'complex property value';\r\n          b.style.opacity = '0.2';\r\n          b.innerText = '...';\r\n          b.onclick = () => {\r\n            editComplex({ value: currentValue, propertyPath })\r\n          }\r\n          wbRender.nodeElem.insertAdjacentElement('afterbegin', b);\r\n          wbRender.nodeElem.style.display = 'flex';\r\n        }\r\n      }\r\n\r\n      return null;\r\n    }\r\n    this._propertygrid.propertyNodeContextMenu.on((data) => {\r\n      ContextMenu.show([{\r\n        title: 'edit complex value',\r\n        action: async () => {\r\n          editComplex(data);\r\n        }\r\n      },\r\n      {\r\n        title: 'edit string',\r\n        action: async () => {\r\n          const v = prompt(\"enter value:\");\r\n          if (v) {\r\n            this._propertygrid.setPropertyValue(data.propertyPath, v);\r\n            this._propertygrid.refresh();\r\n          }\r\n        }\r\n      },\r\n      {\r\n        title: 'remove complex value',\r\n        action: async () => {\r\n          this._propertygrid.setPropertyValue(data.propertyPath, undefined);\r\n          this._propertygrid.refresh();\r\n        }\r\n      }], data.event);\r\n    })\r\n  }\r\n\r\n  //Converter from TypscriptJsonSchema to our Property list...\r\n\r\n  private async addPossibleCommands() {\r\n    let commands = Object.keys(this.scriptCommandsTypeInfo.definitions).filter(x => this.scriptCommandsTypeInfo.definitions[x].type == 'object');\r\n\r\n    for (let c of commands) {\r\n      if (c == 'ScriptCommands')\r\n        continue;\r\n      let option = document.createElement('option');\r\n      option.innerText = c;\r\n      this._possibleCommands.add(option);\r\n    }\r\n  }\r\n\r\n  loadScript(script: Script) {\r\n    this._script = script;\r\n\r\n    let commandListTreeItems = [];\r\n\r\n    for (let c of this._script.commands) {\r\n      c = ScriptUpgrades.upgradeScriptCommand(c);\r\n      commandListTreeItems.push(this.createTreeItem(c));\r\n    };\r\n\r\n    this._commandListFancyTree = new Wunderbaum({\r\n      ...defaultOptions,\r\n      element: this._commandListDiv,\r\n      icon: false,\r\n      source: commandListTreeItems,\r\n      activate: (e) => {\r\n        this._propertygrid.selectedObject = e.node.data.data.item;\r\n      },\r\n      render: (e) => {\r\n        if (e.isNew) {\r\n          let span = e.nodeElem;\r\n          span.oncontextmenu = (ev) => {\r\n            e.node.setActive();\r\n            if (e.node.data.contextMenu) {\r\n              e.node.data.contextMenu(ev, e.node.data, e.node);\r\n            }\r\n            ev.preventDefault();\r\n            return false;\r\n          }\r\n        }\r\n      },\r\n      dnd: {\r\n        guessDropEffect: true,\r\n        preventRecursion: true,\r\n        preventVoidMoves: false,\r\n        serializeClipboardData: false,\r\n        dragStart: (e) => {\r\n          e.event.dataTransfer.effectAllowed = \"move\";\r\n          e.event.dataTransfer.dropEffect = \"move\";\r\n          return true;\r\n        },\r\n        dragEnter: (e) => {\r\n          e.event.dataTransfer.dropEffect = 'move';\r\n          return true;\r\n        },\r\n        dragOver: (e) => {\r\n          e.event.dataTransfer.dropEffect = 'move';\r\n        },\r\n        drop: async (e) => {\r\n          e.sourceNode.moveTo(e.node, e.region == 'before' ? 'before' : 'after');\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  private createTreeItem(currentItem: ScriptCommands) {\r\n    let cti = {\r\n      title: currentItem.type,\r\n      data: { item: currentItem },\r\n      contextMenu: (e, data, node) => {\r\n        ContextMenu.show([{ title: 'Remove Item', action: (e) => node.remove() }], e);\r\n      }\r\n    };\r\n    return cti;\r\n  }\r\n\r\n  addItem() {\r\n    const cmdName = this._possibleCommands.value;\r\n    const command = { type: cmdName }\r\n    const ti = this.createTreeItem(<any>command);\r\n    this._commandListFancyTree.addChildren(ti);\r\n  }\r\n\r\n  getScriptCommands() {\r\n    let children = this._commandListFancyTree.root.children;\r\n    return children.map(x => x.data.data.item);\r\n  }\r\n}\r\n\r\ncustomElements.define(SimpleScriptEditor.is, SimpleScriptEditor);"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/components/VisualizationPropertyGrid.ts",
    "content": "import { IProperty, PropertyGrid } from '@node-projects/propertygrid.webcomponent'\r\nimport { WbRenderEventType } from \"types\";\r\nimport { VisualizationHandler } from '../interfaces/VisualizationHandler';\r\nimport { VisualizationShell } from '../interfaces/VisualizationShell';\r\nimport { CodeViewMonaco } from '@node-projects/web-component-designer-codeview-monaco';\r\nimport { BindableObjectsTarget, IBindableObject, InstanceServiceContainer, ServiceContainer } from '@node-projects/web-component-designer';\r\n\r\nexport class VisualizationPropertyGrid extends PropertyGrid {\r\n    public serviceContainer: ServiceContainer;\r\n    public instanceServiceContainer: InstanceServiceContainer;\r\n    public visualizationHandler: VisualizationHandler;\r\n    public visualizationShell: VisualizationShell;\r\n    public bindableObjectsTarget: BindableObjectsTarget = 'property';\r\n\r\n    constructor() {\r\n        super();\r\n    }\r\n\r\n    bindingDoubleClicked: (bindableObject: IBindableObject<any>) => void;\r\n\r\n    public override async getEditorForType(property: IProperty, currentValue, propertyPath: string, wbRender: WbRenderEventType, additionalInfo?: any): Promise<HTMLElement> {\r\n        if (this.getSpecialEditorForType) {\r\n            let edt = await this.getSpecialEditorForType(property, currentValue, propertyPath, wbRender, additionalInfo);\r\n            if (edt)\r\n                return edt;\r\n        }\r\n\r\n        switch (property.format) {\r\n            case 'screen': {\r\n                let editor = document.createElement('select');\r\n                editor.style.width = '100%';\r\n\r\n                const akop = document.createElement('option');\r\n                akop.value = currentValue ?? '';\r\n                akop.innerText = currentValue ?? '';\r\n                editor.appendChild(akop);\r\n\r\n                for (let v of await this.visualizationHandler.getAllNames('screen')) {\r\n                    if (v == currentValue)\r\n                        continue;\r\n                    const op = document.createElement('option');\r\n                    op.value = v;\r\n                    op.innerText = v;\r\n                    editor.appendChild(op);\r\n                }\r\n                editor.onchange = () => {\r\n                    this.setPropertyValue(propertyPath, editor.value);\r\n                };\r\n                editor.value = currentValue;\r\n                return editor;\r\n            }\r\n            case 'signal': {\r\n                let cnt = document.createElement('div');\r\n                cnt.style.display = 'flex';\r\n                let inp = document.createElement('input');\r\n                inp.value = currentValue ?? '';\r\n                inp.style.flexGrow = '1';\r\n                inp.style.width = '0';\r\n                inp.onchange = (e) => this.setPropertyValue(propertyPath, inp.value);\r\n                inp.onfocus = (e) => {\r\n                    inp.selectionStart = 0;\r\n                    inp.selectionEnd = inp.value?.length;\r\n                }\r\n                cnt.appendChild(inp);\r\n                let btn = document.createElement('button');\r\n                btn.textContent = '...';\r\n                btn.onclick = async () => {\r\n                    let b = this.visualizationShell.createBindableObjectBrowser();\r\n                    b.initialize(this.serviceContainer, this.instanceServiceContainer, this.bindableObjectsTarget);\r\n                    b.title = 'select signal...';\r\n                    const abortController = new AbortController();\r\n                    b.objectDoubleclicked.on(() => {\r\n                        if (this.bindingDoubleClicked)\r\n                            this.bindingDoubleClicked(b.selectedObject);\r\n                        abortController.abort();\r\n                        inp.value = b.selectedObject.fullName;\r\n                        this.setPropertyValue(propertyPath, inp.value);\r\n                    });\r\n                    let res = await this.visualizationShell.openConfirmation(b, { x: 100, y: 100, width: 400, height: 300, parent: this, abortSignal: abortController.signal });\r\n                    if (res) {\r\n                        inp.value = b.selectedObject.fullName;\r\n                        this.setPropertyValue(propertyPath, inp.value);\r\n                    }\r\n                }\r\n                cnt.appendChild(btn);\r\n                return cnt;\r\n\r\n            }\r\n            case 'html':\r\n            case 'script': {\r\n                let editor = document.createElement('div');\r\n                editor.style.boxSizing = 'border-box';\r\n                editor.style.width = '100%';\r\n                editor.style.display = 'flex';\r\n\r\n                let inp = document.createElement('textarea');\r\n                inp.style.boxSizing = 'border-box';\r\n                inp.value = currentValue ?? '';\r\n                inp.style.width = '100%';\r\n                inp.onblur = e => { this.setPropertyValue(propertyPath, inp.value); }\r\n                editor.appendChild(inp);\r\n\r\n                let btn = document.createElement('button');\r\n                btn.innerHTML = '...';\r\n                btn.style.boxSizing = 'border-box';\r\n                btn.onclick = async () => {\r\n                    let cvm = new CodeViewMonaco();\r\n                    const monaco = await CodeViewMonaco.getMonacoLib();\r\n                    if (property.format == 'html') {\r\n                        cvm.language = 'html';\r\n                    } else {\r\n                        let monacoInfo = {\r\n                            content: `declare global {\r\n                            var context: { event: Event, element: Element };\r\n                        }`, filePath: 'global.d.ts'\r\n                        }\r\n                        //@ts-ignore\r\n                        monaco.languages.typescript.typescriptDefaults.setExtraLibs([monacoInfo]);\r\n                        cvm.language = 'javascript';\r\n                    }\r\n                    cvm.code = inp.value;\r\n                    cvm.style.position = 'relative';\r\n                    let res = await this.visualizationShell.openConfirmation(cvm, { x: 200, y: 200, width: 600, height: 400, parent: this });\r\n                    if (res) {\r\n                        inp.value = cvm.getText();\r\n                        this.setPropertyValue(propertyPath, inp.value);\r\n                    }\r\n                }\r\n                editor.appendChild(btn);\r\n\r\n                return editor;\r\n            }\r\n        }\r\n\r\n        return super.getEditorForType(property, currentValue, propertyPath, wbRender, additionalInfo);\r\n    }\r\n}\r\n\r\ncustomElements.define(\"node-projects-visualization-property-grid\", VisualizationPropertyGrid);\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/helpers/BindingsHelper.ts",
    "content": "import { TypedEvent, cssFromString } from \"@node-projects/base-custom-webcomponent\";\r\nimport { VisualizationBinding } from \"../interfaces/VisualizationBinding.js\";\r\nimport { State, VisualizationHandler } from \"../interfaces/VisualizationHandler.js\";\r\nimport { BindingTarget } from \"@node-projects/web-component-designer/dist/elements/item/BindingTarget.js\";\r\nimport { PropertiesHelper } from \"@node-projects/web-component-designer/dist/elements/services/propertiesService/services/PropertiesHelper.js\";\r\n\r\n//;,[ are not allowed in bindings, so they could be used for a short form...\r\n\r\nexport type SpecialValueHandler = { valueProvider: (propertyName: string, context: { element: Element, binding?: namedBinding, relativeSignalPath: string, root: HTMLElement, [key: string]: any }) => Promise<any> | any, valueChangedCallbacks: Map<string, (() => void)[]> }\r\n\r\nexport const bindingPrefixProperty = 'bind-prop:';\r\nexport const bindingPrefixAttribute = 'bind-attr:';\r\nexport const bindingPrefixClass = 'bind-class:';\r\nexport const bindingPrefixCss = 'bind-css:';\r\nexport const bindingPrefixCssVar = 'bind-cssvar:';\r\nexport const bindingPrefixContent = 'bind-content:';\r\nexport const bindingPrefixVisible = 'bind-visible:';\r\nexport const bindingPrefixInsideCss = 'bind(';\r\nexport const bindingPrefixInsideCssVarName = '--tmpBinding_';\r\n\r\nexport const bindingsInCssRegex = /{{(.*)}}/;\r\n\r\nexport type namedBinding = [name: string, binding: VisualizationBinding];\r\n\r\nexport function isLit(element: Element) {\r\n  //@ts-ignore\r\n  return element.constructor?.elementProperties != null;\r\n}\r\n\r\nexport function parseBindingString(id: string) {\r\n  let parts: string[] = [];\r\n  let signals: string[] = [];\r\n  let tx = '';\r\n  for (let n = 0; n < id.length; n++) {\r\n    if (id[n] == '{') {\r\n      parts.push(tx);\r\n      tx = '';\r\n    } else if (id[n] == '}') {\r\n      signals.push(tx);\r\n      tx = '';\r\n    } else {\r\n      tx += id[n];\r\n    }\r\n  }\r\n  parts.push(tx);\r\n  return { parts, signals };\r\n}\r\n\r\nexport function getNestedProperty(obj, path) {\r\n  const parts = path.split('.');\r\n  let current = obj;\r\n\r\n  for (const part of parts) {\r\n    if (current == null || typeof current !== 'object') return undefined;\r\n    current = current[part];\r\n  }\r\n\r\n  return current;\r\n}\r\n\r\nclass IndirectSignal {\r\n  private parts: string[];\r\n  private signals: string[];\r\n  private values: string[];\r\n  private unsubscribeTargetValue: [((id: string, value: any) => void), any];\r\n  private cleanupCalls = [];\r\n  private combinedName: string;\r\n  private disposed: boolean;\r\n  private valueChangedCb: (value: any) => void\r\n  private visualizationHandler: VisualizationHandler;\r\n  private element: Element;\r\n  private relativeSignalPath: string;\r\n\r\n  constructor(bindingsHelper: BindingsHelper, visualizationHandler: VisualizationHandler, id: string, valueChangedCb: (value: State) => void, element: Element, relativeSignalPath: string, root: HTMLElement, specialValueHandler?: SpecialValueHandler) {\r\n    this.visualizationHandler = visualizationHandler;\r\n    this.valueChangedCb = valueChangedCb;\r\n    this.element = element;\r\n    this.relativeSignalPath = relativeSignalPath;\r\n    this.parseIndirectBinding(id);\r\n    this.values = new Array(this.signals.length);\r\n    for (let i = 0; i < this.signals.length; i++) {\r\n      let nm = this.signals[i];\r\n      if (nm[0] === '?' && nm[1] === '?') {\r\n        const propNm = nm.substring(2);\r\n        if (!propNm.includes('.')) {\r\n          this.handleValueChanged(root[propNm], i);\r\n\r\n          const evtCallback = () => this.handleValueChanged(root[propNm], i);\r\n          const evtName = bindingsHelper.getChangedEventName(root, propNm);\r\n          root.addEventListener(evtName, (evtCallback));\r\n          this.cleanupCalls.push(() => root.removeEventListener(evtName, evtCallback));\r\n        } else {\r\n          const val = getNestedProperty(root, propNm);\r\n          this.handleValueChanged(val, i);\r\n        }\r\n        continue;\r\n      } else if (nm[0] === '#' && nm[1] === '#') {\r\n        const propNm = nm.substring(2);\r\n        if (!propNm.includes('.')) {\r\n          this.handleValueChanged(element[propNm], i);\r\n\r\n          const evtCallback = () => this.handleValueChanged(element[propNm], i);\r\n          const evtName = bindingsHelper.getChangedEventName(element, propNm);\r\n          element.addEventListener(evtName, (evtCallback));\r\n          this.cleanupCalls.push(() => element.removeEventListener(evtName, evtCallback));\r\n        } else {\r\n          const val = getNestedProperty(element, propNm);\r\n          this.handleValueChanged(val, i);\r\n        }\r\n        continue;\r\n      } else if (nm[0] === '§') {\r\n        const mS = nm.substring(1);\r\n        const value = specialValueHandler.valueProvider(mS, { element, relativeSignalPath, root });\r\n        if (value instanceof Promise)\r\n          value.then(v => this.handleValueChanged(v, i));\r\n        else\r\n          this.handleValueChanged(value, i);\r\n        if (!specialValueHandler.valueChangedCallbacks)\r\n          specialValueHandler.valueChangedCallbacks = new Map();\r\n        let changeList = specialValueHandler.valueChangedCallbacks.get(mS);\r\n        if (changeList == null) {\r\n          changeList = [];\r\n          specialValueHandler.valueChangedCallbacks.set(mS, changeList);\r\n        }\r\n        changeList.push(() => {\r\n          const value = specialValueHandler.valueProvider(mS, { element, relativeSignalPath, root })\r\n          if (value instanceof Promise)\r\n            value.then(v => this.handleValueChanged(v, i));\r\n          else\r\n            this.handleValueChanged(value, i);\r\n        });\r\n      } else if (nm[0] === '?') {\r\n        //TODO: react to changes of signal name in prop\r\n        if (!nm.includes('.')) {\r\n          nm = root[nm.substring(1)];\r\n        } else {\r\n          nm = getNestedProperty(root, nm.substring(1));\r\n        }\r\n      } else if (nm[0] === '#') {\r\n        //TODO: react to changes of signal name in prop\r\n        if (!nm.includes('.')) {\r\n          nm = root[nm.substring(1)];\r\n        } else {\r\n          nm = getNestedProperty(root, nm.substring(1));\r\n        }\r\n      }\r\n\r\n      let cb = (id: string, value: any) => this.handleValueChanged(value.val, i);\r\n      const subscr = this.visualizationHandler.subscribeState(nm, cb);\r\n      this.cleanupCalls.push(() => this.visualizationHandler.unsubscribeState(this.signals[i], cb, subscr));\r\n    }\r\n  }\r\n\r\n  private parseIndirectBinding(id: string) {\r\n    let { parts, signals } = parseBindingString(id);\r\n    this.parts = parts;\r\n    this.signals = signals;\r\n\r\n    for (let n = 0; n < signals.length; n++) {\r\n      if (signals[n][0] == '.')\r\n        signals[n] = this.visualizationHandler.getNormalizedSignalName(signals[n], this.relativeSignalPath, this.element);\r\n    }\r\n  }\r\n\r\n  handleValueChanged(value: any, index: number) {\r\n    this.values[index] = value;\r\n    let nm = this.parts[0];\r\n    for (let i = 0; i < this.parts.length - 1; i++) {\r\n      let v = this.values[i];\r\n      if (v == null)\r\n        return;\r\n      nm += v + this.parts[i + 1];\r\n    }\r\n    if (nm[0] == '.')\r\n      nm = this.visualizationHandler.getNormalizedSignalName(nm, this.relativeSignalPath, this.element);\r\n    if (this.combinedName != nm) {\r\n      if (this.unsubscribeTargetValue) {\r\n        this.visualizationHandler.unsubscribeState(this.combinedName, this.unsubscribeTargetValue[0], this.unsubscribeTargetValue[1]);\r\n      }\r\n      if (!this.disposed) {\r\n        this.combinedName = nm;\r\n        let cb = (id: string, value: any) => this.valueChangedCb(value);\r\n        this.unsubscribeTargetValue = [cb, this.visualizationHandler.subscribeState(nm, cb)];\r\n      }\r\n    }\r\n  }\r\n\r\n  dispose() {\r\n    this.disposed = true;\r\n    if (this.unsubscribeTargetValue) {\r\n      this.visualizationHandler.unsubscribeState(this.combinedName, this.unsubscribeTargetValue[0], this.unsubscribeTargetValue[1]);\r\n      this.unsubscribeTargetValue = null;\r\n    }\r\n    for (let i = 0; i < this.signals.length; i++) {\r\n      this.cleanupCalls[i]();\r\n    }\r\n  }\r\n\r\n  setState(value) {\r\n    if (!this.disposed) {\r\n      this.visualizationHandler.setState(this.combinedName, value);\r\n    }\r\n  }\r\n}\r\n\r\nexport class BindingsHelper {\r\n  _visualizationHandler: VisualizationHandler;\r\n  namedConverterCallback: (converter: string, value: any, element: Element, binding: namedBinding) => any;\r\n\r\n  constructor(visualizationHandler: VisualizationHandler) {\r\n    this._visualizationHandler = visualizationHandler;\r\n  }\r\n\r\n  getChangedEventName(element: Element, propertyName: string) {\r\n    const posColon = propertyName.indexOf('::');\r\n    if (posColon >= 0)\r\n      return propertyName.substring(posColon + 2);\r\n    if (element instanceof HTMLInputElement)\r\n      return 'change';\r\n    if (element instanceof HTMLSelectElement)\r\n      return 'change';\r\n    if (isLit(element))\r\n      return PropertiesHelper.camelToDashCase(propertyName);\r\n    return PropertiesHelper.camelToDashCase(propertyName) + '-changed';\r\n  }\r\n\r\n  //Not allowed chars in Var Names: |{}(),;:[]\r\n\r\n  parseBinding(element: Element, name: string, value: string, bindingTarget: BindingTarget, prefix: string): namedBinding {\r\n    //Loooks like:\r\n    //a:var1;b:var2;expression\r\n    //=varname => two way\r\n    //!varname => inverted\r\n    //{...} => json\r\n    let propname = name.substring(prefix.length);\r\n    if (bindingTarget === BindingTarget.cssvar)\r\n      propname = '--' + propname;\r\n    if (!value.startsWith('{')) {\r\n      let binding: VisualizationBinding = {\r\n        signal: value,\r\n        target: bindingTarget\r\n      }\r\n\r\n      if (value[0] === '=') {\r\n        value = value.substring(1);\r\n        binding.signal = value;\r\n        if (value.includes('::')) {\r\n          const parts = value.split('::');\r\n          value = parts[0];\r\n          binding.signal = value;\r\n          binding.events = parts[1].split(',');\r\n        }\r\n        binding.twoWay = true;\r\n        if (!binding.events) {\r\n          if (element instanceof HTMLInputElement)\r\n            binding.events = [this.getChangedEventName(element, propname)];\r\n          else if (element instanceof HTMLSelectElement)\r\n            binding.events = [this.getChangedEventName(element, propname)];\r\n          else {\r\n            if (isLit(element)) {\r\n              binding.events = [this.getChangedEventName(element, propname)];\r\n            } else {\r\n              binding.events = [this.getChangedEventName(element, propname)];\r\n              //Binding could be a lit elemnt but not yet loaded\r\n              binding.maybeLitElement = true;\r\n              binding.litEventNames = [this.getChangedEventName(element, propname)];\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      if (value[0] === '!') {\r\n        binding.signal = value.substring(1);\r\n        binding.inverted = true;\r\n      }\r\n\r\n      if (binding.signal.includes(';')) {\r\n        const parts = binding.signal.split(';');\r\n        binding.expression = parts.pop();\r\n        binding.signal = parts.join(';');\r\n      }\r\n\r\n      if (bindingTarget === BindingTarget.cssvar || bindingTarget === BindingTarget.class)\r\n        return [BindingsHelper.dotToCamelCase(propname), binding];\r\n      if (bindingTarget === BindingTarget.attribute)\r\n        return [propname, binding];\r\n      return [PropertiesHelper.dashToCamelCase(propname), binding];\r\n    }\r\n\r\n    let binding: VisualizationBinding = JSON.parse(value);\r\n    binding.target = bindingTarget;\r\n\r\n    if (binding.twoWay && (binding.events == null || binding.events.length == 0)) {\r\n      if (element instanceof HTMLInputElement)\r\n        binding.events = ['change'];\r\n      else if (element instanceof HTMLSelectElement)\r\n        binding.events = ['change'];\r\n      else {\r\n        binding.events = [this.getChangedEventName(element, propname)];\r\n      }\r\n    }\r\n    if (bindingTarget === BindingTarget.cssvar || bindingTarget === BindingTarget.class)\r\n      return [BindingsHelper.dotToCamelCase(propname), binding];\r\n    if (bindingTarget === BindingTarget.attribute)\r\n      return [propname, binding];\r\n    return [PropertiesHelper.dashToCamelCase(propname), binding];\r\n  }\r\n\r\n  serializeBinding(element: Element, targetName: string, binding: VisualizationBinding): [name: string, value: string] {\r\n    let bindingCopy = { ...binding };\r\n    delete bindingCopy.type;\r\n    if (!binding.twoWay) {\r\n      delete bindingCopy.events;\r\n      delete bindingCopy.expressionTwoWay;\r\n    } else if ((binding.events != null && binding.events.length == 1)) {\r\n      if (element instanceof HTMLInputElement && binding.events?.[0] == \"change\")\r\n        delete bindingCopy.events;\r\n      else if (element instanceof HTMLSelectElement && binding.events?.[0] == \"change\")\r\n        delete bindingCopy.events;\r\n      else if (isLit(element) && binding.events?.[0] == targetName)\r\n        delete bindingCopy.events;\r\n      else if (!isLit(element) && binding.events?.[0] == targetName + '-changed')\r\n        delete bindingCopy.events;\r\n    }\r\n\r\n    const eventsString = bindingCopy.twoWay && bindingCopy.events?.length > 0 ? '::' + bindingCopy.events.join(',') : '';\r\n\r\n    let needsJson = false;\r\n    if (eventsString && binding.expression?.includes('::') || binding.expressionTwoWay?.includes('::'))\r\n      needsJson = true;\r\n    if (binding.signal.trim()[0] == '{')\r\n      needsJson = true;\r\n\r\n    if (!needsJson && binding.target == BindingTarget.property &&\r\n      !binding.expression && !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      if (targetName == 'textContent')\r\n        return [bindingPrefixContent + 'text', (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n      if (targetName == 'innerHTML')\r\n        return [bindingPrefixContent + 'html', (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n      return [bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n    }\r\n\r\n    //Multi Var Expressions\r\n    if (!needsJson && binding.target == BindingTarget.property &&\r\n      binding.expression && !binding.expression.includes(\"\\n\") && !binding.expression.includes(\";\") &&\r\n      !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      if (targetName == 'textContent')\r\n        return [bindingPrefixContent + 'text', (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n      if (targetName == 'innerHTML')\r\n        return [bindingPrefixContent + 'html', (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n      return [bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n    }\r\n\r\n    if (!needsJson && binding.target == BindingTarget.attribute &&\r\n      !binding.expression && !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n    }\r\n\r\n    //Multi Var Expressions\r\n    if (!needsJson && binding.target == BindingTarget.attribute &&\r\n      binding.expression && !binding.expression.includes(\"\\n\") && !binding.expression.includes(\";\") &&\r\n      !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName), (binding.twoWay ? '=' : '') + (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n    }\r\n\r\n    if (!needsJson && binding.target == BindingTarget.class &&\r\n      !binding.expression && !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n    }\r\n\r\n    //Multi Var Expressions\r\n    if (!needsJson && binding.target == BindingTarget.class &&\r\n      binding.expression && !binding.expression.includes(\"\\n\") && !binding.expression.includes(\";\") &&\r\n      !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixClass + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n    }\r\n\r\n    if (!needsJson && binding.target == BindingTarget.css &&\r\n      !binding.expression && !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n    }\r\n\r\n\r\n    //Multi Var Expressions\r\n    if (!needsJson && binding.target == BindingTarget.css &&\r\n      binding.expression && !binding.expression.includes(\"\\n\") && !binding.expression.includes(\";\") &&\r\n      !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n    }\r\n\r\n    if (!needsJson && binding.target == BindingTarget.cssvar &&\r\n      !binding.expression && !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2)), (binding.inverted ? '!' : '') + binding.signal + (!binding.twoWay && binding.signal.includes(';') ? ';' : '') + eventsString];\r\n    }\r\n\r\n    //Multi Var Expressions\r\n    if (!needsJson && binding.target == BindingTarget.cssvar &&\r\n      binding.expression && !binding.expression.includes(\"\\n\") && !binding.expression.includes(\";\") &&\r\n      !binding.expressionTwoWay &&\r\n      binding.converter == null &&\r\n      //!binding.type &&\r\n      !binding.historic &&\r\n      !binding.writeBackSignal) {\r\n      return [bindingPrefixCssVar + PropertiesHelper.camelToDashCase(targetName), (binding.inverted ? '!' : '') + binding.signal + ';' + binding.expression + eventsString];\r\n    }\r\n\r\n    if (binding.inverted === null || binding.inverted === false) {\r\n      delete bindingCopy.inverted;\r\n    }\r\n    if (binding.expression === null || binding.expression === '') {\r\n      delete bindingCopy.expression;\r\n    }\r\n    if (binding.expressionTwoWay === null || binding.expressionTwoWay === '') {\r\n      delete bindingCopy.expressionTwoWay;\r\n    }\r\n    if (binding.twoWay === null || binding.twoWay === false) {\r\n      delete bindingCopy.twoWay;\r\n    }\r\n    /*if (binding.type === null || binding.type === '') {\r\n      delete bindingCopy.type;\r\n    }*/\r\n    delete bindingCopy.target;\r\n\r\n    if (!binding.historic) {\r\n      delete bindingCopy.historic;\r\n    }\r\n\r\n    if (binding.target == BindingTarget.content)\r\n      return [bindingPrefixContent + 'html', JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.attribute)\r\n      return [bindingPrefixAttribute + PropertiesHelper.camelToDashCase(targetName), JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.class)\r\n      return [bindingPrefixClass + BindingsHelper.camelToDotCase(targetName), JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.css)\r\n      return [bindingPrefixCss + PropertiesHelper.camelToDashCase(targetName), JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.cssvar)\r\n      return [bindingPrefixCssVar + BindingsHelper.camelToDotCase(targetName.substring(2)), JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.property && targetName == 'innerHTML')\r\n      return [bindingPrefixContent + 'html', JSON.stringify(bindingCopy)];\r\n    if (binding.target == BindingTarget.property && targetName == 'textContent')\r\n      return [bindingPrefixContent + 'text', JSON.stringify(bindingCopy)];\r\n    return [bindingPrefixProperty + PropertiesHelper.camelToDashCase(targetName), JSON.stringify(bindingCopy)];\r\n  }\r\n\r\n  getBindingAttributeName(element: Element, propertyName: string, propertyTarget: BindingTarget) {\r\n    if (propertyTarget == BindingTarget.attribute) {\r\n      return bindingPrefixAttribute + PropertiesHelper.camelToDashCase(propertyName);\r\n    }\r\n    if (propertyTarget == BindingTarget.class) {\r\n      return bindingPrefixClass + BindingsHelper.camelToDotCase(propertyName);\r\n    }\r\n    if (propertyTarget == BindingTarget.css) {\r\n      return bindingPrefixCss + PropertiesHelper.camelToDashCase(propertyName);\r\n    }\r\n    if (propertyTarget == BindingTarget.visible) {\r\n      return bindingPrefixVisible;\r\n    }\r\n    if (propertyTarget == BindingTarget.cssvar) {\r\n      return bindingPrefixCssVar + BindingsHelper.camelToDotCase(propertyName);\r\n    }\r\n    if (propertyTarget == BindingTarget.property && propertyName == 'innerHTML') {\r\n      return bindingPrefixContent + 'html';\r\n    }\r\n    if (propertyTarget == BindingTarget.property && propertyName == 'textContent') {\r\n      return bindingPrefixContent + 'text';\r\n    }\r\n    return bindingPrefixProperty + PropertiesHelper.camelToDashCase(propertyName);\r\n  }\r\n\r\n  *getBindings(element: Element) {\r\n    if (element.attributes) {\r\n      for (let a of element.attributes) {\r\n        if (a.name.startsWith(bindingPrefixProperty)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.property, bindingPrefixProperty);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixContent)) {\r\n          yield this.parseBinding(element, a.name === 'bind-content:html' ? 'bind-prop:inner-h-t-m-l' : 'bind-prop:text-content', a.value, BindingTarget.property, bindingPrefixProperty);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixAttribute)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.attribute, bindingPrefixAttribute);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixClass)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.class, bindingPrefixClass);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixCss)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.css, bindingPrefixCss);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixCssVar)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.cssvar, bindingPrefixCssVar);\r\n        }\r\n        else if (a.name.startsWith(bindingPrefixVisible)) {\r\n          yield this.parseBinding(element, a.name, a.value, BindingTarget.visible, bindingPrefixVisible);\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  applyAllBindings(rootElement: ParentNode, relativeSignalPath: string, root: HTMLElement, specialValueHandler?: SpecialValueHandler, skipChildrenFor?: (element: Element) => boolean): (() => void)[] {\r\n    let retVal: (() => void)[] = [];\r\n    const tw = document.createTreeWalker(rootElement, NodeFilter.SHOW_ELEMENT);\r\n    let e: Element;\r\n    while (e = <Element>tw.nextNode()) {\r\n      const bindings = this.getBindings(e);\r\n      for (let b of bindings) {\r\n        try {\r\n          let applied = this.applyBinding(e, b, relativeSignalPath, root, specialValueHandler);\r\n          retVal.push(applied);\r\n\r\n          if (b[1].maybeLitElement && e.localName.includes('-') && !customElements.get(e.localName)) {\r\n            const el = e;\r\n            const bnd = b;\r\n            customElements.whenDefined(e.localName).then(() => {\r\n              if (isLit(el)) {\r\n                applied();\r\n                bnd[1].events = bnd[1].litEventNames;\r\n                retVal.push(this.applyBinding(el, bnd, relativeSignalPath, root, specialValueHandler));\r\n              }\r\n            })\r\n          }\r\n        } catch (err) {\r\n          console.warn(\"error applying binding\", e, b, err)\r\n        }\r\n      }\r\n      if (skipChildrenFor) {\r\n        if (skipChildrenFor(e)) {\r\n          e = <Element>tw.nextSibling();\r\n          continue;\r\n        }\r\n      }\r\n    }\r\n    return retVal;\r\n  }\r\n\r\n  static #cssBindingsVarId = 0;\r\n\r\n  async parseCssBindings(sheet: string, element: Element, relativeSignalPath: string, root: HTMLElement): Promise<[stylesheet: CSSStyleSheet, unsub: (() => void)[]]> {\r\n    const parser = (await import(\"@node-projects/css-parser\"));\r\n    const ast = parser.parse(sheet);\r\n\r\n    let unsub: (() => void)[];\r\n    for (let r of ast.stylesheet.rules) {\r\n      if (r.type === parser.CssTypes.rule) {\r\n        for (const d of r.declarations) {\r\n          if (d.type === parser.CssTypes.declaration) {\r\n            if (d.value.includes(bindingPrefixInsideCss)) {\r\n              const newValue = this.parseCssBinding(d.value, element, relativeSignalPath, root);\r\n              d.value = newValue[0];\r\n              if (unsub) {\r\n                unsub.push(...newValue[1])\r\n              } else {\r\n                unsub = newValue[1];\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    const newStyle = parser.stringify(ast, { indent: '', compress: true });\r\n    return [cssFromString(newStyle), unsub];\r\n  }\r\n\r\n  parseCssBinding(value: string, element: Element, relativeSignalPath: string, root: HTMLElement, specialValueHandler?: SpecialValueHandler): [name: string, unsub: (() => void)[]] {\r\n    value = value.trim();\r\n    let res = '';\r\n    let tmp = '';\r\n    let inBind = false;\r\n    let binding = '';\r\n    let escape = false;\r\n    let quote = null;\r\n    let unsub: (() => void)[] = [];\r\n    for (let n = 0; n < value.length; n++) {\r\n      const c = value[n];\r\n      if (inBind) {\r\n        if (escape)\r\n          binding += c;\r\n        else if (quote && c === '\\\\')\r\n          escape = true;\r\n        else if (c === quote)\r\n          quote = null;\r\n        else if (quote === null && c === ')') {\r\n          const id = BindingsHelper.#cssBindingsVarId++;\r\n          const varName = bindingPrefixInsideCssVarName + id;\r\n          let bnd: namedBinding = [varName, { signal: binding, target: BindingTarget.cssvar }];\r\n          if (binding.startsWith('{')) {\r\n            bnd = JSON.parse(binding);\r\n          }\r\n          unsub.push(this.applyBinding(element, bnd, relativeSignalPath, root, specialValueHandler));\r\n          res += 'var(' + varName + ')';\r\n          inBind = false;\r\n          binding = '';\r\n        } else\r\n          binding += c;\r\n      } else if (c === '(') {\r\n        if (tmp !== 'bind') {\r\n          res += tmp;\r\n          res += c;\r\n        } else {\r\n          inBind = true;\r\n          if (value[n + 1] === '\\'' || value[n + 1] === '\"') {\r\n            n++;\r\n            quote = value[n];\r\n          } else {\r\n            quote = null;\r\n          }\r\n        }\r\n        tmp = '';\r\n      } else if (c === ' ' || c === ',' || c === '(' || c === '+' || c === '-' || c === '*' || c === '/') {\r\n        res += tmp + c;\r\n        tmp = '';\r\n      } else {\r\n        tmp += c;\r\n      }\r\n    }\r\n    return [res + tmp, unsub];\r\n  }\r\n\r\n  /**\r\n   * ? = bind to signals in properties\r\n   * ?? = binding to a property\r\n   * $ = bind to a signal configuration\r\n   * § = bind to a special value \r\n   * @param element \r\n   * @param binding \r\n   * @param relativeSignalPath \r\n   * @param root \r\n   * @returns \r\n   */\r\n  applyBinding(element: Element, binding: namedBinding, relativeSignalPath: string, root: HTMLElement, specialValueHandler?: SpecialValueHandler): () => void {\r\n    let unsubscribeList: [id: string, ((id: string, value: any) => void), any][] = [];\r\n    let cleanupCalls: (() => void)[];\r\n    const cleanUp = () => {\r\n      for (const u of unsubscribeList) {\r\n        this._visualizationHandler.unsubscribeState(u[0], u[1], u[2]);\r\n      }\r\n      if (cleanupCalls) {\r\n        for (let e of cleanupCalls) {\r\n          e();\r\n        }\r\n      }\r\n    };\r\n\r\n    const signals = binding[1].signal.split(';');\r\n    const signalVars: string[] = new Array(signals.length);\r\n    for (let i = 0; i < signals.length; i++) {\r\n      let sng = signals[i];\r\n      signalVars[i] = '__' + i;\r\n      if (sng.includes(':')) {\r\n        const spl = sng.split(':');\r\n        signalVars[i] = spl[0];\r\n        sng = spl[1];\r\n        signals[i] = sng;\r\n      }\r\n      if (sng[0] === '?') { //access object path in property in custom control, todo: bind direct to property value in local property\r\n        if (root) { //root is null when opened in designer, then do not apply property bindings\r\n          let s = sng.substring(1);\r\n          if (s[0] == '?') {\r\n            signals[i] = s;\r\n          } else {\r\n            signals[i] = root[s];\r\n            if (s[0] === '$') {\r\n              s = s.substring(1);\r\n              signals[i] = '$' + root[s];\r\n            }\r\n            let evtCallback = () => {\r\n              cleanUp();\r\n              this.applyBinding(element, binding, relativeSignalPath, root, specialValueHandler);\r\n            };\r\n            const evtNm = this.getChangedEventName(root, s);\r\n            root.addEventListener(evtNm, evtCallback);\r\n            if (!cleanupCalls)\r\n              cleanupCalls = [];\r\n            cleanupCalls.push(() => root.removeEventListener(evtNm, evtCallback));\r\n          }\r\n        }\r\n      }\r\n      else if (sng[0] === '#') { //access object path in target element        \r\n        let s = sng.substring(1);\r\n        if (s[0] == '#') {\r\n          signals[i] = s;\r\n        } else {\r\n          signals[i] = root[s];\r\n          if (s[0] === '$') {\r\n            s = s.substring(1);\r\n            signals[i] = '$' + root[s];\r\n          }\r\n          let evtCallback = () => {\r\n            cleanUp();\r\n            this.applyBinding(element, binding, relativeSignalPath, root, specialValueHandler);\r\n          };\r\n          const evtNm = this.getChangedEventName(element, s);\r\n          root.addEventListener(evtNm, evtCallback);\r\n          if (!cleanupCalls)\r\n            cleanupCalls = [];\r\n          cleanupCalls.push(() => root.removeEventListener(evtNm, evtCallback));\r\n        }\r\n      }\r\n      if (sng[0] === '.') {\r\n        signals[i] = this._visualizationHandler.getNormalizedSignalName(sng, relativeSignalPath, element);\r\n      }\r\n    }\r\n\r\n    let valuesObject = new Array(signals.length);\r\n    for (let i = 0; i < signals.length; i++) {\r\n      const s = signals[i];\r\n      if (s[0] === '?') {\r\n        if (root) {\r\n          const nm = s.substring(1);\r\n          let evtCallback = () => {\r\n            let disableValueChanged = false;\r\n            if (!disableValueChanged) {\r\n              disableValueChanged = true;\r\n              this.handleValueChanged(element, root, binding, root[nm], valuesObject, i, signalVars, false, relativeSignalPath);\r\n              disableValueChanged = false;\r\n            }\r\n          };\r\n          root.addEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback);\r\n          if (!cleanupCalls)\r\n            cleanupCalls = [];\r\n          cleanupCalls.push(() => root.removeEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback));\r\n          try {\r\n            this.handleValueChanged(element, root, binding, root[nm], valuesObject, i, signalVars, false, relativeSignalPath);\r\n          } catch (err) {\r\n            console.error(err);\r\n          }\r\n          if (binding[1].twoWay && i == 0) {\r\n            this.addTwoWayBinding(binding, element, v => root[nm] = v);\r\n          }\r\n        }\r\n      } else if (s[0] === '#') { //Binding to element properties\r\n        const nm = s.substring(1);\r\n        let evtCallback = () => {\r\n          let disableValueChanged = false;\r\n          if (!disableValueChanged) {\r\n            disableValueChanged = true;\r\n            this.handleValueChanged(element, root, binding, element[nm], valuesObject, i, signalVars, false, relativeSignalPath);\r\n            disableValueChanged = false;\r\n          }\r\n        };\r\n        element.addEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback);\r\n        if (!cleanupCalls)\r\n          cleanupCalls = [];\r\n        cleanupCalls.push(() => element.removeEventListener(PropertiesHelper.camelToDashCase(nm) + '-changed', evtCallback));\r\n        try {\r\n          this.handleValueChanged(element, root, binding, element[nm], valuesObject, i, signalVars, false, relativeSignalPath);\r\n        } catch (err) {\r\n          console.error(err);\r\n        }\r\n        if (binding[1].twoWay && i == 0) {\r\n          this.addTwoWayBinding(binding, element, v => element[nm] = v);\r\n        }\r\n      } else if (s[0] === '$') {\r\n        let mS = s.substring(1);\r\n        if (mS[0] === '.') {\r\n          mS = this._visualizationHandler.getNormalizedSignalName(mS, relativeSignalPath, element);\r\n        }\r\n        this._visualizationHandler.getObject(mS).then(x => {\r\n          this.handleValueChanged(element, root, binding, x, valuesObject, i, signalVars, true, relativeSignalPath);\r\n        });\r\n      } else if (s[0] === '§') {\r\n        const mS = s.substring(1);\r\n        const value = specialValueHandler.valueProvider(mS, { element, binding, relativeSignalPath, root });\r\n        if (value instanceof Promise)\r\n          value.then(v => this.handleValueChanged(element, root, binding, v, valuesObject, i, signalVars, true, relativeSignalPath));\r\n        else\r\n          this.handleValueChanged(element, root, binding, value, valuesObject, i, signalVars, true, relativeSignalPath);\r\n        if (!specialValueHandler.valueChangedCallbacks)\r\n          specialValueHandler.valueChangedCallbacks = new Map();\r\n        let changeList = specialValueHandler.valueChangedCallbacks.get(mS);\r\n        if (changeList == null) {\r\n          changeList = [];\r\n          specialValueHandler.valueChangedCallbacks.set(mS, changeList);\r\n        }\r\n        changeList.push(() => {\r\n          const value = specialValueHandler.valueProvider(mS, { element, binding, relativeSignalPath, root })\r\n          if (value instanceof Promise)\r\n            value.then(v => this.handleValueChanged(element, root, binding, v, valuesObject, i, signalVars, true, relativeSignalPath));\r\n          else\r\n            this.handleValueChanged(element, root, binding, value, valuesObject, i, signalVars, true, relativeSignalPath);\r\n        });\r\n      } else {\r\n        if (s.includes('{')) {\r\n          let indirectSignal = new IndirectSignal(this, this._visualizationHandler, s, (value) => this.handleValueChanged(element, root, binding, value.val, valuesObject, i, signalVars, false, relativeSignalPath), element, relativeSignalPath, root, specialValueHandler);\r\n          if (!cleanupCalls)\r\n            cleanupCalls = [];\r\n          cleanupCalls.push(() => indirectSignal.dispose());\r\n          if (binding[1].twoWay && i == 0) {\r\n            this.addTwoWayBinding(binding, element, v => indirectSignal.setState(v));\r\n          }\r\n        } else {\r\n          if (binding[1].historic) {\r\n            if (binding[1].historic.reloadInterval) {\r\n              let myTimer = { timerId: <any>-1 };\r\n              const loadHistoric = async () => {\r\n                const res = await this._visualizationHandler.getHistoricData(s, binding[1].historic);\r\n                this.handleValueChanged(element, root, binding, res?.values, valuesObject, i, signalVars, true, relativeSignalPath);\r\n                if (myTimer.timerId !== null)\r\n                  myTimer.timerId = setTimeout(loadHistoric, binding[1].historic.reloadInterval);\r\n              }\r\n              loadHistoric();\r\n              if (!cleanupCalls)\r\n                cleanupCalls = [];\r\n              cleanupCalls.push(() => {\r\n                if (myTimer.timerId > 0)\r\n                  clearTimeout(myTimer.timerId);\r\n                myTimer.timerId = null;\r\n              });\r\n            } else\r\n              this._visualizationHandler.getHistoricData(s, binding[1].historic).then(x => this.handleValueChanged(element, root, binding, x?.values, valuesObject, i, signalVars, true, relativeSignalPath))\r\n          } else {\r\n            const cb = (id: string, value: State) => this.handleValueChanged(element, root, binding, value.val, valuesObject, i, signalVars, false, relativeSignalPath);\r\n            unsubscribeList.push([s, cb, this._visualizationHandler.subscribeState(s, cb)]);\r\n            this._visualizationHandler.getState(s).then(x => this.handleValueChanged(element, root, binding, x?.val, valuesObject, i, signalVars, false, relativeSignalPath));\r\n            if (binding[1].twoWay && i == 0) {\r\n              this.addTwoWayBinding(binding, element, v => this._visualizationHandler.setState(s, v));\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    return cleanUp;\r\n  }\r\n\r\n  private addTwoWayBinding(binding: namedBinding, element: Element, setter: (value) => void) {\r\n    if (binding[1].expressionTwoWay) {\r\n      if (!binding[1].compiledExpressionTwoWay) {\r\n        if (binding[1].expressionTwoWay.includes('return '))\r\n          binding[1].compiledExpressionTwoWay = new Function(<any>['value'], binding[1].expressionTwoWay);\r\n        else\r\n          binding[1].compiledExpressionTwoWay = new Function(<any>['value'], 'return ' + binding[1].expressionTwoWay);\r\n      }\r\n    }\r\n\r\n    for (let e of binding[1].events) {\r\n      const evt = element[e];\r\n      if (evt instanceof TypedEvent) {\r\n        evt.on(() => {\r\n          let v;\r\n          if (binding[1].target == BindingTarget.attribute)\r\n            v = element.getAttribute(binding[0]);\r\n          else\r\n            v = element[binding[0]];\r\n          v = BindingsHelper.parseValueWithType(v, binding);\r\n          if (binding[1].compiledExpressionTwoWay)\r\n            v = binding[1].compiledExpressionTwoWay(v);\r\n          setter(v);\r\n        })\r\n      } else {\r\n        element.addEventListener(e, (evt) => {\r\n          let v;\r\n          if (binding[1].target == BindingTarget.attribute)\r\n            v = element.getAttribute(binding[0]);\r\n          else\r\n            v = element[binding[0]];\r\n          v = BindingsHelper.parseValueWithType(v, binding);\r\n          if (binding[1].compiledExpressionTwoWay)\r\n            v = binding[1].compiledExpressionTwoWay(v);\r\n          setter(v);\r\n        });\r\n      }\r\n    }\r\n  }\r\n\r\n  private static parseValueWithType(value, binding: namedBinding) {\r\n    if (binding[1].type) {\r\n      switch (binding[1].type) {\r\n        case 'number':\r\n          return parseFloat(<any>value);\r\n        case 'boolean':\r\n          return value === true || value === 'true' || !!parseInt(<any>value);\r\n        case 'string':\r\n          return value?.toString();\r\n        case 'integer':\r\n          return parseInt(<any>value);\r\n        //case 'bitOfNumber':\r\n        //  return parseInt(<any>value);\r\n      }\r\n    }\r\n    return value;\r\n  }\r\n\r\n  handleValueChanged(element: Element, root: Element, binding: namedBinding, value: any, valuesObject: any[], index: number, signalVarNames: string[], noParse: boolean, relativeSignalPath: string) {\r\n    let v: (number | boolean | string) = value;\r\n    //should this be done??\r\n    if (!noParse && index == 0)\r\n      v = BindingsHelper.parseValueWithType(v, binding);\r\n    valuesObject[index] = v;\r\n    if (binding[1].expression) {\r\n      if (!binding[1].compiledExpression) {\r\n        signalVarNames.push('__res')\r\n        signalVarNames.push('__ctx')\r\n        if (binding[1].expression.includes('return '))\r\n          binding[1].compiledExpression = new Function(<any>signalVarNames, binding[1].expression);\r\n        else\r\n          binding[1].compiledExpression = new Function(<any>signalVarNames, 'return ' + binding[1].expression);\r\n      }\r\n      valuesObject[signalVarNames.length - 1] = { element, root, boundNames: binding[1].signal, boundTargetName: binding[0], boundTargetType: binding[1].target };\r\n      v = binding[1].compiledExpression(...valuesObject);\r\n      valuesObject[signalVarNames.length - 1] = v;\r\n    }\r\n    if (binding[1].converter) {\r\n      if (typeof binding[1].converter === 'string') {\r\n        v = this.namedConverterCallback(<string><never>binding[1].converter, v, element, binding);\r\n      } else {\r\n        const stringValue = <string>(v != null ? v.toString() : v);\r\n        if (stringValue in binding[1].converter) {\r\n          const cvVal = binding[1].converter[stringValue];\r\n          if (typeof cvVal === 'string')\r\n            v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[stringValue] + '`')(...valuesObject);\r\n          else\r\n            v = cvVal;\r\n        } else {\r\n          let endedWithBreak = false;\r\n          //@ts-ignore\r\n          const nr = parseFloat(v);\r\n          for (let c in binding[1].converter) {\r\n            if (c.length > 2 && c[0] === '>' && c[1] === '=') {\r\n              const wr = parseFloat(c.substring(2));\r\n              if (nr >= wr) {\r\n                const cvVal = binding[1].converter[c];\r\n                if (typeof cvVal === 'string')\r\n                  v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);\r\n                else\r\n                  v = cvVal;\r\n                endedWithBreak = true;\r\n                break;\r\n              }\r\n            } else if (c.length > 2 && c[0] === '<' && c[1] === '=') {\r\n              const wr = parseFloat(c.substring(2));\r\n              if (nr <= wr) {\r\n                const cvVal = binding[1].converter[c];\r\n                if (typeof cvVal === 'string')\r\n                  v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);\r\n                else\r\n                  v = cvVal;\r\n                endedWithBreak = true;\r\n                break;\r\n              }\r\n            } else if (c.length > 1 && c[0] === '>') {\r\n              const wr = parseFloat(c.substring(1));\r\n              if (nr > wr) {\r\n                const cvVal = binding[1].converter[c];\r\n                if (typeof cvVal === 'string')\r\n                  v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);\r\n                else\r\n                  v = cvVal;\r\n                endedWithBreak = true;\r\n                break;\r\n              }\r\n            } else if (c.length > 1 && c[0] === '<') {\r\n              const wr = parseFloat(c.substring(1));\r\n              if (nr < wr) {\r\n                const cvVal = binding[1].converter[c];\r\n                if (typeof cvVal === 'string')\r\n                  v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);\r\n                else\r\n                  v = cvVal;\r\n                endedWithBreak = true;\r\n                break;\r\n              }\r\n            } else {\r\n              const sp = c.split('-');\r\n              if (sp.length > 1) {\r\n                if ((sp[0] === '' || nr >= parseFloat(sp[0])) && (sp[1] === '' || parseFloat(sp[1]) >= nr)) {\r\n                  const cvVal = binding[1].converter[c];\r\n                  if (typeof cvVal === 'string')\r\n                    v = new Function(<any>signalVarNames, 'return `' + binding[1].converter[c] + '`')(...valuesObject);\r\n                  else\r\n                    v = cvVal;\r\n                  endedWithBreak = true;\r\n                  break;\r\n                }\r\n              }\r\n            }\r\n          }\r\n\r\n          if (!endedWithBreak && binding[1].converterDefault !== undefined)\r\n            v = binding[1].converterDefault;\r\n        }\r\n      }\r\n    }\r\n    if (binding[1].inverted)\r\n      v = !v;\r\n\r\n    if (binding[1].writeBackSignal) {\r\n      let wb = binding[1].writeBackSignal;\r\n      if (wb[0] === '.') {\r\n        wb = relativeSignalPath + wb;\r\n      }\r\n      this._visualizationHandler.setState(wb, v, true);\r\n    }\r\n\r\n    if (binding[1].target == BindingTarget.property)\r\n      element[binding[0]] = v;\r\n    else if (binding[1].target == BindingTarget.attribute)\r\n      element.setAttribute(binding[0], <string>v);\r\n    else if (binding[1].target == BindingTarget.css)\r\n      (<HTMLElement>element).style[binding[0]] = v;\r\n    else if (binding[1].target == BindingTarget.cssvar)\r\n      (<HTMLElement>element).style.setProperty(binding[0], <string>v);\r\n    else if (binding[1].target == BindingTarget.class) {\r\n      if (v)\r\n        (<HTMLElement>element).classList.add(binding[0]);\r\n      else\r\n        (<HTMLElement>element).classList.remove(binding[0]);\r\n    }\r\n    else if (binding[1].target == BindingTarget.visible)\r\n      (<HTMLElement>element).style.visibility = v ? '' : 'collapse';\r\n  }\r\n\r\n  public static camelToDotCase(text: string) {\r\n    return text.replace(/([A-Z])/g, (g) => `.${g[0].toLowerCase()}`);\r\n  }\r\n\r\n  public static dotToCamelCase(text: string) {\r\n    return text.replace(/\\.([a-z])/g, (i) => i[1].toUpperCase());\r\n  }\r\n}\r\n\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/helpers/info.txt",
    "content": ";,[ are not allowed in bindings, so they could be used for a short form...\n\nInfos about bindings:\n\n?  -> Signal in Property of Container\n?? -> Value of Property of Container\n?$ -> Value of Attribute of Container\n#  -> Signal in Property of Target\n## -> Value of Property of Target\n#$ -> Value of Attribute of Target\n$  -> Signal config\n§  -> access special context\n\n;  -> multiple signals\n\n{} -> indirect signal\n\n*/\n\n\n\n\n/*\n\nInfos about bindings:\n\n?  -> Access Container\n#  -> Access Target\n\n?\n\n$  -> Signal config\n§  -> access special context\n##  -> Signal in Property of Target\n??  -> Signal in Property of Container\n?$ -> Value of Attribute of Container\n#$ -> Value of Attribute of Target\n\n{} -> indirect signal"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/index.ts",
    "content": "export * from './blockly/BlocklyJavascriptHelper.js';\nexport * from './blockly/BlocklyScriptEditor.js';\nexport * from './blockly/BlocklyToolbox.js';\n\nexport * from './blockly/components/components.js';\n\nexport * from './components/BindingsEditor.js';\nexport * from './components/BindingsEditorHistoric.js';\nexport * from './components/SimpleScriptEditor.js';\nexport * from './components/VisualizationPropertyGrid.js';\nexport * from './components/ParameterEditor.js';\nexport * from './components/EventAssignment.js';\n\nexport * from './helpers/BindingsHelper.js';\n\nexport type * from './interfaces/IScriptMultiplexValue.js';\nexport type * from './interfaces/VisualisationElementScript.js';\nexport type * from './interfaces/VisualizationBinding.js';\nexport type * from './interfaces/VisualizationHandler.js';\nexport type * from './interfaces/VisualizationShell.js';\n\nexport * from './scripting/Script.js';\nexport * from './scripting/ScriptCommands.js';\nexport * from './scripting/ScriptSystem.js';\n\nexport * from './services/BindableObjectDragDropService.js';\nexport * from './services/VisualizationBindingsService.js';\nexport * from './services/VisualizationBindingsRefactorService.js';\nexport * from './services/PropertyGridDragDropService.js';\nexport * from './services/ScriptRefactorService.js';\nexport * from './services/SignalPropertyEditor.js';\nexport * from './services/VisualizationEventsService.js';\n\n//export * from './setupVisuService.js';"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/interfaces/IScriptMultiplexValue.ts",
    "content": "export interface IScriptMultiplexValue {\r\n     /**\r\n     * signal - read the value from a Signal\r\n     * property - read the value from a property of the customControl (not usable in screens)\r\n     * signalInProperty - read the value from a Signal (wich name is in the Property)\r\n     * event - read the value of a property of the event object\r\n     * parameter - a parameter you hand over \r\n     * context - a value of the context\r\n     * complexString - a string with signals (contained in {})\r\n     * complexSignal - read the value from a signal wich name is build here (it can contain other signals in {})\r\n     * expression - js expression, 'ctx' is context object\r\n     * elementProperty - a property defined on the element raising the event\r\n     */\r\n    source: 'signal' | 'property' | 'signalInProperty' | 'event' | 'parameter' | 'complexString' | 'complexSignal' | 'expression' | 'context' | 'elementProperty';\r\n    /**\r\n     * Name of the signal, the property of the component or the parameter of the script\r\n     * or for example in a event : srcElement.value to get the value of a input wich raises the event\r\n     * @TJS-format signal\r\n     */\r\n    name: string;\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/interfaces/VisualisationElementScript.ts",
    "content": "export interface VisualisationElementScript {\r\n    init?(instance: HTMLElement, shadowRoot: ShadowRoot);\r\n\r\n    connectedCallback?(instance: HTMLElement, shadowRoot: ShadowRoot);\r\n    disconnectedCallback?(instance: HTMLElement, shadowRoot: ShadowRoot);\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/interfaces/VisualizationBinding.ts",
    "content": "//Binding: haow to use...\r\n//binding:display=\"{'signal':'aa',...\"\r\n//binding:value=\"aa\" -> simplified form when only binding a direct property\r\n\r\nimport type { BindingTarget } from \"@node-projects/web-component-designer\";\r\n\r\nexport interface VisualizationBinding {\r\n    interval?: number; //if triggered by interval\r\n    signal: string;\r\n    inverted?: boolean;\r\n    twoWay?: boolean;\r\n    events?: string[];\r\n    target: BindingTarget;\r\n    converter?: Record<string, any>;\r\n    converterDefault?: any;\r\n    expression?: string; // could also be blockly ora complete javascript\r\n    expressionTwoWay?: string;\r\n    compiledExpression?: Function;\r\n    compiledExpressionTwoWay?: Function;\r\n    type?: string;\r\n    writeBackSignal?: string;\r\n    historic?: { reloadInterval?: number, [nm: string]: any };\r\n\r\n    maybeLitElement?: boolean,\r\n    litEventNames?: string[]\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/interfaces/VisualizationHandler.ts",
    "content": "\r\ntype StateValue = string | number | boolean | null;\r\n\r\nexport interface State {\r\n  val: StateValue;\r\n}\r\n\r\nexport interface Signal {\r\n  \"$type\": 'Signal'\r\n}\r\n\r\nexport interface SignalInformation {\r\n  type: 'string' | 'number' | 'boolean'\r\n  role: 'url' | 'datetime' | 'date' | 'time',\r\n  writeable: boolean,\r\n}\r\n\r\ntype StateChangeHandler = (id: string, state: State) => void\r\n\r\nexport interface VisualizationHandler {\r\n  getNormalizedSignalName(id: string, relativeSignalPath?: string, element?: Element): string;\r\n  \r\n  getState(id: string): Promise<State>;\r\n  setState(id: string, val: State | StateValue, ack?: boolean): Promise<void>;\r\n  subscribeState(id: string, cb: StateChangeHandler): any;\r\n  /**\r\n   * @param {any} subscriptionResult - the result of the subscribe call\r\n   */\r\n  unsubscribeState(id: string, cb: StateChangeHandler, subscriptionResult: any): void;\r\n  getHistoricData(id: string, config: any);\r\n  getObject(id: string): Promise<Signal>;\r\n\r\n  writeSignalsInGroup?(group: string): Promise<void>;\r\n  clearSignalsInGroup?(group: string): Promise<void>;\r\n\r\n  getAllNames(type: string);\r\n  getSignalInformation(signal: Signal): SignalInformation;\r\n};"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/interfaces/VisualizationShell.ts",
    "content": "import { IBindableObjectsBrowser } from \"@node-projects/web-component-designer\";\r\n\r\nexport interface VisualizationShell {\r\n  openConfirmation(element: HTMLElement, options:\r\n    {\r\n      x?: number,\r\n      y?: number,\r\n      width?: number,\r\n      height?: number,\r\n      parent?: HTMLElement,\r\n      abortSignal?: AbortSignal,\r\n      disableResize?: boolean,\r\n      confirmText?: string,\r\n      cancelText?: string\r\n    }): Promise<boolean>;\r\n\r\n  createBindableObjectBrowser: () => IBindableObjectsBrowser\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/scripting/Script.ts",
    "content": "import { ScriptCommands } from \"./ScriptCommands.js\";\n\nexport class Script {\n    name?: string;\n    relativeSignalsPath?: string;\n    commands: ScriptCommands[];\n    parameters?: Record<string, any>;\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/scripting/ScriptCommands.ts",
    "content": "export declare type ScriptCommands = RunnableScriptCommands |\r\n  Comment | Condition | Exit | Label | Goto;\r\n\r\nexport declare type RunnableScriptCommands = OpenScreen | OpenUrl | OpenDialog | CloseDialog |\r\n  ToggleSignalValue | ToggleSignalValueThroughList | SetSignalValue | IncrementSignalValue | DecrementSignalValue |\r\n  SetBitInSignal | ClearBitInSignal | ToggleBitInSignal | Console | CalculateSignalValue |\r\n  Javascript | SetElementProperty | Delay | SwitchLanguage |\r\n  Login | Logout |\r\n  SubscribeSignal | UnsubscribeSignal |\r\n  WriteSignalsInGroup | ClearSignalsInGroup |\r\n  RunScript |\r\n  CopySignalValuesFromFolder | ShowMessageBox | ShowPrompt |\r\n  ExportSignalValuesAsJson | ImportSignalValuesFromJson |\r\n  Repeat;\r\n\r\n/**\r\n * signal - a signal (the default)\r\n * property - a property defined in the current screen/control\r\n * elementProperty - a property defined on the element raising the event\r\n */\r\nexport declare type signalTarget = 'signal' | 'property' | 'elementProperty';\r\n\r\n/* \r\nTODO:\r\nIndirect Values in Scripts:\r\n \r\nIndirection Source:\r\nObject Values,\r\nCurrent Element Property\r\n*/\r\n\r\nexport interface Comment {\r\n  type: 'Comment';\r\n  comment: string;\r\n}\r\n\r\nexport interface OpenScreen {\r\n  type: 'OpenScreen';\r\n  /**\r\n   * Name of the Screen\r\n   * @TJS-format screen\r\n   */\r\n  screen: string;\r\n  title?: string;\r\n  /**\r\n   * If signals in screen are defined relative (starting with a '.'), this will be prepended\r\n   */\r\n  relativeSignalsPath?: string;\r\n  noHistory?: boolean;\r\n  closeable?: boolean;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface OpenDialog {\r\n  type: 'OpenDialog';\r\n  /**\r\n   * Name of the Screen\r\n   * @TJS-format screen\r\n   */\r\n  screen: string;\r\n  title?: string;\r\n  /**\r\n   * If signals in screen are defined relative (starting with a '.'), this will be prepended\r\n   */\r\n  relativeSignalsPath?: string;\r\n  moveable?: boolean;\r\n  closeable?: boolean;\r\n\r\n  width?: string;\r\n  height?: string;\r\n\r\n  left?: string;\r\n  top?: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ShowMessageBox {\r\n  type: 'ShowMessageBox';\r\n  /**\r\n   * title text\r\n   */\r\n  title: string;\r\n  /**\r\n   * message text\r\n   */\r\n  message: string;\r\n  /**\r\n   * message type\r\n   * @default info\r\n   */\r\n  messageType?: 'info' | 'warning' | 'error';\r\n  /**\r\n   * message text\r\n   * @default ok\r\n   */\r\n  buttons: 'ok' | 'okCancel' | 'yesNo' | 'retryCancel' | 'yesNoCancel' | 'abortRetryIgnore' | 'cancelTryContinue';\r\n  /**\r\n   * number of the clicked button, starting with 1\r\n   * @TJS-format signal\r\n   */\r\n  resultSignal: string;\r\n\r\n  width?: string;\r\n  height?: string;\r\n  additionalData?: string;\r\n\r\n  exitScriptOnCancel?: boolean; // if true the script execution will be stopped when the user clicks a button that is considered negative (cancel, no, retry, abort)\r\n}\r\n\r\nexport interface ShowPrompt {\r\n  type: 'ShowPrompt';\r\n  /**\r\n   * title text\r\n   */\r\n  title: string;\r\n  /**\r\n   * message text\r\n   */\r\n  message: string;\r\n  /**\r\n   * default value\r\n   */\r\n  default?: string;\r\n  /**\r\n   * number of the clicked button, starting with 1\r\n   * @TJS-format signal\r\n   */\r\n  resultSignal: string;\r\n  /**\r\n   * if true the script execution will be stopped when the user clicks a button that is considered negative (cancel, no, retry, abort)\r\n   */\r\n  exitScriptOnCancel?: boolean;\r\n\r\n  width?: string;\r\n  height?: string;\r\n  additionalData?: string;\r\n}\r\n\r\n//TODO: dialogId, closeChildDialogs\r\nexport interface CloseDialog {\r\n  type: 'CloseDialog';\r\n  /**\r\n   * A dialogId. If empty the parent dialog will be closed\r\n   * @TJS-format signal\r\n   */\r\n  //dialogId: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface OpenUrl {\r\n  type: 'OpenUrl';\r\n  url: string;\r\n  /**\r\n   * defaults to '_blank'\r\n   */\r\n  target: string;\r\n  openInDialog: boolean;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface SetSignalValue {\r\n  type: 'SetSignalValue';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  target: signalTarget;\r\n  value: any;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ToggleSignalValue {\r\n  type: 'ToggleSignalValue';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  target: signalTarget;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ToggleSignalValueThroughList {\r\n  type: 'ToggleSignalValueThroughList';\r\n  /**\r\n  * List of values wich can be toggled through\r\n  * @TJS-format collection\r\n  */\r\n  valueList: (string | number | boolean)[]\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  target: signalTarget;\r\n  additionalData?: string;\r\n}\r\n\r\n//export interface ToggleSignalValueFromJsonArray {\r\n//  type: 'ToggleSignalValueFromJsonArray';\r\n//  /**\r\n//   * Name of the signal\r\n//   * @TJS-format signal\r\n//   */\r\n//  signal: string;\r\n//  /**\r\n//   * list of values (as json array string e.g. ['aa', 1, false])\r\n//   */\r\n//  list: string;\r\n//}\r\n\r\nexport interface IncrementSignalValue {\r\n  type: 'IncrementSignalValue';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  value: number;\r\n  target: signalTarget;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface CalculateSignalValue {\r\n  type: 'CalculateSignalValue';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  targetSignal: string;\r\n  /**\r\n   * A formula to calculate the new signal value, can contain other signals in angle brackets: {}\r\n   * Example: {adapter.0.level} * 100 + 30\r\n   */\r\n  formula: string;\r\n  target: signalTarget;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface DecrementSignalValue {\r\n  type: 'DecrementSignalValue';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  value: number;\r\n  target: signalTarget;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface SetBitInSignal {\r\n  type: 'SetBitInSignal';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  /**\r\n   * @default 0\r\n   */\r\n  target: signalTarget;\r\n  bitNumber: number;\r\n  additionalData?: string;\r\n}\r\nexport interface ClearBitInSignal {\r\n  type: 'ClearBitInSignal';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  /**\r\n   * @default 0\r\n   */\r\n  target: signalTarget;\r\n  bitNumber: number;\r\n  additionalData?: string;\r\n}\r\nexport interface ToggleBitInSignal {\r\n  type: 'ToggleBitInSignal';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  /**\r\n   * @default 0\r\n   */\r\n  target: signalTarget;\r\n  bitNumber: number;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Javascript {\r\n  type: 'Javascript';\r\n  /**\r\n   * Usable objects in Script: \r\n   * context : {event : Event, element: Element, shadowRoot: ShadowRoot, instance: Element }\r\n   * @TJS-format script\r\n   */\r\n  script: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface SetElementProperty {\r\n  type: 'SetElementProperty';\r\n  /**\r\n   * what of the elements do you want to set\r\n   * @default property\r\n   */\r\n  target: 'property' | 'attribute' | 'css' | 'class';\r\n  /**\r\n   * where to search for the elements (used as a start)\r\n   * element = currentElement\r\n   * container = screen or customControl\r\n   * @default container\r\n   */\r\n  targetSelectorTarget: 'container' | 'element';\r\n  /**\r\n   * wich parent, 0 = current, 1 = first parent, 2 = ...\r\n   * of the targetSelectorTarget\r\n   * @default 0\r\n   */\r\n  parentIndex: number;\r\n  /**\r\n   * css selector to find elements on targetSelectorTarget, if empty the targetSelectorTarget directly is used\r\n   */\r\n  targetSelector: string;\r\n  /**\r\n   * name of property/attribute or css value you want to set\r\n   */\r\n  name: string;\r\n  /**\r\n   * only for target: class\r\n   * @default toggle\r\n   */\r\n  mode: 'add' | 'remove' | 'toggle';\r\n  /**\r\n   * value you want to set\r\n   */\r\n  value: any;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Delay {\r\n  type: 'Delay';\r\n  /**\r\n   * miliseconds to delay\r\n   * @default 500\r\n   */\r\n  value: number;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Console {\r\n  type: 'Console';\r\n  /**\r\n  * target where to log\r\n  * @default log\r\n  */\r\n  target: 'log' | 'info' | 'debug' | 'warn' | 'error';\r\n  /**\r\n   * console message\r\n   */\r\n  message: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface SwitchLanguage {\r\n  type: 'SwitchLanguage';\r\n  language: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Logout {\r\n  type: 'Logout';\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Login {\r\n  type: 'Login';\r\n  /**\r\n  * username\r\n  */\r\n  username: 'string';\r\n  /**\r\n   * password\r\n   */\r\n  password: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface SubscribeSignal {\r\n  type: 'SubscribeSignal';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  oneTime: boolean;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface UnsubscribeSignal {\r\n  type: 'UnsubscribeSignal';\r\n  /**\r\n   * Name of the signal\r\n   * @TJS-format signal\r\n   */\r\n  signal: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface WriteSignalsInGroup {\r\n  type: 'WriteSignalsInGroup';\r\n  /**\r\n  * Name of the Group\r\n  */\r\n  group: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ClearSignalsInGroup {\r\n  type: 'ClearSignalsInGroup';\r\n  /**\r\n  * Name of the Group\r\n  */\r\n  group: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface CopySignalValuesFromFolder {\r\n  type: 'CopySignalValuesFromFolder';\r\n  /**\r\n   * name of the source folder\r\n   */\r\n  sourceFolder: string;\r\n  /**\r\n   * name of destination folder\r\n   */\r\n  destinationFolder?: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ExportSignalValuesAsJson {\r\n  type: 'ExportSignalValuesAsJson';\r\n  /**\r\n   * regex to select the signals (every matching one)\r\n   */\r\n  regex: string;\r\n  /**\r\n   * wait for updated values from the connection\r\n   */\r\n  waitForUpdatedValues?: boolean;\r\n  /**\r\n   * download filename\r\n   */\r\n  fileName?: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface ImportSignalValuesFromJson {\r\n  type: 'ImportSignalValuesFromJson';\r\n  /**\r\n   * json data with the values\r\n   */\r\n  data: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Condition {\r\n  type: 'Condition';\r\n  /**\r\n  * Name of the value1\r\n  * @TJS-format complex\r\n  */\r\n  value1: any;\r\n  value2?: any;\r\n  comparisonType: '==null' | '!=null' | '==true' | '==false' | '==' | '!=' | '>' | '<' | '>=' | '<=' | '&&' | '||';\r\n  /**\r\n  * Name of the label to jumpe to when condition is true\r\n  */\r\n  trueGotoLabel?: string;\r\n  trueScriptName?: string;\r\n  trueScriptType?: string;\r\n  falseGotoLabel?: string;\r\n  falseScriptName?: string;\r\n  falseScriptType?: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Exit {\r\n  type: 'Exit';\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Label {\r\n  type: 'Label';\r\n  label: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Goto {\r\n  type: 'Goto';\r\n  label: string;\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface RunScript {\r\n  type: 'RunScript';\r\n  /**\r\n  * Name of the Script\r\n  */\r\n  name: 'string';\r\n  /**\r\n  * Type of the Script\r\n  */\r\n  scriptType: 'string';\r\n  additionalData?: string;\r\n}\r\n\r\nexport interface Repeat {\r\n  type: 'Repeat';\r\n  /**\r\n  * Name of the Label to jump to, if empty it jumps to the start.\r\n  */\r\n  label?: string;\r\n  /**\r\n  * Number of times the script repeats itself. If 0, it repeats indefinitly \r\n  * @default 0\r\n  */\r\n  count?: number;\r\n  /**\r\n  * always: script repeats until count is 0 or indefinitly when count is 0\r\n  * eventValid: script repeats until event is no longer active\r\n  * @default eventValid\r\n  */\r\n  mode: 'eventValid' | 'always';\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/scripting/ScriptSystem.ts",
    "content": "import { deepValue, setDeepValue, sleep } from \"@node-projects/web-component-designer/dist/elements/helper/Helper.js\";\r\nimport { generateEventCodeFromBlockly } from \"../blockly/BlocklyJavascriptHelper.js\";\r\nimport { parseBindingString } from \"../helpers/BindingsHelper.js\";\r\nimport { IScriptMultiplexValue } from \"../interfaces/IScriptMultiplexValue.js\";\r\nimport { VisualisationElementScript } from \"../interfaces/VisualisationElementScript.js\";\r\nimport { VisualizationHandler } from \"../interfaces/VisualizationHandler.js\";\r\nimport { Script } from \"./Script.js\";\r\nimport { ScriptCommands, signalTarget } from \"./ScriptCommands.js\";\r\nimport Long from 'long'\r\nimport { ScriptUpgrades } from \"./ScriptUpgrader.js\";\r\n\r\nexport const cyclicAttributeName = 'cyclic';\r\nexport type contextType = { event: Event, element: Element, root: HTMLElement, parameters?: Record<string, any>, relativeSignalsPath?: string };\r\n\r\ntype ContextCreator = (\r\n  root: HTMLElement,\r\n  event: Event | null,\r\n  element: Element,\r\n  parameters: Record<string, any>,\r\n  relativeSignalsPath: string\r\n) => any;\r\n\r\nconst eventOpposites: Record<string, string> = {\r\n  'pointerdown': 'pointerup',\r\n  'pointerenter': 'pointerleave',\r\n  'mouseenter': 'mouseleave',\r\n  'mouseover': 'mouseout',\r\n  'focus': 'blur',\r\n  'focusin': 'focusout',\r\n  'keydown': 'keyup',\r\n};\r\nexport class ScriptSystem {\r\n\r\n  _visualizationHandler: VisualizationHandler;\r\n  _subscriptionCallback = () => { };\r\n\r\n  constructor(visualizationHandler: VisualizationHandler) {\r\n    this._visualizationHandler = visualizationHandler;\r\n  }\r\n\r\n  async execute(scriptCommands: ScriptCommands[], outerContext: contextType) {\r\n    let repeatCount = -1;\r\n    let eventNotValid = false;\r\n\r\n    const triggerEvent = outerContext.event?.type;\r\n    const cancelEvent = triggerEvent ? eventOpposites[triggerEvent] : null;\r\n\r\n    if (cancelEvent) {\r\n      let element: Element | Window = outerContext.element;\r\n      if(triggerEvent == 'pointerdown'){\r\n        element = window;\r\n      }\r\n      element.addEventListener(cancelEvent, () => {\r\n        eventNotValid = true;\r\n      }, { once: true });\r\n    }\r\n    for (let i = 0; i < scriptCommands.length; i++) {\r\n      let c = scriptCommands[i];\r\n      if (c.type == \"Exit\") {\r\n        break;\r\n      } else if (c.type == \"Goto\") {\r\n        const label = await this.getValue(c.label, outerContext);\r\n        i = scriptCommands.findIndex(x => x.type == \"Label\" && x.label == label);\r\n        if (i < 0)\r\n          break;\r\n      } else if (c.type == \"Condition\") {\r\n        const value1 = await this.getValue(c.value1, outerContext);\r\n        const value2 = await this.getValue(c.value2, outerContext);\r\n        const comparisonType = await this.getValue(c.comparisonType, outerContext);\r\n        let res = false;\r\n        switch (comparisonType) {\r\n          case '==null': res = value1 == null; break;\r\n          case '!=null': res = value1 != null; break;\r\n          case '==true': res = value1 == true; break;\r\n          case '==false': res = value1 == false; break;\r\n          case '==': res = value1 == value2; break;\r\n          case '!=': res = value1 != value2; break;\r\n          case '>': res = value1 > value2; break;\r\n          case '<': res = value1 < value2; break;\r\n          case '>=': res = value1 >= value2; break;\r\n          case '<=': res = value1 <= value2; break;\r\n          case '&&': res = value1 && value2; break;\r\n          case '||': res = value1 || value2; break;\r\n        }\r\n        if (res) {\r\n          await this.runExternalScript(await this.getValue(c.trueScriptName, outerContext), await this.getValue(c.trueScriptType, outerContext));\r\n          const trueGotoLabel = await this.getValue(c.trueGotoLabel, outerContext);\r\n          if (trueGotoLabel) {\r\n            i = scriptCommands.findIndex(x => x.type == \"Label\" && x.label == trueGotoLabel);\r\n            if (i < 0)\r\n              break;\r\n          }\r\n        } else {\r\n          await this.runExternalScript(await this.getValue(c.falseScriptName, outerContext), await this.getValue(c.falseScriptType, outerContext));\r\n          const falseGotoLabel = await this.getValue(c.falseGotoLabel, outerContext);\r\n          if (falseGotoLabel) {\r\n            i = scriptCommands.findIndex(x => x.type == \"Label\" && x.label == falseGotoLabel);\r\n            if (i < 0)\r\n              break;\r\n          }\r\n        }\r\n      } else if (c.type == \"Repeat\") {\r\n        const label = await this.getValue(c.label, outerContext);\r\n        const count = await this.getValue(c.count, outerContext);\r\n        const mode = await this.getValue(c.mode, outerContext);\r\n\r\n        if (mode == 'eventValid' && eventNotValid) {\r\n          eventNotValid = false;\r\n          continue;\r\n        }\r\n\r\n        if (count != 0 && repeatCount == -1) {\r\n          repeatCount = count - 1;\r\n        }\r\n        if (repeatCount == 0) {\r\n          continue;\r\n        }\r\n        if (repeatCount > 0) {\r\n          repeatCount--;\r\n        }\r\n\r\n        i = -1;\r\n        if (label) {\r\n          i = scriptCommands.findIndex(x => x.type == \"Label\" && x.label == label);\r\n          if (i < 0) continue;\r\n        }\r\n      } else {\r\n        const continueScript = await this.runScriptCommand(c, outerContext);\r\n        if (!continueScript) {\r\n          break;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  async getValueFromTarget(target: signalTarget, name: string, context: contextType) {\r\n    if (target === 'property') {\r\n      return deepValue((<any>context).root, name);\r\n    } else if (target === 'elementProperty') {\r\n      return deepValue((<any>context).element, name);\r\n    } else {\r\n      return (await this._visualizationHandler.getState(this.getSignalName(name, context)))?.val;\r\n    }\r\n  }\r\n\r\n  async setValueOnTarget(target: signalTarget, name: string, context: contextType, value: any) {\r\n    if (target === 'property') {\r\n      setDeepValue((<any>context).root, name, value);\r\n    } else if (target === 'elementProperty') {\r\n      setDeepValue((<any>context).element, name, value);\r\n    } else {\r\n      await this._visualizationHandler.setState(this.getSignalName(name, context), value);\r\n    }\r\n  }\r\n\r\n  async runExternalScript(name: string, type: string) {\r\n    //TODO...\r\n  }\r\n\r\n  async runScriptCommand<T extends ScriptCommands>(command: T, context: contextType): Promise<boolean> {\r\n    switch (command.type) {\r\n\r\n      case 'Comment':\r\n      case 'Label':\r\n      case 'Condition':\r\n      case 'Goto':\r\n      case 'Exit':\r\n      case 'Repeat':\r\n        {\r\n          //Do nothing on this commands\r\n          break;\r\n        }\r\n\r\n      case 'OpenUrl': {\r\n        window.open(await this.getValue(command.url, context), command.target);\r\n        break;\r\n      }\r\n\r\n      case 'Delay': {\r\n        const value = await this.getValue(command.value ?? 500, context);\r\n        await sleep(value)\r\n        break;\r\n      }\r\n\r\n      case 'Console': {\r\n        const target = await this.getValue(command.target ?? 'log', context);\r\n        const message = await this.getValue(command.message, context);\r\n        console[target](message);\r\n        break;\r\n      }\r\n\r\n      case 'ToggleSignalValue': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = await this.getValueFromTarget(target, signal, context);\r\n        await this._visualizationHandler.setState(this.getSignalName(signal, context), !state);\r\n        break;\r\n      }\r\n      case 'ToggleSignalValueThroughList': {\r\n        const valueList = await this.getValue(command.valueList, context);\r\n        const signal = await this.getValue(command.signal, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = await this.getValueFromTarget(target, signal, context);\r\n        let nextIdx = valueList.indexOf(state) + 1;\r\n        if (nextIdx >= valueList.length)\r\n          nextIdx = 0;\r\n        const newState = valueList[nextIdx];\r\n        await this._visualizationHandler.setState(this.getSignalName(signal, context), newState);\r\n        break;\r\n      }\r\n      case 'SetSignalValue': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const target = await this.getValue(command.target, context);\r\n        await this.setValueOnTarget(target, signal, context, await this.getValue(command.value, context));\r\n        break;\r\n      }\r\n      case 'IncrementSignalValue': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = <number>await this.getValueFromTarget(target, signal, context);\r\n        const val = <number>state + await this.getValue(command.value, context);\r\n        await this.setValueOnTarget(target, signal, context, val);\r\n        break;\r\n      }\r\n      case 'DecrementSignalValue': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = <number>await this.getValueFromTarget(target, signal, context);\r\n        const val = <number>state - await this.getValue(command.value, context);\r\n        await this.setValueOnTarget(target, signal, context, val);\r\n        break;\r\n      }\r\n      case 'CalculateSignalValue': {\r\n        const formula = await this.getValue(command.formula, context);\r\n        const target = await this.getValue(command.target, context);\r\n        const targetSignal = await this.getValue(command.targetSignal, context);\r\n        let nm = await this.parseStringWithValues(formula, context);\r\n        let result = eval(nm);\r\n        await this.setValueOnTarget(target, targetSignal, context, result);\r\n        break;\r\n      }\r\n\r\n      case 'SetBitInSignal': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const bitNumber = await this.getValue(command.bitNumber ?? 0, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = <number>await this.getValueFromTarget(target, signal, context);\r\n        let mask = Long.fromNumber(1).shiftLeft(bitNumber);\r\n        const newVal = Long.fromNumber(<number>state).or(mask).toNumber();\r\n        await this.setValueOnTarget(target, signal, context, newVal);\r\n        break;\r\n      }\r\n      case 'ClearBitInSignal': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const bitNumber = await this.getValue(command.bitNumber ?? 0, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = <number>await this.getValueFromTarget(target, signal, context);\r\n        let mask = Long.fromNumber(1).shiftLeft(bitNumber);\r\n        mask.negate();\r\n        const newVal = Long.fromNumber(<number>state).and(mask).toNumber();\r\n        await this.setValueOnTarget(target, signal, context, newVal);\r\n        break;\r\n      }\r\n      case 'ToggleBitInSignal': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const bitNumber = await this.getValue(command.bitNumber ?? 0, context);\r\n        const target = await this.getValue(command.target, context);\r\n        let state = <number>await this.getValueFromTarget(target, signal, context);\r\n        let mask = Long.fromNumber(1).shiftLeft(bitNumber);\r\n        const newVal = Long.fromNumber(<number>state).xor(mask).toNumber();\r\n        await this.setValueOnTarget(target, signal, context, newVal);\r\n        break;\r\n      }\r\n\r\n      case 'Javascript': {\r\n        const script = await this.getValue(command.script, context);\r\n        let mycontext: { event: Event, element: Element } = context;\r\n        (<any>mycontext).shadowRoot = (<ShadowRoot>context.element.getRootNode());\r\n        (<any>mycontext).instance = (<any>context).shadowRoot.host;\r\n        if (!(<any>command).compiledScript)\r\n          (<any>command).compiledScript = new Function('context', script);\r\n        (<any>command).compiledScript(mycontext);\r\n        break;\r\n      }\r\n\r\n      case 'SetElementProperty': {\r\n        //@ts-ignore\r\n        command = ScriptUpgrades.upgradeSetElementProperty(command);\r\n        let name = await this.getValue(command.name, context);\r\n        if (name === '')\r\n          name = null;\r\n        const value = await this.getValue(command.value, context);\r\n        const parentIndex = await this.getValue(command.parentIndex ?? 0, context);\r\n        const target = await this.getValue(command.target ?? 'property', context);\r\n        const targetSelectorTarget = await this.getValue(command.targetSelectorTarget ?? 'container', context);\r\n        const targetSelector = await this.getValue(command.targetSelector, context);\r\n        const mode = await this.getValue(command.mode ?? 'toggle', context);\r\n        let elements = this.getTargetFromTargetSelector(context, <any>targetSelectorTarget, parentIndex, targetSelector);\r\n        for (let e of elements) {\r\n          if (target == 'attribute') {\r\n            e.setAttribute(name, value);\r\n          } else if (target == 'property') {\r\n            e[name] = value;\r\n          } else if (target == 'css') {\r\n            (<HTMLElement>e).style[name] = value;\r\n          } else if (target == 'class') {\r\n            if (mode === 'toggle') {\r\n              (<HTMLElement>e).classList.toggle(name ?? value);\r\n            } else if (mode === 'remove') {\r\n              (<HTMLElement>e).classList.remove(name ?? value);\r\n            } else {\r\n              (<HTMLElement>e).classList.add(name ?? value);\r\n            }\r\n          }\r\n        }\r\n        break;\r\n      }\r\n\r\n      case 'SubscribeSignal': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        const oneTime = await this.getValue(command.oneTime, context);\r\n        if (oneTime) {\r\n          let cb = () => {\r\n            this._visualizationHandler.unsubscribeState(signal, cb, null);\r\n          }\r\n          this._visualizationHandler.subscribeState(signal, cb);\r\n        }\r\n        else\r\n          this._visualizationHandler.subscribeState(signal, this._subscriptionCallback);\r\n        break;\r\n      }\r\n\r\n      case 'UnsubscribeSignal': {\r\n        const signal = await this.getValue(command.signal, context);\r\n        this._visualizationHandler.unsubscribeState(signal, this._subscriptionCallback, null);\r\n        break;\r\n      }\r\n\r\n      case 'WriteSignalsInGroup': {\r\n        const group = await this.getValue(command.group, context);\r\n        this._visualizationHandler.writeSignalsInGroup(group);\r\n        break;\r\n      }\r\n\r\n      case 'ClearSignalsInGroup': {\r\n        const group = await this.getValue(command.group, context);\r\n        this._visualizationHandler.clearSignalsInGroup(group);\r\n        break;\r\n      }\r\n\r\n      case 'RunScript': {\r\n        const name = await this.getValue(command.name, context);\r\n        const scriptType = await this.getValue(command.scriptType, context);\r\n        this.runExternalScript(name, scriptType);\r\n        break;\r\n      }\r\n\r\n      case 'ShowMessageBox': {\r\n        let res = null;\r\n        const exitScriptOnCancel = await this.getValue(command.exitScriptOnCancel, context);\r\n        const buttons = await this.getValue(command.buttons, context);\r\n        if (buttons == 'yesNo') {\r\n          const message = await this.getValue(command.message, context);\r\n          if (confirm(message))\r\n            res = 1;\r\n          else res = 2;\r\n        } else {\r\n          const message = await this.getValue(command.message, context);\r\n          alert(message);\r\n          res = 1;\r\n        }\r\n        const resultSignal = await this.getValue(command.resultSignal, context);\r\n        if (resultSignal) {\r\n          this._visualizationHandler.setState(resultSignal, res);\r\n        }\r\n\r\n        if (exitScriptOnCancel && res != 1) {\r\n          return false;\r\n        }\r\n\r\n        break;\r\n      }\r\n\r\n      case 'ShowPrompt': {\r\n        const message = await this.getValue(command.message, context);\r\n        const defaultValue = await this.getValue(command.default, context);\r\n        const result = prompt(message, defaultValue);\r\n        const resultSignal = await this.getValue(command.resultSignal, context);\r\n        if (resultSignal && result) {\r\n          this._visualizationHandler.setState(resultSignal, result);\r\n        }\r\n        break;\r\n      }\r\n\r\n      case 'Login':\r\n      case 'Logout':\r\n      case 'CloseDialog':\r\n      case 'OpenDialog':\r\n      case 'OpenScreen':\r\n      case 'SwitchLanguage':\r\n      case 'CopySignalValuesFromFolder':\r\n      case 'ExportSignalValuesAsJson':\r\n      case 'ImportSignalValuesFromJson': {\r\n        alert('command: \"' + command.type + '\" is not yet implemented');\r\n        break;\r\n      }\r\n    }\r\n    return true;\r\n  }\r\n\r\n  getTarget(context: contextType, targetSelectorTarget: 'element' | 'container', parentLevel: number) {\r\n    if (targetSelectorTarget === 'container') {\r\n      let el = (<ShadowRoot>context.element.getRootNode()).host\r\n      for (let i = 0; i < (parentLevel ?? 0); i++)\r\n        el = (<ShadowRoot>el.getRootNode()).host;\r\n      return el;\r\n    } else if (targetSelectorTarget === \"element\") {\r\n      let el = context.element;\r\n      for (let i = 0; i < (parentLevel ?? 0); i++)\r\n        el = el.parentElement;\r\n      return el;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  getTargetFromTargetSelector(context: contextType, targetSelectorTarget: 'element' | 'container', parentLevel: number, targetSelector: string): Iterable<Element> {\r\n    const target = this.getTarget(context, targetSelectorTarget, parentLevel);\r\n    let elements: Iterable<Element> = [target];\r\n    if (targetSelector) {\r\n      if (targetSelectorTarget === 'container')\r\n        elements = target.shadowRoot.querySelectorAll(targetSelector);\r\n      else\r\n        elements = target.querySelectorAll(targetSelector);\r\n    }\r\n    return elements;\r\n  }\r\n\r\n  async getValue<T>(value: T, outerContext: contextType): Promise<T> {\r\n    if (value == null)\r\n      return null;\r\n    if (typeof value === 'object') {\r\n      switch ((<IScriptMultiplexValue><any>value).source) {\r\n        case 'property': {\r\n          return deepValue(outerContext.root, (<IScriptMultiplexValue><any>value).name);\r\n        }\r\n        case 'elementProperty': {\r\n          return deepValue(outerContext.element, (<IScriptMultiplexValue><any>value).name);\r\n        }\r\n        case 'signal': {\r\n          let sng = await this._visualizationHandler.getState(this.getSignalName((<IScriptMultiplexValue><any>value).name, outerContext));\r\n          return <T>sng?.val;\r\n        }\r\n        case 'signalInProperty': {\r\n          const sngName = deepValue(outerContext.root, (<IScriptMultiplexValue><any>value).name)\r\n          let sng = await this._visualizationHandler.getState(this.getSignalName(sngName, outerContext));\r\n          return <T>sng?.val;\r\n        }\r\n        case 'event': {\r\n          let obj = outerContext.event;\r\n          if ((<IScriptMultiplexValue><any>value).name)\r\n            obj = deepValue(obj, (<IScriptMultiplexValue><any>value).name);\r\n          return <T>obj;\r\n        }\r\n        case 'parameter': {\r\n          return outerContext.parameters[(<IScriptMultiplexValue><any>value).name];\r\n        }\r\n        case 'context': {\r\n          return deepValue(outerContext, (<IScriptMultiplexValue><any>value).name);\r\n        }\r\n        case 'complexString': {\r\n          let text = (<IScriptMultiplexValue><any>value).name;\r\n          if (text != null) {\r\n            return <T>await this.parseStringWithValues(text, outerContext);\r\n          }\r\n          return null;\r\n        }\r\n        case 'complexSignal': {\r\n          let text = (<IScriptMultiplexValue><any>value).name;\r\n          if (text != null) {\r\n            const signal = await this.parseStringWithValues(text, outerContext);\r\n            const state = await this._visualizationHandler.getState(this.getSignalName(signal, outerContext));\r\n            return <T>state?.val;\r\n          }\r\n          return null;\r\n        }\r\n        case 'expression': {\r\n          //@ts-ignore\r\n          var ctx = outerContext;\r\n          return eval((<any>value).name);\r\n        }\r\n      }\r\n    }\r\n    return value;\r\n  }\r\n\r\n  async getStateOrFieldOrParameter(name: string, context: contextType) {\r\n    //use same shortcuts as in bindings. $ = signals object, § = access context\r\n    if (name[0] === '§') {\r\n      //@ts-ignore\r\n      var ctx = context;\r\n      return eval('ctx.' + name.substring(1));\r\n    } else if (name[0] === '?' && name[1] === '?') {\r\n      return context.root[name.substring(2)];\r\n    } else if (name[0] === '?') {\r\n      return await this._visualizationHandler.getState(this.getSignalName(context.root[name.substring(1)], context));\r\n    }\r\n    return await this._visualizationHandler.getState(this.getSignalName(name, context));\r\n  }\r\n\r\n  async parseStringWithValues(text: string, context: contextType) {\r\n    const parsed = parseBindingString(text);\r\n    let results = await Promise.all(parsed.signals.map(x => this.getStateOrFieldOrParameter(this.getSignalName(x, context), context)));\r\n    let nm = parsed.parts[0];\r\n    for (let i = 0; i < parsed.parts.length - 1; i++) {\r\n      let v = results[i];\r\n      if (typeof v === 'object')\r\n        v = v.val;\r\n      if (v == null)\r\n        v = '';\r\n      nm += v + parsed.parts[i + 1];\r\n    }\r\n    return nm;\r\n  }\r\n\r\n  getSignalName(name: string, outerContext: contextType) {\r\n    if (name[0] === '.')\r\n      return outerContext.relativeSignalsPath + name;\r\n    return name;\r\n  }\r\n\r\n  public createScriptContext(root: HTMLElement, event: Event, element: Element, parameters: Record<string, any>, relativeSignalsPath: string): any {\r\n    return { root, event, element, parameters, relativeSignalsPath };\r\n  }\r\n\r\n  async assignAllScripts(\r\n    source: string,\r\n    javascriptCode: string,\r\n    shadowRoot: ShadowRoot,\r\n    instance: HTMLElement,\r\n    visualizationHandler: VisualizationHandler,\r\n    contextCreator?: (root: HTMLElement, event: Event, element: Element, parameters: Record<string, any>, relativeSignalsPath: string) => any,\r\n    assignExternalScript?: (element: Element, event: string, scriptData: any) => void,\r\n    runInitalization?: (jsObject: VisualisationElementScript) => void\r\n  ): Promise<VisualisationElementScript> {\r\n    contextCreator ??= this.createScriptContext;\r\n\r\n    const jsObject = await this.loadAndInitJsObject(source, javascriptCode, shadowRoot, instance, runInitalization);\r\n\r\n    for (const element of shadowRoot.querySelectorAll('*')) {\r\n      for (const attr of element.attributes) {\r\n        if (attr.name[0] !== '@') continue;\r\n\r\n        try {\r\n          const evtName = attr.name.substring(1);\r\n          const script = attr.value.trim();\r\n          const isCyclic = evtName.startsWith(cyclicAttributeName);\r\n          const interval = isCyclic ? parseInt(evtName.substring(evtName.indexOf(':') + 1)) : null;\r\n\r\n          const handler = await this.resolveHandler(\r\n            script, jsObject, element, shadowRoot, instance,\r\n            visualizationHandler, contextCreator, assignExternalScript, evtName\r\n          );\r\n\r\n          if (handler) {\r\n            this.bindHandler(element, evtName, isCyclic, interval, handler);\r\n          }\r\n        } catch (err) {\r\n          console.warn('error assigning script', element, attr);\r\n        }\r\n      }\r\n    }\r\n\r\n    return jsObject;\r\n  }\r\n\r\n  // --- Hilfsmethoden ---\r\n\r\n  private async loadAndInitJsObject(\r\n    source: string,\r\n    javascriptCode: string,\r\n    shadowRoot: ShadowRoot,\r\n    instance: HTMLElement,\r\n    runInitalization?: (jsObject: VisualisationElementScript) => void\r\n  ): Promise<VisualisationElementScript | null> {\r\n    if (!javascriptCode) return null;\r\n\r\n    try {\r\n      const scriptUrl = URL.createObjectURL(new Blob([javascriptCode], { type: 'application/javascript' }));\r\n      const jsObject = await import(scriptUrl);\r\n\r\n      if (runInitalization) {\r\n        runInitalization(jsObject);\r\n      } else {\r\n        jsObject.init?.(instance, shadowRoot);\r\n      }\r\n\r\n      return jsObject;\r\n    } catch (err) {\r\n      console.error('error parsing javascript - ' + source, err);\r\n      return null;\r\n    }\r\n  }\r\n\r\n  // Gibt eine normalisierte Handler-Funktion (evt) => void zurück\r\n  private async resolveHandler(\r\n    script: string,\r\n    jsObject: VisualisationElementScript,\r\n    element: Element,\r\n    shadowRoot: ShadowRoot,\r\n    instance: HTMLElement,\r\n    visualizationHandler: VisualizationHandler,\r\n    contextCreator: ContextCreator,\r\n    assignExternalScript?: (element: Element, event: string, scriptData: any) => void,\r\n    evtName?: string\r\n  ): Promise<((evt: Event | null) => void) | null> {\r\n\r\n    // Plain-String: direkte Referenz auf eine JS-Funktion\r\n    if (script[0] !== '{') {\r\n      return (evt) => {\r\n        if (!jsObject?.[script]) {\r\n          console.warn(`javascript function named: \"${script}\" not found, maybe missing a \"export\" ?`);\r\n        } else {\r\n          jsObject[script](evt, element, shadowRoot, instance);\r\n        }\r\n      };\r\n    }\r\n\r\n    const scriptObj: Script = JSON.parse(script);\r\n\r\n    // commands-Script\r\n    if ('commands' in scriptObj) {\r\n      return (evt) => this.execute(\r\n        scriptObj.commands,\r\n        contextCreator(instance, evt, element, scriptObj.parameters, scriptObj.relativeSignalsPath)\r\n      );\r\n    }\r\n\r\n    // Blockly-Script\r\n    if ('blocks' in scriptObj) {\r\n      let compiledFunc: Awaited<ReturnType<typeof generateEventCodeFromBlockly>> | null = null;\r\n      return async (evt) => {\r\n        compiledFunc ??= await generateEventCodeFromBlockly(scriptObj);\r\n        compiledFunc(\r\n          evt, shadowRoot,\r\n          (scriptObj as any).parameters,\r\n          (scriptObj as any).relativeSignalsPath ?? '',\r\n          visualizationHandler,\r\n          contextCreator(instance, evt, element, (scriptObj as any).parameters, (scriptObj as any).relativeSignalsPath)\r\n        );\r\n      };\r\n    }\r\n\r\n    // Externes Script oder benannte JS-Funktion\r\n    if ('name' in scriptObj) {\r\n      if (assignExternalScript) {\r\n        assignExternalScript(element, evtName, scriptObj);\r\n        return null; // wird extern verwaltet\r\n      }\r\n\r\n      const nm = (scriptObj as any).name;\r\n      return (evt) => {\r\n        if (!jsObject?.[nm]) {\r\n          console.warn(`javascript function named: \"${nm}\" not found, maybe missing a \"export\" ?`);\r\n        } else {\r\n          jsObject[nm](evt, element, shadowRoot, instance, (scriptObj as any).parameters);\r\n        }\r\n      };\r\n    }\r\n\r\n    return null;\r\n  }\r\n\r\n  private bindHandler(\r\n    element: Element,\r\n    evtName: string,\r\n    isCyclic: boolean,\r\n    interval: number | null,\r\n    handler: (evt: Event | null) => void\r\n  ): void {\r\n    if (!isCyclic) {\r\n      element.addEventListener(evtName, handler);\r\n      return;\r\n    }\r\n\r\n    const intervalId = setInterval(() => {\r\n      if (!element.isConnected) {\r\n        clearInterval(intervalId);\r\n      } else {\r\n        handler(null);\r\n      }\r\n    }, interval);\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/scripting/ScriptUpgrader.ts",
    "content": "import { ScriptCommands, SetElementProperty } from \"./ScriptCommands\";\n\n//For upgradeing changed script commands\nexport class ScriptUpgrades {\n    static upgradeScriptCommand(scriptCommand: ScriptCommands) {\n        if (scriptCommand.type === 'SetElementProperty') {\n            return ScriptUpgrades.upgradeSetElementProperty(scriptCommand);\n        }\n        return scriptCommand;\n    }\n\n    static upgradeSetElementProperty(scriptCommand: SetElementProperty): SetElementProperty {\n        if (<string>scriptCommand.targetSelectorTarget === 'currentScreen') {\n            scriptCommand.targetSelectorTarget = 'container';\n        } else if (<string>scriptCommand.targetSelectorTarget === 'parentScreen') {\n            scriptCommand.targetSelectorTarget = 'container';\n            scriptCommand.parentIndex = 1;\n        } else if (<string>scriptCommand.targetSelectorTarget === 'currentElement') {\n            scriptCommand.targetSelectorTarget = 'element';\n        } else if (<string>scriptCommand.targetSelectorTarget === 'parentElement') {\n            scriptCommand.targetSelectorTarget = 'element';\n            scriptCommand.parentIndex = 1;\n        }\n        return scriptCommand;\n    }\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/BindableObjectDragDropService.ts",
    "content": "import { OverlayLayer, DesignItem, IBindableObject, IBindableObjectDragDropService, IDesignerCanvas, InsertAction, ChangeGroup, BindingTarget, IProperty, IDesignItem, PropertyType } from \"@node-projects/web-component-designer\";\r\nimport { BindingsHelper } from \"../helpers/BindingsHelper.js\";\r\nimport { VisualizationHandler } from \"../interfaces/VisualizationHandler.js\";\r\nimport { VisualizationBinding } from \"../interfaces/VisualizationBinding.js\";\r\n\r\nexport class BindableObjectDragDropService implements IBindableObjectDragDropService {\r\n  constructor(bindingsHelper: BindingsHelper, visualizationHandler: VisualizationHandler) {\r\n    this._bindingsHelper = bindingsHelper;\r\n    this._visualizationHandler = visualizationHandler;\r\n  }\r\n\r\n  protected _bindingsHelper: BindingsHelper;\r\n  protected _visualizationHandler: VisualizationHandler\r\n\r\n  rectMap = new Map<Element, SVGRectElement>();\r\n  rect: SVGRectElement;\r\n\r\n  dragEnter(designerCanvas: IDesignerCanvas, event: DragEvent, element: Element) {\r\n    const designItem = DesignItem.GetDesignItem(element);\r\n    if (designItem && !designItem.isRootItem) {\r\n      let itemRect = designerCanvas.getNormalizedElementCoordinates(element);\r\n      this.rect = designerCanvas.overlayLayer.drawRect('IobrokerWebuiBindableObjectDragDropService', itemRect.x, itemRect.y, itemRect.width, itemRect.height, '', null, OverlayLayer.Background);\r\n      this.rect.style.fill = '#ff0000';\r\n      this.rect.style.opacity = '0.3';\r\n      this.rectMap.set(element, this.rect);\r\n    }\r\n  }\r\n\r\n  dragLeave(designerCanvas: IDesignerCanvas, event: DragEvent, element: Element) {\r\n    const designItem = DesignItem.GetDesignItem(element);\r\n    if (designItem && !designItem.isRootItem) {\r\n      const rect = this.rectMap.get(element);\r\n      designerCanvas.overlayLayer.removeOverlay(rect);\r\n      this.rectMap.delete(element);\r\n    }\r\n  }\r\n\r\n  dragOver(designerView: IDesignerCanvas, event: DragEvent, element: Element): \"none\" | \"copy\" | \"link\" | \"move\" {\r\n    return 'copy';\r\n  }\r\n\r\n  async drop(designerCanvas: IDesignerCanvas, event: DragEvent, bindableObject: IBindableObject<any>, element: Element) {\r\n    for (let r of this.rectMap.values()) {\r\n      designerCanvas.overlayLayer.removeOverlay(r);\r\n    }\r\n    this.rectMap.clear();\r\n\r\n    const designItem = DesignItem.GetDesignItem(element);\r\n    const obj = await this._visualizationHandler.getObject(bindableObject.fullName);\r\n    const info = this._visualizationHandler.getSignalInformation(obj);\r\n\r\n    if (designItem && !designItem.isRootItem) {\r\n      // Add binding to drop target...\r\n      if (element instanceof HTMLInputElement) {\r\n        const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property };\r\n        const serializedBinding = this._bindingsHelper.serializeBinding(element, element.type == 'checkbox' ? 'checked' : 'value', binding);\r\n        designItem.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n      } else {\r\n        const binding = { signal: bindableObject.fullName, target: BindingTarget.content };\r\n        const serializedBinding = this._bindingsHelper.serializeBinding(element, null, binding);\r\n        designItem.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n      }\r\n    } else {\r\n      const position = designerCanvas.getNormalizedEventCoordinates(event);\r\n\r\n      let di: DesignItem;\r\n      let grp: ChangeGroup;\r\n\r\n      let state = await this._visualizationHandler.getState(bindableObject.fullName);\r\n      //TODO: only icon&image if val is a url with extension \r\n      if (info.role === 'url' && typeof state?.val === 'string') {\r\n        if (state.val.endsWith('jpg') || state.val.endsWith('jpeg') || state.val.endsWith('png') || state.val.endsWith('gif') || state.val.endsWith('svg')) {\r\n          const img = document.createElement('img');\r\n          di = DesignItem.createDesignItemFromInstance(img, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n          grp = di.openGroup(\"Insert\");\r\n          const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property };\r\n          let serializedBinding = this._bindingsHelper.serializeBinding(img, 'src', binding);\r\n          di.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n          (<HTMLImageElement>di.element).src = state.val;\r\n        } else if (state.val.endsWith('mp4')) {\r\n          const video = document.createElement('video');\r\n          di = DesignItem.createDesignItemFromInstance(video, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n          grp = di.openGroup(\"Insert\");\r\n          const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property };\r\n          let serializedBinding = this._bindingsHelper.serializeBinding(video, 'src', binding);\r\n          di.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n          (<HTMLImageElement>di.element).src = state.val;\r\n        } else {\r\n          const video = document.createElement('iframe');\r\n          di = DesignItem.createDesignItemFromInstance(video, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n          grp = di.openGroup(\"Insert\");\r\n          const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property };\r\n          let serializedBinding = this._bindingsHelper.serializeBinding(video, 'src', binding);\r\n          di.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n          (<HTMLImageElement>di.element).src = state.val;\r\n        }\r\n      }\r\n\r\n      if (!di) {\r\n        const input = document.createElement('input');\r\n        di = DesignItem.createDesignItemFromInstance(input, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\r\n        grp = di.openGroup(\"Insert\");\r\n        let twoWay = info.writeable !== false;\r\n        const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property, twoWay: twoWay };\r\n        let serializedBinding = this._bindingsHelper.serializeBinding(input, 'value', binding);\r\n\r\n        if (info.type === 'boolean') {\r\n          serializedBinding = this._bindingsHelper.serializeBinding(input, 'checked', binding);\r\n          di.setAttribute(\"type\", \"checkbox\");\r\n        } else if (info.role == 'date') {\r\n          binding.twoWay = twoWay;\r\n          serializedBinding = this._bindingsHelper.serializeBinding(input, 'value-as-number', binding);\r\n          di.setAttribute(\"type\", \"date\");\r\n          di.setAttribute(\"readonly\", \"\");\r\n        } else if (info.role == 'datetime') {\r\n          binding.twoWay = twoWay;\r\n          serializedBinding = this._bindingsHelper.serializeBinding(input, 'value-as-number', binding);\r\n          di.setAttribute(\"type\", \"datetime-local\");\r\n          di.setAttribute(\"readonly\", \"\");\r\n        }\r\n        di.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n      }\r\n      di.setStyle('position', 'absolute');\r\n      di.setStyle('left', position.x + 'px');\r\n      di.setStyle('top', position.y + 'px');\r\n      designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\r\n      grp.commit();\r\n      requestAnimationFrame(() => designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([di]));\r\n    }\r\n  }\r\n\r\n  dragOverOnProperty?(event: DragEvent, property: IProperty, designItems: IDesignItem[]): 'none' | 'copy' | 'link' | 'move' {\r\n    return 'copy';\r\n  }\r\n\r\n  dropOnProperty?(event: DragEvent, property: IProperty, bindableObject: IBindableObject<any>, designItems: IDesignItem[]) {\r\n    if (property.type == 'signal') {\r\n      property.service.setValue(designItems, property, bindableObject.fullName);\r\n      return;\r\n    }\r\n    const binding: VisualizationBinding = { signal: bindableObject.fullName, target: BindingTarget.property };\r\n    if (property.propertyType == PropertyType.attribute)\r\n      binding.target = BindingTarget.attribute;\r\n    if (property.propertyType == PropertyType.cssValue)\r\n      binding.target = BindingTarget.css;\r\n    binding.signal = bindableObject.fullName;\r\n    binding.twoWay = property.propertyType == PropertyType.property || property.propertyType == PropertyType.propertyAndAttribute;\r\n    //if (designItems[0].element instanceof BaseCustomControl)\r\n    //    binding.twoWay = false;\r\n    const group = designItems[0].openGroup('drop binding')\r\n    for (let d of designItems) {\r\n      const serializedBinding = this._bindingsHelper.serializeBinding(d.element, property.name, binding);\r\n      d.setAttribute(serializedBinding[0], serializedBinding[1]);\r\n    }\r\n    group.commit();\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/PropertyGridDragDropService.ts",
    "content": "import { IProperty, IDesignItem, IPropertyGridDragDropService } from \"@node-projects/web-component-designer\";\r\n\r\nexport class PropertyGridDragDropService implements IPropertyGridDragDropService {\r\n\r\n    dragOverOnProperty?(event: DragEvent, property: IProperty, designItems: IDesignItem[]): 'none' | 'copy' | 'link' | 'move' {\r\n        return 'copy';\r\n    }\r\n\r\n    dropOnProperty?(event: DragEvent, property: IProperty, dropObject: any, designItems: IDesignItem[]) {\r\n        property.service.setValue(designItems, property, dropObject.text);\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/ScriptRefactorService.ts",
    "content": "import { BindingTarget, IDesignItem, IRefactorService, IRefactoring } from \"@node-projects/web-component-designer\";\r\nimport { Script } from \"../scripting/Script.js\";\r\nimport { IScriptMultiplexValue } from \"../interfaces/IScriptMultiplexValue.js\";\r\n\r\nexport class ScriptRefactorService implements IRefactorService {\r\n    getRefactorings(designItems: IDesignItem[]): (IRefactoring & { refactor: (newValue) => void })[] {\r\n        const refactorings: (IRefactoring & { refactor: (newValue) => void })[] = [];\r\n        for (let d of designItems) {\r\n            for (let a of d.attributes()) {\r\n                if (a[0][0] == '@') {\r\n                    const sc = a[1];\r\n                    if (sc[0] == '{') {\r\n                        const script = JSON.parse(sc) as Script;\r\n                        if ('commands' in script) {\r\n                            for (let c of script.commands) {\r\n                                for (let p in c) {\r\n                                    const cp = c[p];\r\n                                    if (cp != null && typeof cp === 'object') {\r\n                                        let mp = cp as IScriptMultiplexValue;\r\n                                        if (mp.source === 'signal') {\r\n                                            refactorings.push({ name: mp.name, itemType: 'signal', target: BindingTarget.event, targetName: a[0], service: this, designItem: d, type: 'script', sourceObject: script, display: c.type + '/' + a[0].substring(1) + '/' + p + '[signal]', refactor: newValue => mp.name = newValue });\r\n                                        } else if (mp.source === 'property') {\r\n                                            refactorings.push({ name: mp.name, itemType: 'property', target: BindingTarget.event, targetName: a[0], service: this, designItem: d, type: 'script', sourceObject: script, display: c.type + '/' + a[0].substring(1) + '/' + p + '[property]', refactor: newValue => mp.name = newValue });\r\n                                        } else if (mp.source === 'complexString') {\r\n                                            for (let m of mp.name.matchAll(/\\{(.*?)\\}/g)) {\r\n                                                let full = m[0];\r\n                                                let nm = m[1];\r\n                                                if (nm[0] === '?') {\r\n                                                    let prefix = '?';\r\n                                                    nm = nm.substring(1);\r\n                                                    if (nm[0] === '?') {\r\n                                                        prefix = '??';\r\n                                                        nm = nm.substring(1);\r\n                                                    }\r\n                                                    refactorings.push({ name: nm, itemType: 'property', target: BindingTarget.event, targetName: a[0], service: this, designItem: d, type: 'script', sourceObject: script, display: c.type + '/' + a[0].substring(1) + '/' + p + '[complexString]->property', refactor: newValue => mp.name = mp.name.replace(full, '{' + prefix + newValue + '}') });\r\n                                                } else {\r\n                                                    refactorings.push({ name: nm, itemType: 'signal', target: BindingTarget.event, targetName: a[0], service: this, designItem: d, type: 'script', sourceObject: script, display: c.type + '/' + a[0].substring(1) + '/' + p + '[complexString]->signal', refactor: newValue => mp.name = mp.name.replace(full, '{' + newValue + '}') });\r\n                                                }\r\n                                            }\r\n                                        }\r\n                                    }\r\n                                }\r\n                                switch (c.type) {\r\n                                    case 'SetSignalValue':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'ToggleSignalValue':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'IncrementSignalValue':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'DecrementSignalValue':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'SetBitInSignal':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'ClearBitInSignal':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'ToggleBitInSignal':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'OpenScreen':\r\n                                        if (c.screen && typeof c.screen === 'string')\r\n                                            refactorings.push({ name: c.screen, itemType: 'screen', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/screen', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.screen = newValue });\r\n                                        break;\r\n                                    case 'OpenDialog':\r\n                                        if (c.screen && typeof c.screen === 'string')\r\n                                            refactorings.push({ name: c.screen, itemType: 'screen', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/screen', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.screen = newValue });\r\n                                        break;\r\n                                    case 'CalculateSignalValue':\r\n                                        if (c.targetSignal && typeof c.targetSignal === 'string')\r\n                                            refactorings.push({ name: c.targetSignal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/targetSignal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.targetSignal = newValue });\r\n                                        break;\r\n                                    case 'SubscribeSignal':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'UnsubscribeSignal':\r\n                                        if (c.signal && typeof c.signal === 'string')\r\n                                            refactorings.push({ name: c.signal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/signal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.signal = newValue });\r\n                                        break;\r\n                                    case 'ShowMessageBox':\r\n                                        if (c.resultSignal && typeof c.resultSignal === 'string')\r\n                                            refactorings.push({ name: c.resultSignal, itemType: 'signal', target: BindingTarget.event, targetName: a[0], display: c.type + '/' + a[0].substring(1) + '/resultSignal', service: this, designItem: d, type: 'script', sourceObject: script, refactor: newValue => c.resultSignal = newValue });\r\n                                        break;\r\n                                }\r\n                            }\r\n                        } else if ('blocks' in script) {\r\n\r\n                        }\r\n                        if ('parameters' in script) {\r\n                            for (let p in script.parameters) {\r\n                                if (typeof script.parameters[p] === 'string') {\r\n                                    refactorings.push({ name: script.parameters[p], itemType: 'parameter', target: BindingTarget.event, targetName: a[0], service: this, designItem: d, type: 'script', sourceObject: script, display: a[0].substring(1) + '/parameters/' + p, refactor: newValue => script.parameters[p] = newValue });\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return refactorings;\r\n    }\r\n\r\n    refactor(refactoring: (IRefactoring & { refactor: (newValue) => void }), oldValue: string, newValue: string) {\r\n        refactoring.refactor(newValue);\r\n        let scriptString = JSON.stringify(refactoring.sourceObject);\r\n        refactoring.designItem.setAttribute(refactoring.targetName, scriptString);\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/SignalPropertyEditor.ts",
    "content": "import { BasePropertyEditor, IProperty, ValueType } from \"@node-projects/web-component-designer\";\r\nimport { VisualizationShell } from \"../interfaces/VisualizationShell.js\";\r\n\r\nexport class SignalPropertyEditor extends BasePropertyEditor<HTMLElement> {\r\n\r\n  _ip: HTMLInputElement;\r\n  protected _container: HTMLDivElement;\r\n\r\n  constructor(property: IProperty, shell: VisualizationShell) {\r\n    super(property);\r\n\r\n    this._container = document.createElement('div');\r\n    this._container.style.display = 'flex';\r\n    this._ip = document.createElement('input');\r\n    this._ip.onchange = (e) => this._valueChanged(this._ip.value);\r\n    this._ip.onfocus = (e) => {\r\n      this._ip.selectionStart = 0;\r\n      this._ip.selectionEnd = this._ip.value?.length;\r\n    }\r\n    this._container.appendChild(this._ip);\r\n    let btn = document.createElement('button');\r\n    btn.textContent = '...';\r\n    btn.onclick = async () => {\r\n      let b = shell.createBindableObjectBrowser();\r\n      b.initialize(this.designItems[0].serviceContainer, this.designItems[0].instanceServiceContainer, 'property');\r\n      b.title = 'select signal...';\r\n      const abortController = new AbortController();\r\n      b.objectDoubleclicked.on(() => {\r\n        abortController.abort();\r\n        this._ip.value = b.selectedObject.fullName;\r\n        this._valueChanged(this._ip.value);\r\n      });\r\n      let res = await shell.openConfirmation(b, { x: 100, y: 100, width: 400, height: 300, abortSignal: abortController.signal });\r\n      if (res) {\r\n        this._ip.value = b.selectedObject.fullName;\r\n        this._valueChanged(this._ip.value);\r\n      }\r\n    }\r\n    this._container.appendChild(btn);\r\n    this.element = this._container;\r\n  }\r\n\r\n  refreshValue(valueType: ValueType, value: any) {\r\n    if (value == null)\r\n      this._ip.value = \"\";\r\n    else\r\n      this._ip.value = value;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/VisualizationBindingsRefactorService.ts",
    "content": "import { IBinding, IDesignItem, IRefactoring, IRefactorService } from \"@node-projects/web-component-designer\";\r\n\r\n\r\nexport class VisualizationBindingsRefactorService implements IRefactorService {\r\n    getRefactorings(designItems: IDesignItem[]): IRefactoring[] {\r\n        let refactorings: (IRefactoring & { shortName?: string, prefix?: string })[] = [];\r\n        for (let d of designItems) {\r\n            let bindings = d.serviceContainer.bindingService.getBindings(d);\r\n            if (bindings) {\r\n                for (let b of bindings) {\r\n                    for (let s of b.bindableObjectNames) {\r\n                        let itemType = 'signal';\r\n                        let prefix = \"\"\r\n                        if (s.includes(':')) {\r\n                            let nm = s.split(':')[0];\r\n                            let sng = s.substring(nm.length + 1);\r\n                            if (sng.startsWith('?')) {\r\n                                sng = sng.substring(1);\r\n                                prefix = '?';\r\n                                itemType = 'property';\r\n                                if (sng.startsWith('?')) {\r\n                                    sng = sng.substring(1);\r\n                                    prefix = '??';\r\n                                }\r\n                            }\r\n                            refactorings.push({ service: this, name: sng, itemType: itemType, designItem: d, type: 'binding', sourceObject: b, display: b.target + '/' + b.targetName + ' - ' + nm + ':', shortName: nm, prefix: prefix });\r\n                        } else {\r\n                            if (s.startsWith('?')) {\r\n                                s = s.substring(1);\r\n                                prefix = '?';\r\n                                itemType = 'property';\r\n                                if (s.startsWith('?')) {\r\n                                    s = s.substring(1);\r\n                                    prefix = '??';\r\n                                }\r\n                            }\r\n                            refactorings.push({ service: this, name: s, itemType: itemType, designItem: d, type: 'binding', sourceObject: b, display: b.target + '/' + b.targetName, prefix: prefix });\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        return refactorings;\r\n    }\r\n\r\n    refactor(refactoring: (IRefactoring & { shortName?: string, prefix?: string }), oldValue: string, newValue: string) {\r\n        let binding = refactoring.sourceObject as IBinding;\r\n        if (refactoring.shortName)\r\n            binding.bindableObjectNames = binding.bindableObjectNames.map(x => x == refactoring.shortName + ':' + refactoring.prefix + oldValue ? refactoring.shortName + ':' + refactoring.prefix + newValue : x);\r\n        else\r\n            binding.bindableObjectNames = binding.bindableObjectNames.map(x => x == refactoring.prefix + oldValue ? refactoring.prefix + newValue : x);\r\n        refactoring.designItem.serviceContainer.bindingService.setBinding(refactoring.designItem, binding);\r\n    }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/VisualizationBindingsService.ts",
    "content": "import { BindingMode, BindingTarget, IBinding, IBindingService, IDesignItem, PropertiesHelper } from \"@node-projects/web-component-designer\";\r\nimport { VisualizationBinding } from \"../interfaces/VisualizationBinding.js\";\r\nimport { BindingsHelper } from \"../helpers/BindingsHelper.js\";\r\n\r\nexport class VisualizationBindingsService implements IBindingService {\r\n  constructor(bindingsHelper: BindingsHelper) {\r\n    this._bindingsHelper = bindingsHelper;\r\n  }\r\n\r\n  private _bindingsHelper: BindingsHelper;\r\n\r\n  public static type = 'visualization-binding';\r\n\r\n  getBindings(designItem: IDesignItem): (IBinding & { converter: Record<string, any> })[] {\r\n    const iobBindings = Array.from(this._bindingsHelper.getBindings(designItem.element));\r\n    return iobBindings.map(x => ({\r\n      targetName: (x[1].target === BindingTarget.css || x[1].target === BindingTarget.attribute) ? PropertiesHelper.camelToDashCase(x[0]) : x[0],\r\n      target: x[1].target,\r\n      mode: x[1].twoWay ? BindingMode.twoWay : BindingMode.oneWay,\r\n      invert: x[1].inverted,\r\n      bindableObjectNames: x[1].signal.split(';'),\r\n      expression: x[1].expression,\r\n      expressionTwoWay: x[1].expressionTwoWay,\r\n      converter: x[1].converter,\r\n      //type: x[1].type,\r\n      type: VisualizationBindingsService.type,\r\n      service: this,\r\n      changedEvents: x[1].events,\r\n      historic: x[1].historic,\r\n      writeBackSignal: x[1].writeBackSignal,\r\n    }));\r\n  }\r\n\r\n  setBinding(designItem: IDesignItem, binding: IBinding): boolean {\r\n    let bnd: VisualizationBinding = { signal: binding.bindableObjectNames.join(';'), target: binding.target };\r\n    bnd.inverted = binding.invert;\r\n    bnd.twoWay = binding.mode == BindingMode.twoWay;\r\n    bnd.expression = binding.expression;\r\n    bnd.expressionTwoWay = binding.expressionTwoWay;\r\n    //@ts-ignore\r\n    bnd.historic = binding.historic;\r\n    bnd.type = binding.type;\r\n    bnd.converter = binding.converters;\r\n    bnd.target = binding.target;\r\n    bnd.events = binding.changedEvents;\r\n\r\n    let serializedBnd = this._bindingsHelper.serializeBinding(designItem.element, binding.targetName, bnd);\r\n    let group = designItem.openGroup('edit_binding');\r\n    designItem.setAttribute(serializedBnd[0], serializedBnd[1]);\r\n    group.commit();\r\n\r\n    return true;\r\n  }\r\n\r\n  clearBinding(designItem: IDesignItem, propertyName: string, propertyTarget: BindingTarget): boolean {\r\n    const name = this._bindingsHelper.getBindingAttributeName(designItem.element, propertyName, propertyTarget);\r\n    designItem.removeAttribute(name);\r\n    return true;\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/services/VisualizationEventsService.ts",
    "content": "import {\r\n  EventsService,\r\n  IDesignItem,\r\n  IEvent,\r\n} from \"@node-projects/web-component-designer\";\r\nimport { cyclicAttributeName } from \"../scripting/ScriptSystem.js\";\r\n\r\nexport class VisualizationEventsService extends EventsService {\r\n  public static _cyclicEvent: IEvent[] = [\r\n    {\r\n      name: `${cyclicAttributeName}:100`,\r\n      propertyName: undefined,\r\n      eventObjectName: \"Event\",\r\n      description: \"this event will retrigger itself every 100 ms\",\r\n    },\r\n  ];\r\n\r\n  public override getPossibleEvents(designItem: IDesignItem): IEvent[] {\r\n    let events = super.getPossibleEvents(designItem);\r\n    events.push(...VisualizationEventsService._cyclicEvent);\r\n    return events;\r\n  }\r\n\r\n  public override getEvent(designItem: IDesignItem, name: string): IEvent {\r\n    if (name.includes(cyclicAttributeName)) {\r\n      let evt = VisualizationEventsService._cyclicEvent.find(\r\n        (x) => x.name == name,\r\n      );\r\n      return (\r\n        evt ?? { name, propertyName: \"on\" + name, eventObjectName: \"Event\" }\r\n      );\r\n    }\r\n\r\n    return super.getEvent(designItem, name);\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-visualization-addons/src/setupVisuService.ts",
    "content": ""
  },
  {
    "path": "packages/web-component-designer-visualization-addons/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false,\r\n    \"moduleResolution\": \"bundler\"\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/README.md",
    "content": "# web-component-designer-widgets-wunderbaum\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-widgets-wunderbaum\r\n\r\n     npm i @node-projects/web-component-designer-widgets-wunderbaum\r\n\r\n## Description\r\n\r\nThis package contains widgets using wunderbaum. This are more powerfull than the included ones in the designer.\r\n\r\n## Usage\r\n\r\nthen import the widgets:\r\n\r\n    import '@node-projects/web-component-designer-widgets-wunderbaum';\r\n\r\nnow you could use following widgets:\r\n\r\n    <node-projects-palette-tree-view></node-projects-palette-tree-view>\r\n    <node-projects-tree-view-extended></node-projects-tree-view-extended>"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/package.json",
    "content": "{\r\n  \"description\": \"web-component-designer addon: Widgets using wunderbaum\",\r\n  \"name\": \"@node-projects/web-component-designer-widgets-wunderbaum\",\r\n  \"version\": \"0.2.1\",\r\n  \"type\": \"module\",\r\n  \"main\": \"./dist/index.js\",\r\n  \"author\": \"jochen.kuehner@gmx.de\",\r\n  \"license\": \"MIT\",\r\n  \"scripts\": {\r\n    \"tsc\": \"tsc\",\r\n    \"build\": \"tsc\",\r\n    \"link\": \"npm link\",\r\n    \"prepublishOnly\": \"npm run build && npm run bundle\",\r\n    \"bundle\": \"esbuild ./dist/index.js --format=esm --minify --external:wunderbaum --external:@node-projects/* --platform=neutral --bundle --outfile=./dist/index-min.js\"\r\n  },\r\n  \"dependencies\": {    \r\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\r\n    \"@node-projects/web-component-designer\": \">=0.2.0\",\r\n    \"wunderbaum\": \">=0.13.0\"\r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/Constants.ts",
    "content": "let imporUrl = new URL((import.meta.url));\r\nexport var assetsPath = imporUrl.origin + imporUrl.pathname.split('/').slice(0, -1).join('/') + '/../assets/';"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/index.ts",
    "content": "export * from \"./widgets/bindableObjectsBrowser/bindable-objects-browser.js\";\r\nexport * from \"./widgets/paletteView/paletteTreeView.js\";\r\nexport * from \"./widgets/treeView/treeViewExtended.js\";\r\nexport * from \"./widgets/treeView/ExpandCollapseContextMenu.js\";\r\nexport * from \"./widgets/WunderbaumOptions.js\";"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/widgets/WunderbaumOptions.ts",
    "content": "import { css } from \"@node-projects/base-custom-webcomponent\";\r\nimport { WunderbaumOptions } from \"wb_options\";\r\nimport { assetsPath } from \"../Constants.js\";\r\n\r\nexport const defaultStyle = css`\r\ni.wb-icon > span.wb-badge {\r\n  font-size: 70%;\r\n  background-color: #486471;\r\n}\r\ndiv.wunderbaum {\r\n  --wb-active-color-grayscale: #dddddd;\r\n}\r\ndiv.wunderbaum span.wb-node i.wb-icon {\r\n  background-size: 12px 12px;\r\n  background-position-x: 5px;\r\n  background-position-y: 5px;\r\n}\r\ndiv.wunderbaum span.wb-node i.wb-expander {\r\n  background-size: 12px 12px;\r\n  background-position-x: 5px;\r\n  background-position-y: 5px;\r\n}`;\r\n\r\nexport const defaultOptions: WunderbaumOptions = {\r\n  element: null,\r\n  debugLevel: 0,\r\n  scrollIntoViewOnExpandClick: false,\r\n  //@ts-ignore\r\n  iconMap: {\r\n    expanderCollapsed: assetsPath + 'images/expander.svg',\r\n    expanderExpanded: assetsPath + 'images/expanderClose.svg',\r\n    folder: assetsPath + 'images/folder.svg',\r\n    folderLazy: assetsPath + 'images/folder.svg',\r\n    folderOpen: assetsPath + 'images/folder.svg',\r\n    doc: assetsPath + 'images/file.svg',\r\n  },\r\n  quicksearch: true,\r\n  checkbox: false,\r\n  source: [],\r\n  header: false,\r\n  iconBadge: (e) => {\r\n    //@ts-ignore\r\n    const node = e.node;\r\n    if (node.expanded || !node.children || !node.children.length) {\r\n      return null;\r\n    }\r\n    return { badge: node.children.length };\r\n  }\r\n}"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/widgets/bindableObjectsBrowser/bindable-objects-browser.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, TypedEvent, css, cssFromString, html } from '@node-projects/base-custom-webcomponent';\r\nimport { BindableObjectsTarget, IBindableObject, IBindableObjectsBrowser, IBindableObjectsService, InstanceServiceContainer, ServiceContainer, dragDropFormatNameBindingObject } from '@node-projects/web-component-designer';\r\nimport { WbNodeData } from 'types';\r\nimport { Wunderbaum } from 'wunderbaum';\r\nimport { defaultOptions, defaultStyle } from '../WunderbaumOptions.js';\r\n//@ts-ignore\r\nimport wunderbaumStyle from 'wunderbaum/dist/wunderbaum.css' with { type: 'css' };\r\n\r\ntype serviceNode = { service: IBindableObjectsService, bindable: IBindableObject<any> }\r\n\r\nexport class BindableObjectsBrowser extends BaseCustomWebComponentConstructorAppend implements IBindableObjectsBrowser {\r\n  private _treeDiv: HTMLDivElement;\r\n  private _tree: Wunderbaum;\r\n\r\n  public selectedObject: IBindableObject<any>;\r\n\r\n  public objectDoubleclicked = new TypedEvent<void>;\r\n\r\n  static override readonly style = css``;\r\n\r\n  static override template = html`\r\n      <div id=\"tree\" style=\"height: 100%; overflow: auto; box-sizing: border-box;\" class=\"wb-skeleton wb-initializing wb-no-select wb-alternate\">\r\n      </div>`\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    this.shadowRoot.adoptedStyleSheets = [cssFromString(wunderbaumStyle), defaultStyle, BindableObjectsBrowser.style];\r\n\r\n    this._treeDiv = this._getDomElement<HTMLDivElement>('tree');\r\n    this.shadowRoot.appendChild(this._treeDiv);\r\n\r\n    this._tree = new Wunderbaum({\r\n      ...defaultOptions,\r\n      element: this._treeDiv,\r\n      iconBadge: null,\r\n      lazyLoad: (event) => {\r\n        return new Promise(async resolve => {\r\n          const service: IBindableObjectsService = (<serviceNode>event.node.data).service;\r\n          const bindable: IBindableObject<any> = (<serviceNode>event.node.data).bindable;\r\n          let children: IBindableObject<any>[];\r\n          if (bindable?.children)\r\n            children = bindable.children;\r\n          else\r\n            children = await service.getBindableObjects(bindable, this._instanceServiceContainer);\r\n          resolve(children.map(x => ({\r\n            title: x.name,\r\n            service,\r\n            bindable: x,\r\n            lazy: x.children !== false\r\n          })));\r\n        });\r\n      },\r\n      //@ts-ignore\r\n      dblclick: (e) => {\r\n        this.objectDoubleclicked.emit();\r\n        return true;\r\n      },\r\n      activate: (event) => {\r\n        this.selectedObject = event.node.data.bindable;\r\n      },\r\n      dnd: {\r\n        guessDropEffect: true,\r\n        preventRecursion: true,\r\n        preventVoidMoves: false,\r\n        serializeClipboardData: false,\r\n        dragStart: (e) => {\r\n          e.event.dataTransfer.effectAllowed = \"all\";\r\n          e.event.dataTransfer.setData(dragDropFormatNameBindingObject, JSON.stringify(e.node.data.bindable));\r\n          e.event.dataTransfer.dropEffect = \"copy\";\r\n          return true\r\n        },\r\n        dragEnter: (e) => {\r\n          return true;\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  private _instanceServiceContainer;\r\n\r\n  public async initialize(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, bindableObjectsTarget: BindableObjectsTarget) {\r\n    this._instanceServiceContainer = instanceServiceContainer;\r\n    let rootNode = this._tree.root;\r\n    rootNode.removeChildren();\r\n    const services = serviceContainer.bindableObjectsServices;\r\n    for (const s of services) {\r\n      if (!s.hasObjectsForInstanceServiceContainer(this._instanceServiceContainer, bindableObjectsTarget))\r\n        continue;\r\n      this._tree.root.addChildren(<WbNodeData>{\r\n        title: s.name,\r\n        lazy: true,\r\n        service: s\r\n      });\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-bindable-objects-browser', BindableObjectsBrowser);"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/widgets/paletteView/paletteTreeView.ts",
    "content": "import { css, html, BaseCustomWebComponentConstructorAppend, cssFromString } from '@node-projects/base-custom-webcomponent';\r\nimport { IElementDefinition, IElementsService, NamedTools, ServiceContainer, dragDropFormatNameElementDefinition } from '@node-projects/web-component-designer';\r\nimport { Wunderbaum } from 'wunderbaum';\r\nimport { defaultOptions, defaultStyle } from '../WunderbaumOptions.js';\r\n//@ts-ignore\r\nimport wunderbaumStyle from 'wunderbaum/dist/wunderbaum.css' with { type: 'css' };\r\nimport { WbNodeData } from 'types';\r\n\r\nexport class PaletteTreeView extends BaseCustomWebComponentConstructorAppend {\r\n  private _treeDiv: HTMLTableElement;\r\n  private _tree: Wunderbaum;\r\n  private _filter: HTMLInputElement;\r\n\r\n  static override readonly style = css`\r\n        :host {\r\n          display: block;\r\n        }\r\n\r\n        * {\r\n            touch-action: none;\r\n        }`;\r\n\r\n  static override readonly template = html`\r\n      <div style=\"height: 100%;\">\r\n        <input id=\"input\" style=\"width: 100%; height: 25px; box-sizing: border-box;\" placeholder=\"Filter...\" autocomplete=\"off\">\r\n        <div style=\"height: calc(100% - 26px);\">\r\n          <div id=\"treetable\" class=\"wb-alternate\" style=\"min-width: 100%; box-sizing: border-box;\"></div>\r\n        </div>\r\n      </div>`;\r\n\r\n  public serviceContainer: ServiceContainer;\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    this.shadowRoot.adoptedStyleSheets = [cssFromString(wunderbaumStyle), defaultStyle, PaletteTreeView.style];\r\n\r\n    this._filter = this._getDomElement<HTMLInputElement>('input');\r\n    this._filter.onkeyup = () => {\r\n      let match = this._filter.value;\r\n      this._tree.filterNodes((node) => {\r\n        return new RegExp(match, \"i\").test(node.title);\r\n      }, {});\r\n    }\r\n\r\n    this._treeDiv = this._getDomElement<HTMLTableElement>('treetable')\r\n\r\n    this._tree = new Wunderbaum({\r\n      ...defaultOptions,\r\n      element: this._treeDiv,\r\n      filter: {\r\n        autoExpand: true,\r\n        mode: 'hide',\r\n        highlight: true\r\n      },\r\n      click: (e) => {\r\n        if (e.event) { // only for clicked items, not when elements selected via code.\r\n          let node = e.node;\r\n          let elDef: IElementDefinition = node.data.ref;\r\n          if (elDef) {\r\n            let tool = this.serviceContainer.designerTools.get(elDef.tool ?? NamedTools.DrawElementTool);\r\n            if (typeof tool == 'function')\r\n              tool = new tool(elDef)\r\n            this.serviceContainer.globalContext.tool = tool;\r\n          }\r\n        }\r\n      },\r\n      dnd: {\r\n        guessDropEffect: true,\r\n        preventRecursion: true, // Prevent dropping nodes on own descendants\r\n        preventVoidMoves: false,\r\n        serializeClipboardData: false,\r\n        dragStart: (e) => {\r\n          e.event.dataTransfer.effectAllowed = \"all\";\r\n          e.event.dataTransfer.setData(dragDropFormatNameElementDefinition, JSON.stringify(e.node.data.ref));\r\n          e.event.dataTransfer.dropEffect = \"copy\";\r\n          return true;\r\n        },\r\n        dragEnter: (e) => {\r\n          return false;\r\n        }\r\n      }\r\n    });\r\n  }\r\n\r\n  public async loadControls(serviceContainer: ServiceContainer, elementsServices: IElementsService[]) {\r\n    this.serviceContainer = serviceContainer;\r\n\r\n    let rootNode = this._tree.root;\r\n    rootNode.removeChildren();\r\n\r\n    for (const s of elementsServices) {\r\n      const newNode = rootNode.addChildren({\r\n        title: s.name\r\n      });\r\n\r\n      try {\r\n        let elements = await s.getElements();\r\n        for (let e of elements) {\r\n          let node: WbNodeData = {\r\n            title: e.name ?? e.tag,\r\n            //@ts-ignore\r\n            ref: e\r\n          };\r\n          if (e.icon)\r\n            node.icon = e.icon;\r\n          newNode.addChildren(node);\r\n        }\r\n      } catch (err) {\r\n        console.warn('Error loading elements', err);\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-palette-tree-view', PaletteTreeView);"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/widgets/treeView/ExpandCollapseContextMenu.ts",
    "content": "import { ContextmenuInitiator, IContextMenuExtension, IContextMenuItem, IDesignItem, IDesignerCanvas } from \"@node-projects/web-component-designer\";\nimport { TreeViewExtended } from \"./treeViewExtended.js\";\nimport { assetsPath } from \"../../Constants.js\";\n\nexport class ExpandCollapseContextMenu implements IContextMenuExtension {\n\n  public shouldProvideContextmenu(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator) {\n    if (initiator == 'treeView') {\n      return true\n    }\n    return false;\n  }\n\n  public provideContextMenuItems(event: MouseEvent, designerView: IDesignerCanvas, designItem: IDesignItem, initiator: ContextmenuInitiator, provider: TreeViewExtended): IContextMenuItem[] {\n    return [\n      {\n        title: 'collapse children', icon: `<img src=\"${assetsPath + 'icons/collapse.svg'}\">`, action: () => {\n          provider.collapseChildren(designItem)\n        }\n      },\n      {\n        title: 'expand children', icon: `<img src=\"${assetsPath + 'icons/expand.svg'}\">`, action: () => {\n          provider.expandChildren(designItem)\n        }\n      },\n    ]\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/src/widgets/treeView/treeViewExtended.ts",
    "content": "import { css, html, BaseCustomWebComponentConstructorAppend, Disposable, cssFromString } from '@node-projects/base-custom-webcomponent';\r\nimport { NodeType, ITreeView, InstanceServiceContainer, IDesignItem, assetsPath, IContextMenuItem, ContextMenu, switchContainer, ISelectionChangedEvent, DomConverter, ForceCssContextMenu, hasCommandKey } from '@node-projects/web-component-designer';\r\nimport { Wunderbaum } from 'wunderbaum';\r\nimport { defaultOptions, defaultStyle } from '../WunderbaumOptions.js'\r\n//@ts-ignore\r\nimport wunderbaumStyle from 'wunderbaum/dist/wunderbaum.css' with { type: 'css' };\r\n\r\ntype WunderbaumNode = {\r\n  getColElem(n: number);\r\n  addChildren({ }: any);\r\n}\r\ntype treeNode = { title: string, ref: IDesignItem, children?: treeNode[] };\r\n\r\nconst wbNodeSymbol = Symbol.for('wunderbaumnode');\r\n\r\nexport class TreeViewExtended extends BaseCustomWebComponentConstructorAppend implements ITreeView {\r\n\r\n  private _treeDiv: HTMLTableElement;\r\n  private _tree: Wunderbaum;\r\n  private _filter: HTMLInputElement;\r\n  private _instanceServiceContainer: InstanceServiceContainer;\r\n  private _selectionChangedHandler: Disposable;\r\n  private _contentChangedHandler: Disposable;\r\n\r\n  static override readonly style = css`\r\n      * {\r\n          touch-action: none;\r\n          cursor: default;\r\n      }\r\n    \r\n      .cmd {\r\n        display: flex;\r\n        position: absolute;\r\n        right: 0;\r\n        top: 0;\r\n        height: 100%;\r\n        padding-right: 2px;\r\n        align-items: center;\r\n        gap: 2px;\r\n        background: #ffffffc9;\r\n        width: 42px;\r\n        justify-content: flex-end;\r\n        background: white;\r\n      }\r\n      .cmd > img {\r\n        width: 10px;\r\n      }\r\n      \r\n      div.wunderbaum div.wb-row {\r\n        display: block;\r\n      }\r\n      span.wb-node.wb-col {\r\n        width: unset !important;\r\n        display: inline-block;\r\n      }\r\n      div.wunderbaum span.wb-node span.wb-title {\r\n        text-overflow: unset;\r\n        width: unset !important;\r\n      }\r\n      div.forced {\r\n        border-radius: 50%;\r\n        width: 10px;\r\n        height: 10px;\r\n        background: transparent;\r\n        pointer-events: none;\r\n        top: 5px;\r\n        left: 5px;\r\n        position: relative;\r\n      }\r\n      div.isforced {\r\n        background: orange;\r\n        pointer-events: auto;\r\n      }`;\r\n\r\n  static override readonly template = html`\r\n      <div style=\"height: 100%;\">\r\n        <input id=\"input\" style=\"width: 100%; box-sizing: border-box; height:27px;\" placeholder=\"Filter... (regex)\" autocomplete=\"off\">\r\n        <div style=\"height: 18px; border: solid black 1px; padding-left:2px; background: gray;\">\r\n          <svg id=\"expandAll\" style=\"width: 16px;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path d=\"M64 80a16 16 0 0 0-16 16v320a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16zM0 96a64 64 0 0 1 64-64h320a64 64 0 0 1 64 64v320a64 64 0 0 1-64 64H64a64 64 0 0 1-64-64zm200 248v-64h-64a24 24 0 1 1 0-48h64v-64a24 24 0 1 1 48 0v64h64a24 24 0 1 1 0 48h-64v64a24 24 0 1 1-48 0\"></path></svg>\r\n          <svg id=\"collapseAll\" style=\"width: 16px;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path d=\"M64 80a16 16 0 0 0-16 16v320a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16zM0 96a64 64 0 0 1 64-64h320a64 64 0 0 1 64 64v320a64 64 0 0 1-64 64H64a64 64 0 0 1-64-64zm152 136h144a24 24 0 1 1 0 48H152a24 24 0 1 1 0-48\"></path></svg>\r\n        </div>\r\n        <div style=\"height: calc(100% - 46px);\">\r\n        <div id=\"treetable\" class=\"wb-alternate\" style=\"min-width: 100%; box-sizing: border-box;\"></div>\r\n        </div>\r\n      </div>`;\r\n\r\n  public static properties = {\r\n    instanceServiceContainer: Object,\r\n  }\r\n\r\n  constructor() {\r\n    super();\r\n    this._restoreCachedInititalValues();\r\n    this.shadowRoot.adoptedStyleSheets = [cssFromString(wunderbaumStyle), defaultStyle, TreeViewExtended.style];\r\n\r\n    this._filter = this._getDomElement<HTMLInputElement>('input');\r\n    this._filter.onkeyup = () => {\r\n      this._filterNodes();\r\n    }\r\n\r\n    this._treeDiv = this._getDomElement<HTMLTableElement>('treetable');\r\n    this._treeDiv.onscroll = () => {\r\n      for (let e of this._treeDiv.querySelectorAll('.cmd')) {\r\n        (<HTMLElement>e).style.right = '-' + this._treeDiv.scrollLeft + 'px';\r\n      }\r\n    }\r\n\r\n    const expand = this._getDomElement<SVGSVGElement>('expandAll');\r\n    expand.onclick = () => {\r\n      this._tree.root\r\n        .visit((node) => {\r\n          if (!node.isExpanded())\r\n            node.setExpanded(true);\r\n        });\r\n    }\r\n\r\n    const collapse = this._getDomElement<SVGSVGElement>('collapseAll');\r\n    collapse.onclick = () => {\r\n      this._tree.root\r\n        .visit((node) => {\r\n          if (node.isExpanded())\r\n            node.setExpanded(false);\r\n        });\r\n    }\r\n  }\r\n\r\n  _filterNodes() {\r\n    let match = this._filter.value;\r\n    if (match) {\r\n      const regEx = new RegExp(match, \"i\");\r\n      this._tree.filterNodes((node) => {\r\n        const di: IDesignItem = node.data.ref\r\n        return regEx.test(di.name) || regEx.test(di.id) || regEx.test(di.content);\r\n      }, {});\r\n    }\r\n    else {\r\n      this._tree.clearFilter();\r\n    }\r\n  }\r\n\r\n  _showHideAtDesignTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    if (designItem.hideAtDesignTime)\r\n      img.src = assetsPath + \"images/treeview/eyeclose.png\";\r\n    else\r\n      img.src = assetsPath + \"images/treeview/eyeopen.png\";\r\n  }\r\n\r\n  _switchHideAtDesignTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    designItem.hideAtDesignTime = !designItem.hideAtDesignTime;\r\n    this._showHideAtDesignTimeState(img, designItem);\r\n  }\r\n\r\n  _showLockAtDesignTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    if (designItem.lockAtDesignTime)\r\n      img.src = assetsPath + \"images/treeview/lock.png\";\r\n    else\r\n      img.src = assetsPath + \"images/treeview/dot.png\";\r\n  }\r\n\r\n  _switchLockAtDesignTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    designItem.lockAtDesignTime = !designItem.lockAtDesignTime;\r\n    this._showLockAtDesignTimeState(img, designItem);\r\n  }\r\n\r\n  _showHideAtRunTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    if (designItem.hideAtRunTime)\r\n      img.src = assetsPath + \"images/treeview/eyeclose.png\";\r\n    else\r\n      img.src = assetsPath + \"images/treeview/eyeopen.png\";\r\n  }\r\n\r\n  _switchHideAtRunTimeState(img: HTMLImageElement, designItem: IDesignItem) {\r\n    designItem.hideAtRunTime = !designItem.hideAtRunTime;\r\n    this._showHideAtRunTimeState(img, designItem);\r\n  }\r\n\r\n  public showDesignItemContextMenu(designItem: IDesignItem, event: MouseEvent) {\r\n    event.preventDefault();\r\n    const mnuItems: IContextMenuItem[] = [];\r\n    for (let cme of designItem.serviceContainer.designerContextMenuExtensions) {\r\n      if (cme.shouldProvideContextmenu(event, designItem.instanceServiceContainer.designerCanvas, designItem, 'treeView')) {\r\n        mnuItems.push(...cme.provideContextMenuItems(event, designItem.instanceServiceContainer.designerCanvas, designItem, 'treeView', this));\r\n      }\r\n    }\r\n    let ctxMnu = ContextMenu.show(mnuItems, event);\r\n    return ctxMnu;\r\n  }\r\n\r\n  selectedFromTree = false;\r\n\r\n  async ready() {\r\n    this._initWunderbaum();\r\n    if (this._instanceServiceContainer) {\r\n      this.createTree(this._instanceServiceContainer.rootDesignItem);\r\n    }\r\n  }\r\n\r\n  private _initWunderbaum() {\r\n    this._tree = new Wunderbaum({\r\n      ...defaultOptions,\r\n      element: this._treeDiv,\r\n      //@ts-ignore\r\n      click: (e) => {\r\n        if (e.event) { // only for clicked items, not when elements selected via code.\r\n          let node = e.node;\r\n          let designItem: IDesignItem = node.data.ref;\r\n          if (designItem) {\r\n            this.selectedFromTree = true;\r\n            setTimeout(() => {\r\n              this.selectedFromTree = false;\r\n            }, 50);\r\n            if (hasCommandKey(e.event)) {\r\n              const sel = [...designItem.instanceServiceContainer.selectionService.selectedElements];\r\n              const idx = sel.indexOf(designItem);\r\n              if (idx >= 0) {\r\n                sel.splice(idx, 1);\r\n                designItem.instanceServiceContainer.selectionService.setSelectedElements(sel);\r\n              } else {\r\n                designItem.instanceServiceContainer.selectionService.setSelectedElements([...sel, designItem]);\r\n              }\r\n            }\r\n            else {\r\n              designItem.instanceServiceContainer.selectionService.setSelectedElements([designItem]);\r\n            }\r\n          }\r\n        }\r\n        const disableExpand = (<MouseEvent>e.event).ctrlKey || (<MouseEvent>e.event).shiftKey;\r\n        return !disableExpand;\r\n      },\r\n      dnd: {\r\n        guessDropEffect: true,\r\n        preventRecursion: true,\r\n        preventVoidMoves: false,\r\n        serializeClipboardData: false,\r\n        dragStart: (e) => {\r\n          e.event.dataTransfer.effectAllowed = \"all\";\r\n          e.event.dataTransfer.dropEffect = \"move\";\r\n          return true;\r\n        },\r\n        dragEnter: (e) => {\r\n          e.event.dataTransfer.dropEffect = e.event.ctrlKey ? 'copy' : 'move';\r\n          return true;\r\n        },\r\n        dragOver: (e) => {\r\n          e.event.dataTransfer.dropEffect = e.event.ctrlKey ? 'copy' : 'move';\r\n          //return true;\r\n        },\r\n        drop: async (e) => {\r\n          let sourceDesignitems: IDesignItem[] = [e.sourceNode].map(x => x.data.ref);\r\n          if (e.event.dataTransfer.dropEffect == 'copy') {\r\n            let newSourceDesignitems: IDesignItem[] = [];\r\n            for (let d of sourceDesignitems)\r\n              newSourceDesignitems.push(await d.clone());\r\n            sourceDesignitems = newSourceDesignitems;\r\n          }\r\n          const targetDesignitem: IDesignItem = e.node.data.ref;\r\n\r\n          let grp = targetDesignitem.openGroup(\"drag/drop in treeview\");\r\n\r\n          if (e.region == 'over') {\r\n            switchContainer(sourceDesignitems, targetDesignitem);\r\n          } else if (e.region == 'after' || e.region == 'before') {\r\n            for (let d of sourceDesignitems) {\r\n              if (d.parent != targetDesignitem.parent) {\r\n                switchContainer([d], targetDesignitem.parent);\r\n              }\r\n              if (e.region == 'before')\r\n                targetDesignitem.insertAdjacentElement(d, 'beforebegin');\r\n\r\n              else\r\n                targetDesignitem.insertAdjacentElement(d, 'afterend');\r\n            }\r\n          }\r\n\r\n          grp.commit();\r\n        }\r\n      },\r\n      filter: {\r\n        autoApply: true,   // Re-apply last filter if lazy data is loaded\r\n        autoExpand: true, // Expand all branches that contain matches while filtered\r\n        fuzzy: true,      // Match single characters in order, e.g. 'fb' will match 'FooBar'\r\n        hideExpanders: false,       // Hide expanders if all child nodes are hidden by filter\r\n        highlight: true,   // Highlight matches by wrapping inside <mark> tags\r\n        leavesOnly: false, // Match end nodes only\r\n        mode: \"hide\"       // Grayout unmatched nodes (pass \"hide\" to remove unmatched node instead)\r\n      },\r\n      render: (e) => {\r\n        if (e.isNew) {\r\n          const node = e.node;\r\n          const rowElem = e.nodeElem.parentElement;\r\n          let item: IDesignItem = node.data.ref;\r\n\r\n          e.nodeElem.oncontextmenu = (e) => this.showDesignItemContextMenu(item, e);\r\n          e.nodeElem.onmouseenter = (e) => item.instanceServiceContainer.designerCanvas.showHoverExtension(item.element, e);\r\n          e.nodeElem.onmouseleave = (e) => item.instanceServiceContainer.designerCanvas.showHoverExtension(null, e);\r\n\r\n          let sp = document.createElement(\"span\");\r\n          sp.style.display = \"inline-block\";\r\n          sp.style.width = \"42px\";\r\n          e.nodeElem.appendChild(sp);\r\n          if (item && item.nodeType === NodeType.Element && item !== item.instanceServiceContainer.rootDesignItem) {\r\n            const d = document.createElement(\"div\");\r\n            d.className = \"cmd\";\r\n\r\n            const imgL = document.createElement('img');\r\n            this._showLockAtDesignTimeState(imgL, item);\r\n            imgL.onclick = () => this._switchLockAtDesignTimeState(imgL, item);\r\n            imgL.title = 'lock';\r\n            d.appendChild(imgL);\r\n\r\n            const img = document.createElement('img');\r\n            this._showHideAtDesignTimeState(img, item);\r\n            img.onclick = () => this._switchHideAtDesignTimeState(img, item);\r\n            img.title = 'hide in designer';\r\n            d.appendChild(img);\r\n\r\n            const imgH = document.createElement('img');\r\n            this._showHideAtRunTimeState(imgH, item);\r\n            imgH.onclick = () => this._switchHideAtRunTimeState(imgH, item);\r\n            imgH.title = 'hide at runtime';\r\n            d.appendChild(imgH);\r\n\r\n            rowElem.appendChild(d);\r\n\r\n            const f = document.createElement(\"div\");\r\n            f.className = \"forced\";\r\n            f.title = \"has forced style\";\r\n            rowElem.appendChild(f);\r\n            f.addEventListener('click', (event) => {\r\n              const items = new ForceCssContextMenu().provideContextMenuItems(event, item.instanceServiceContainer.designerCanvas, item);\r\n              let ctxMenu = new ContextMenu(items, null);\r\n              ctxMenu.display(event);\r\n            });\r\n\r\n            if (item.hasForcedCss) {\r\n              f.className = \"forced isforced\";\r\n            }\r\n          }\r\n        }\r\n        e.nodeElem.querySelector(\"span.wb-title\").innerHTML = e.node.title;\r\n      },\r\n    });\r\n  }\r\n\r\n  _recomputeRunning;\r\n  _recomputeRequestedAgain;\r\n\r\n  private async refreshNode(node: WunderbaumNode, item: IDesignItem) {\r\n    const el = node.getColElem(0).parentElement;\r\n    const f = el.querySelector('.forced')\r\n    if (item.hasForcedCss)\r\n      f.classList.add('isforced');\r\n    else\r\n      f.classList.remove('isforced');\r\n  }\r\n\r\n  public async createTree(rootItem: IDesignItem) {\r\n    if (this._tree) {\r\n      if (!this._recomputeRunning) {\r\n        this._recomputeRunning = true;\r\n        setTimeout(async () => {\r\n          this._recomputeRequestedAgain = false;\r\n          await this._recomputeTree(rootItem);\r\n          this._recomputeRunning = false;\r\n          if (this._recomputeRequestedAgain) {\r\n            this._recomputeRequestedAgain = false;\r\n            this.createTree(rootItem);\r\n          }\r\n        }, 20);\r\n      } else {\r\n        this._recomputeRequestedAgain = true;\r\n      }\r\n    }\r\n  }\r\n\r\n  public set instanceServiceContainer(value: InstanceServiceContainer) {\r\n    this._selectionChangedHandler?.dispose();\r\n    this._contentChangedHandler?.dispose();\r\n    this._instanceServiceContainer = value;\r\n    if (this._instanceServiceContainer) {\r\n      this._selectionChangedHandler = this._instanceServiceContainer.selectionService.onSelectionChanged.on(e => {\r\n        this.selectionChanged(e);\r\n      });\r\n      this._contentChangedHandler = this._instanceServiceContainer.onContentChanged.on(changes => {\r\n        for (let e of changes) {\r\n          if (e.changeType === 'changed') {\r\n            for (const d of e.designItems) {\r\n              if (d[wbNodeSymbol])\r\n                this.refreshNode(d[wbNodeSymbol], d);\r\n            }\r\n          } else {\r\n            this.createTree(value.rootDesignItem);\r\n            setTimeout(() => {\r\n              this._highlight(this._instanceServiceContainer.selectionService.selectedElements);\r\n            }, 20);\r\n          }\r\n        }\r\n      });\r\n      if (this._tree)\r\n        this.createTree(value.rootDesignItem);\r\n    } else {\r\n      this._tree.root.removeChildren();\r\n    }\r\n  }\r\n\r\n  public selectionChanged(event: ISelectionChangedEvent) {\r\n    this._highlight(event.selectedElements);\r\n  }\r\n\r\n  private async _recomputeTree(rootItem: IDesignItem) {\r\n    try {\r\n      this._tree.root.removeChildren();\r\n      const newTree = this._getChildren(rootItem);\r\n      this._tree.root.addChildren(newTree);\r\n      this._tree.root.visit(node => {\r\n        node.data.ref[wbNodeSymbol] = node;\r\n      });\r\n      await this._tree.expandAll();\r\n      this._filterNodes();\r\n    }\r\n    catch (err) {\r\n      console.error(err);\r\n    }\r\n  }\r\n\r\n  private _getChildren(item: IDesignItem): treeNode {\r\n    const nd: treeNode = {\r\n      title: item.isRootItem ? '-root-' : item.nodeType === NodeType.Element ? item.name + \" \" + (item.id ? ('#' + item.id) : '') : '<small><small><small>#' + (item.nodeType === NodeType.TextNode ? 'text' : 'comment') + '&nbsp;</small></small></small> ' + DomConverter.normalizeContentValue(item.content),\r\n      ref: item\r\n    }\r\n    for (let i of item.children()) {\r\n      if (!i.isEmptyTextNode) {\r\n        if (!nd.children)\r\n          nd.children = []\r\n        nd.children.push(this._getChildren(i));\r\n      }\r\n    }\r\n    return nd;\r\n  }\r\n\r\n  private _highlight(activeElements: IDesignItem[]) {\r\n    let scrolled = false;\r\n    this._tree.runWithDeferredUpdate(() => {\r\n      this._tree.visit((node) => {\r\n        const flag = activeElements && activeElements.includes(node.data.ref);\r\n        if (flag != node.selected)\r\n          node.setSelected(flag);\r\n        if (flag != node.isActive())\r\n          node.setActive(flag);\r\n        if (!this.selectedFromTree) {\r\n          if (flag)\r\n            node.setFocus(true);\r\n          if (flag && !scrolled) {\r\n            scrolled = true;\r\n            requestAnimationFrame(() => {\r\n              node.scrollIntoView();\r\n            });\r\n          }\r\n        }\r\n      });\r\n    });\r\n  }\r\n\r\n  public collapseChildren(designItem: IDesignItem) {\r\n    const node = this._tree.findFirst(x => designItem == x.data.ref);\r\n    if (node) {\r\n      node.visit(x => {\r\n        x.setExpanded(false);\r\n      });\r\n    }\r\n  }\r\n\r\n  public expandChildren(designItem: IDesignItem) {\r\n    const node = this._tree.findFirst(x => designItem == x.data.ref);\r\n    if (node) {\r\n      node.visit(x => {\r\n        x.setExpanded(true);\r\n      });\r\n    }\r\n  }\r\n}\r\n\r\ncustomElements.define('node-projects-tree-view-extended', TreeViewExtended);"
  },
  {
    "path": "packages/web-component-designer-widgets-wunderbaum/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "packages/web-component-designer-zpl/.npmignore",
    "content": "src/\r\ntest/\r\nnode_modules/\r\ntsconfig.json\r\ntsconfig.tsbuildinfo\r\n!dist"
  },
  {
    "path": "packages/web-component-designer-zpl/README.md",
    "content": "# web-component-designer-zpl\r\n\r\n## NPM Package\r\n\r\nhttps://www.npmjs.com/package/@node-projects/web-component-designer-zpl\r\n\r\n     npm i @node-projects/web-component-designer-zpl\r\n\r\n## Description\r\n\r\nThis package contains widgets and services wich help to create a zpl designer.\r\n\r\n## Usage\r\n\r\nsee the zpl designer sample application"
  },
  {
    "path": "packages/web-component-designer-zpl/package.json",
    "content": "{\n  \"description\": \"web-component-designer addon: Widgets and Services for creating a ZPL Designer\",\n  \"name\": \"@node-projects/web-component-designer-zpl\",\n  \"version\": \"0.2.0\",\n  \"type\": \"module\",\n  \"main\": \"./dist/index.js\",\n  \"author\": \"jochen.kuehner@gmx.de\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"build\": \"tsc\",\n    \"link\": \"npm link\",\n    \"prepublishOnly\": \"npm run build\"\n  },\n  \"dependencies\": {\n    \"@node-projects/base-custom-webcomponent\": \">=0.27.8\",\n    \"@node-projects/web-component-designer\": \">=0.2.0\",\n    \"jsbarcode\": \"^3.11.6\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/node-projects/web-component-designer.git\"\n  }\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/extensions/ZplLayoutResizeExtensionProvider.ts",
    "content": "import { IExtensionManager, IDesignerCanvas, IDesignItem, ResizeExtensionProvider } from '@node-projects/web-component-designer';\nimport { ZplImage } from '../widgets/zpl-image.js';\nimport { ZplBarcode } from '../widgets/zpl-barcode.js';\nimport { ZplText } from '../widgets/zpl-text.js';\n\n\n\nexport class ZplLayoutResizeExtensionProvider extends ResizeExtensionProvider {\n    override shouldExtend(extensionManager: IExtensionManager, designerView: IDesignerCanvas, designItem: IDesignItem): boolean {\n        //return designItem.element.localName !== MfcConfigRoute.is;\n        switch (designItem.name) {\n            case ZplImage.is:\n            case ZplBarcode.is:\n            case ZplText.is:\n                return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/index.ts",
    "content": "export * from \"./widgets/zpl-barcode.js\";\r\nexport * from \"./widgets/zpl-graphic-box.js\";\r\nexport * from \"./widgets/zpl-graphic-circle.js\";\r\nexport * from \"./widgets/zpl-graphic-diagonal-line.js\";\r\nexport * from \"./widgets/zpl-image.js\";\r\nexport * from \"./widgets/zpl-text.js\";\r\n\r\nexport * from \"./monaco/ZplLanguage.js\";\r\n\r\nexport * from \"./extensions/ZplLayoutResizeExtensionProvider.js\";\r\n\r\nexport * from \"./services/ZplImageDrop.js\";\r\nexport * from \"./services/ZplLayoutCopyPasteService.js\";\r\nexport * from \"./services/ZplLayoutPlacementService.js\";\r\nexport * from \"./services/ZplParserService.js\";\r\n\r\nexport * from \"./jsBarcodeOptions.js\";\r\nexport * from \"./qr.js\";\r\nexport * from \"./setupZplServiceContainer.js\";\r\nexport * from \"./zplHelper.js\";"
  },
  {
    "path": "packages/web-component-designer-zpl/src/jsBarcodeOptions.ts",
    "content": "export type BarcodeOptions = {\n    format?: BarcodeFormat;\n    width?: number;\n    height?: number;\n    displayValue?: boolean;\n    text?: string;\n    fontOptions?: string;\n    font?: string;\n    textAlign?: string;\n    textPosition?: string;\n    textMargin?: number;\n    fontSize?: number;\n    background?: string;\n    lineColor?: string;\n    margin?: number;\n    marginTop?: number;\n    marginBottom?: number;\n    marginLeft?: number;\n    marginRight?: number;\n    flat?: boolean;\n    valid?: Function;\n}\n\nexport enum BarcodeFormat {\n    CODE128 = \"CODE128\",\n    // CODE128A = \"CODE128A\",\n    // CODE128B = \"CODE128B\",\n    // CODE123C = \"CODE128C\",\n    // CODE39 = \"CODE39\",\n    // EAN2 = \"EAN2\",\n    // EAN5 = \"EAN5\",\n    // EAN8 = \"EAN8\",\n    EAN13 = \"EAN13\",\n    // UPC = \"UPC\",\n    QR = 'QR',\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/monaco/ZplLanguage.ts",
    "content": "export function addZplLanguageToMonaco(monaco?: any) {\n    if (!monaco)\n        //@ts-ignore\n        monaco = window.monaco;\n    //@ts-ignore\n    monaco.languages.register({ id: \"zplLanguage\" });\n\n    //@ts-ignore\n    monaco.languages.setMonarchTokensProvider(\"zplLanguage\", {\n        tokenizer: {\n            root: [\n                // lookbehind seems not to work - [/(?<=\\^FD)[^\\^]*/, \"text\"],\n                [/\\^FX[^\\^]*/, \"comment\"],\n                [/\\^FD[^\\^]*/, \"text\"],\n                [/\\^../, \"command\"],\n            ],\n        },\n    });\n\n    //@ts-ignore\n    monaco.editor.defineTheme(\"zplTheme\", {\n        base: \"vs\",\n        inherit: false,\n        rules: [\n            { token: \"text\", foreground: \"999999\" },\n            { token: \"comment\", foreground: \"008000\" },\n            { token: \"command\", foreground: \"0000FF\", fontStyle: \"bold\" },\n        ],\n        colors: {\n            \"editor.foreground\": \"#000000\",\n        },\n    })\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/qr.ts",
    "content": "/* qr.js -- QR code generator in Javascript (revision 2011-01-19)\n * Written by Kang Seonghoon <public+qrjs@mearie.org>.\n *\n * This source code is in the public domain; if your jurisdiction does not\n * recognize the public domain the terms of Creative Commons CC0 license\n * apply. In the other words, you can always do what you want.\n */\n\n/* Quick overview: QR code composed of 2D array of modules (a rectangular\n * area that conveys one bit of information); some modules are fixed to help\n * the recognition of the code, and remaining data modules are further divided\n * into 8-bit code words which are augumented by Reed-Solomon error correcting\n * codes (ECC). There could be multiple ECCs, in the case the code is so large\n * that it is helpful to split the raw data into several chunks.\n *\n * The number of modules is determined by the code's \"version\", ranging from 1\n * (21x21) to 40 (177x177). How many ECC bits are used is determined by the\n * ECC level (L/M/Q/H). The number and size (and thus the order of generator\n * polynomial) of ECCs depend to the version and ECC level.\n */\n\n// per-version information (cf. JIS X 0510:2004 pp. 30--36, 71)\n//\n// [0]: the degree of generator polynomial by ECC levels\n// [1]: # of code blocks by ECC levels\n// [2]: left-top positions of alignment patterns\n//\n// the number in this table (in particular, [0]) does not exactly match with\n// the numbers in the specficiation. see augumenteccs below for the reason.\nvar VERSIONS = [\n    null,\n    [[10, 7, 17, 13], [1, 1, 1, 1], []],\n    [[16, 10, 28, 22], [1, 1, 1, 1], [4, 16]],\n    [[26, 15, 22, 18], [1, 1, 2, 2], [4, 20]],\n    [[18, 20, 16, 26], [2, 1, 4, 2], [4, 24]],\n    [[24, 26, 22, 18], [2, 1, 4, 4], [4, 28]],\n    [[16, 18, 28, 24], [4, 2, 4, 4], [4, 32]],\n    [[18, 20, 26, 18], [4, 2, 5, 6], [4, 20, 36]],\n    [[22, 24, 26, 22], [4, 2, 6, 6], [4, 22, 40]],\n    [[22, 30, 24, 20], [5, 2, 8, 8], [4, 24, 44]],\n    [[26, 18, 28, 24], [5, 4, 8, 8], [4, 26, 48]],\n    [[30, 20, 24, 28], [5, 4, 11, 8], [4, 28, 52]],\n    [[22, 24, 28, 26], [8, 4, 11, 10], [4, 30, 56]],\n    [[22, 26, 22, 24], [9, 4, 16, 12], [4, 32, 60]],\n    [[24, 30, 24, 20], [9, 4, 16, 16], [4, 24, 44, 64]],\n    [[24, 22, 24, 30], [10, 6, 18, 12], [4, 24, 46, 68]],\n    [[28, 24, 30, 24], [10, 6, 16, 17], [4, 24, 48, 72]],\n    [[28, 28, 28, 28], [11, 6, 19, 16], [4, 28, 52, 76]],\n    [[26, 30, 28, 28], [13, 6, 21, 18], [4, 28, 54, 80]],\n    [[26, 28, 26, 26], [14, 7, 25, 21], [4, 28, 56, 84]],\n    [[26, 28, 28, 30], [16, 8, 25, 20], [4, 32, 60, 88]],\n    [[26, 28, 30, 28], [17, 8, 25, 23], [4, 26, 48, 70, 92]],\n    [[28, 28, 24, 30], [17, 9, 34, 23], [4, 24, 48, 72, 96]],\n    [[28, 30, 30, 30], [18, 9, 30, 25], [4, 28, 52, 76, 100]],\n    [[28, 30, 30, 30], [20, 10, 32, 27], [4, 26, 52, 78, 104]],\n    [[28, 26, 30, 30], [21, 12, 35, 29], [4, 30, 56, 82, 108]],\n    [[28, 28, 30, 28], [23, 12, 37, 34], [4, 28, 56, 84, 112]],\n    [[28, 30, 30, 30], [25, 12, 40, 34], [4, 32, 60, 88, 116]],\n    [[28, 30, 30, 30], [26, 13, 42, 35], [4, 24, 48, 72, 96, 120]],\n    [[28, 30, 30, 30], [28, 14, 45, 38], [4, 28, 52, 76, 100, 124]],\n    [[28, 30, 30, 30], [29, 15, 48, 40], [4, 24, 50, 76, 102, 128]],\n    [[28, 30, 30, 30], [31, 16, 51, 43], [4, 28, 54, 80, 106, 132]],\n    [[28, 30, 30, 30], [33, 17, 54, 45], [4, 32, 58, 84, 110, 136]],\n    [[28, 30, 30, 30], [35, 18, 57, 48], [4, 28, 56, 84, 112, 140]],\n    [[28, 30, 30, 30], [37, 19, 60, 51], [4, 32, 60, 88, 116, 144]],\n    [[28, 30, 30, 30], [38, 19, 63, 53], [4, 28, 52, 76, 100, 124, 148]],\n    [[28, 30, 30, 30], [40, 20, 66, 56], [4, 22, 48, 74, 100, 126, 152]],\n    [[28, 30, 30, 30], [43, 21, 70, 59], [4, 26, 52, 78, 104, 130, 156]],\n    [[28, 30, 30, 30], [45, 22, 74, 62], [4, 30, 56, 82, 108, 134, 160]],\n    [[28, 30, 30, 30], [47, 24, 77, 65], [4, 24, 52, 80, 108, 136, 164]],\n    [[28, 30, 30, 30], [49, 25, 81, 68], [4, 28, 56, 84, 112, 140, 168]]];\n\n// mode constants (cf. Table 2 in JIS X 0510:2004 p. 16)\nvar MODE_TERMINATOR = 0;\nvar MODE_NUMERIC = 1, MODE_ALPHANUMERIC = 2, MODE_OCTET = 4, MODE_KANJI = 8;\n\n// validation regexps\nvar NUMERIC_REGEXP = /^\\d*$/;\nvar ALPHANUMERIC_REGEXP = /^[A-Za-z0-9 $%*+\\-./:]*$/;\nvar ALPHANUMERIC_OUT_REGEXP = /^[A-Z0-9 $%*+\\-./:]*$/;\n\n// ECC levels (cf. Table 22 in JIS X 0510:2004 p. 45)\nvar ECCLEVEL_L = 1, ECCLEVEL_M = 0, ECCLEVEL_Q = 3, ECCLEVEL_H = 2;\n\n// GF(2^8)-to-integer mapping with a reducing polynomial x^8+x^4+x^3+x^2+1\n// invariant: GF256_MAP[GF256_INVMAP[i]] == i for all i in [1,256)\nvar GF256_MAP = [], GF256_INVMAP = [-1];\nfor (var i = 0, v = 1; i < 255; ++i) {\n    GF256_MAP.push(v);\n    GF256_INVMAP[v] = i;\n    v = (v * 2) ^ (v >= 128 ? 0x11d : 0);\n}\n\n// generator polynomials up to degree 30\n// (should match with polynomials in JIS X 0510:2004 Appendix A)\n//\n// generator polynomial of degree K is product of (x-\\alpha^0), (x-\\alpha^1),\n// ..., (x-\\alpha^(K-1)). by convention, we omit the K-th coefficient (always 1)\n// from the result; also other coefficients are written in terms of the exponent\n// to \\alpha to avoid the redundant calculation. (see also calculateecc below.)\nvar GF256_GENPOLY = [[]];\nfor (var i = 0; i < 30; ++i) {\n    var prevpoly = GF256_GENPOLY[i], poly = [];\n    for (var j = 0; j <= i; ++j) {\n        var a = (j < i ? GF256_MAP[prevpoly[j]] : 0);\n        var b = GF256_MAP[(i + (prevpoly[j - 1] || 0)) % 255];\n        poly.push(GF256_INVMAP[a ^ b]);\n    }\n    GF256_GENPOLY.push(poly);\n}\n\n// alphanumeric character mapping (cf. Table 5 in JIS X 0510:2004 p. 19)\nvar ALPHANUMERIC_MAP = {};\nfor (var i = 0; i < 45; ++i) {\n    ALPHANUMERIC_MAP['0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.charAt(i)] = i;\n}\n\n// mask functions in terms of row # and column #\n// (cf. Table 20 in JIS X 0510:2004 p. 42)\nvar MASKFUNCS = [\n    function (i, j) { return (i + j) % 2 == 0; },\n    function (i, j) { return i % 2 == 0; },\n    function (i, j) { return j % 3 == 0; },\n    function (i, j) { return (i + j) % 3 == 0; },\n    function (i, j) { return (((i / 2) | 0) + ((j / 3) | 0)) % 2 == 0; },\n    function (i, j) { return (i * j) % 2 + (i * j) % 3 == 0; },\n    function (i, j) { return ((i * j) % 2 + (i * j) % 3) % 2 == 0; },\n    function (i, j) { return ((i + j) % 2 + (i * j) % 3) % 2 == 0; }];\n\n// returns true when the version information has to be embeded.\nvar needsverinfo = function (ver) { return ver > 6; };\n\n// returns the size of entire QR code for given version.\nvar getsizebyver = function (ver) { return 4 * ver + 17; };\n\n// returns the number of bits available for code words in this version.\nvar nfullbits = function (ver) {\n    /*\n     * |<--------------- n --------------->|\n     * |        |<----- n-17 ---->|        |\n     * +-------+                ///+-------+ ----\n     * |       |                ///|       |    ^\n     * |  9x9  |       @@@@@    ///|  9x8  |    |\n     * |       | # # # @5x5@ # # # |       |    |\n     * +-------+       @@@@@       +-------+    |\n     *       #                               ---|\n     *                                        ^ |\n     *       #                                |\n     *     @@@@@       @@@@@       @@@@@      | n\n     *     @5x5@       @5x5@       @5x5@   n-17\n     *     @@@@@       @@@@@       @@@@@      | |\n     *       #                                | |\n     * //////                                 v |\n     * //////#                               ---|\n     * +-------+       @@@@@       @@@@@        |\n     * |       |       @5x5@       @5x5@        |\n     * |  8x9  |       @@@@@       @@@@@        |\n     * |       |                                v\n     * +-------+                             ----\n     *\n     * when the entire code has n^2 modules and there are m^2-3 alignment\n     * patterns, we have:\n     * - 225 (= 9x9 + 9x8 + 8x9) modules for finder patterns and\n     *   format information;\n     * - 2n-34 (= 2(n-17)) modules for timing patterns;\n     * - 36 (= 3x6 + 6x3) modules for version information, if any;\n     * - 25m^2-75 (= (m^2-3)(5x5)) modules for alignment patterns\n     *   if any, but 10m-20 (= 2(m-2)x5) of them overlaps with\n     *   timing patterns.\n     */\n    var v = VERSIONS[ver];\n    var nbits = 16 * ver * ver + 128 * ver + 64; // finder, timing and format info.\n    if (needsverinfo(ver)) nbits -= 36; // version information\n    if (v[2].length) { // alignment patterns\n        nbits -= 25 * v[2].length * v[2].length - 10 * v[2].length - 55;\n    }\n    return nbits;\n};\n\n// returns the number of bits available for data portions (i.e. excludes ECC\n// bits but includes mode and length bits) in this version and ECC level.\nvar ndatabits = function (ver, ecclevel) {\n    var nbits = nfullbits(ver) & ~7; // no sub-octet code words\n    var v = VERSIONS[ver];\n    nbits -= 8 * v[0][ecclevel] * v[1][ecclevel]; // ecc bits\n    return nbits;\n}\n\n// returns the number of bits required for the length of data.\n// (cf. Table 3 in JIS X 0510:2004 p. 16)\nvar ndatalenbits = function (ver, mode) {\n    switch (mode) {\n        case MODE_NUMERIC: return (ver < 10 ? 10 : ver < 27 ? 12 : 14);\n        case MODE_ALPHANUMERIC: return (ver < 10 ? 9 : ver < 27 ? 11 : 13);\n        case MODE_OCTET: return (ver < 10 ? 8 : 16);\n        case MODE_KANJI: return (ver < 10 ? 8 : ver < 27 ? 10 : 12);\n    }\n    return undefined;\n};\n\n// returns the maximum length of data possible in given configuration.\nvar getmaxdatalen = function (ver, mode, ecclevel) {\n    var nbits = ndatabits(ver, ecclevel) - 4 - ndatalenbits(ver, mode); // 4 for mode bits\n    switch (mode) {\n        case MODE_NUMERIC:\n            return ((nbits / 10) | 0) * 3 + (nbits % 10 < 4 ? 0 : nbits % 10 < 7 ? 1 : 2);\n        case MODE_ALPHANUMERIC:\n            return ((nbits / 11) | 0) * 2 + (nbits % 11 < 6 ? 0 : 1);\n        case MODE_OCTET:\n            return (nbits / 8) | 0;\n        case MODE_KANJI:\n            return (nbits / 13) | 0;\n    }\n    return undefined;\n};\n\n// checks if the given data can be encoded in given mode, and returns\n// the converted data for the further processing if possible. otherwise\n// returns null.\n//\n// this function does not check the length of data; it is a duty of\n// encode function below (as it depends on the version and ECC level too).\nvar validatedata = function (mode, data) {\n    switch (mode) {\n        case MODE_NUMERIC:\n            if (!data.match(NUMERIC_REGEXP)) return null;\n            return data;\n\n        case MODE_ALPHANUMERIC:\n            if (!data.match(ALPHANUMERIC_REGEXP)) return null;\n            return data.toUpperCase();\n\n        case MODE_OCTET:\n            if (typeof data === 'string') { // encode as utf-8 string\n                var newdata = [];\n                for (var i = 0; i < data.length; ++i) {\n                    var ch = data.charCodeAt(i);\n                    if (ch < 0x80) {\n                        newdata.push(ch);\n                    } else if (ch < 0x800) {\n                        newdata.push(0xc0 | (ch >> 6),\n                            0x80 | (ch & 0x3f));\n                    } else if (ch < 0x10000) {\n                        newdata.push(0xe0 | (ch >> 12),\n                            0x80 | ((ch >> 6) & 0x3f),\n                            0x80 | (ch & 0x3f));\n                    } else {\n                        newdata.push(0xf0 | (ch >> 18),\n                            0x80 | ((ch >> 12) & 0x3f),\n                            0x80 | ((ch >> 6) & 0x3f),\n                            0x80 | (ch & 0x3f));\n                    }\n                }\n                return newdata;\n            } else {\n                return data;\n            }\n    }\n};\n\n// returns the code words (sans ECC bits) for given data and configurations.\n// requires data to be preprocessed by validatedata. no length check is\n// performed, and everything has to be checked before calling this function.\nvar encode = function (ver, mode, data, maxbuflen) {\n    var buf = [];\n    var bits = 0, remaining = 8;\n    var datalen = data.length;\n\n    // this function is intentionally no-op when n=0.\n    var pack = function (x, n) {\n        if (n >= remaining) {\n            buf.push(bits | (x >> (n -= remaining)));\n            while (n >= 8) buf.push((x >> (n -= 8)) & 255);\n            bits = 0;\n            remaining = 8;\n        }\n        if (n > 0) bits |= (x & ((1 << n) - 1)) << (remaining -= n);\n    };\n\n    var nlenbits = ndatalenbits(ver, mode);\n    pack(mode, 4);\n    pack(datalen, nlenbits);\n\n    switch (mode) {\n        case MODE_NUMERIC:\n            for (var i = 2; i < datalen; i += 3) {\n                pack(parseInt(data.substring(i - 2, i + 1), 10), 10);\n            }\n            pack(parseInt(data.substring(i - 2), 10), [0, 4, 7][datalen % 3]);\n            break;\n\n        case MODE_ALPHANUMERIC:\n            for (var i = 1; i < datalen; i += 2) {\n                pack(ALPHANUMERIC_MAP[data.charAt(i - 1)] * 45 +\n                    ALPHANUMERIC_MAP[data.charAt(i)], 11);\n            }\n            if (datalen % 2 == 1) {\n                pack(ALPHANUMERIC_MAP[data.charAt(i - 1)], 6);\n            }\n            break;\n\n        case MODE_OCTET:\n            for (var i = 0; i < datalen; ++i) {\n                pack(data[i], 8);\n            }\n            break;\n    };\n\n    // final bits. it is possible that adding terminator causes the buffer\n    // to overflow, but then the buffer truncated to the maximum size will\n    // be valid as the truncated terminator mode bits and padding is\n    // identical in appearance (cf. JIS X 0510:2004 sec 8.4.8).\n    pack(MODE_TERMINATOR, 4);\n    if (remaining < 8) buf.push(bits);\n\n    // the padding to fill up the remaining space. we should not add any\n    // words when the overflow already occurred.\n    while (buf.length + 1 < maxbuflen) buf.push(0xec, 0x11);\n    if (buf.length < maxbuflen) buf.push(0xec);\n    return buf;\n};\n\n// calculates ECC code words for given code words and generator polynomial.\n//\n// this is quite similar to CRC calculation as both Reed-Solomon and CRC use\n// the certain kind of cyclic codes, which is effectively the division of\n// zero-augumented polynomial by the generator polynomial. the only difference\n// is that Reed-Solomon uses GF(2^8), instead of CRC's GF(2), and Reed-Solomon\n// uses the different generator polynomial than CRC's.\nvar calculateecc = function (poly, genpoly) {\n    var modulus = poly.slice(0);\n    var polylen = poly.length, genpolylen = genpoly.length;\n    for (var i = 0; i < genpolylen; ++i) modulus.push(0);\n    for (var i = 0; i < polylen;) {\n        var quotient = GF256_INVMAP[modulus[i++]];\n        if (quotient >= 0) {\n            for (var j = 0; j < genpolylen; ++j) {\n                modulus[i + j] ^= GF256_MAP[(quotient + genpoly[j]) % 255];\n            }\n        }\n    }\n    return modulus.slice(polylen);\n};\n\n// auguments ECC code words to given code words. the resulting words are\n// ready to be encoded in the matrix.\n//\n// the much of actual augumenting procedure follows JIS X 0510:2004 sec 8.7.\n// the code is simplified using the fact that the size of each code & ECC\n// blocks is almost same; for example, when we have 4 blocks and 46 data words\n// the number of code words in those blocks are 11, 11, 12, 12 respectively.\nvar augumenteccs = function (poly, nblocks, genpoly) {\n    var subsizes = [];\n    var subsize = (poly.length / nblocks) | 0, subsize0 = 0;\n    var pivot = nblocks - poly.length % nblocks;\n    for (var i = 0; i < pivot; ++i) {\n        subsizes.push(subsize0);\n        subsize0 += subsize;\n    }\n    for (var i = pivot; i < nblocks; ++i) {\n        subsizes.push(subsize0);\n        subsize0 += subsize + 1;\n    }\n    subsizes.push(subsize0);\n\n    var eccs = [];\n    for (var i = 0; i < nblocks; ++i) {\n        eccs.push(calculateecc(poly.slice(subsizes[i], subsizes[i + 1]), genpoly));\n    }\n\n    var result = [];\n    var nitemsperblock = (poly.length / nblocks) | 0;\n    for (var i = 0; i < nitemsperblock; ++i) {\n        for (var j = 0; j < nblocks; ++j) {\n            result.push(poly[subsizes[j] + i]);\n        }\n    }\n    for (var j = pivot; j < nblocks; ++j) {\n        result.push(poly[subsizes[j + 1] - 1]);\n    }\n    for (var i = 0; i < genpoly.length; ++i) {\n        for (var j = 0; j < nblocks; ++j) {\n            result.push(eccs[j][i]);\n        }\n    }\n    return result;\n};\n\n// auguments BCH(p+q,q) code to the polynomial over GF(2), given the proper\n// genpoly. the both input and output are in binary numbers, and unlike\n// calculateecc genpoly should include the 1 bit for the highest degree.\n//\n// actual polynomials used for this procedure are as follows:\n// - p=10, q=5, genpoly=x^10+x^8+x^5+x^4+x^2+x+1 (JIS X 0510:2004 Appendix C)\n// - p=18, q=6, genpoly=x^12+x^11+x^10+x^9+x^8+x^5+x^2+1 (ibid. Appendix D)\nvar augumentbch = function (poly, p, genpoly, q) {\n    var modulus = poly << q;\n    for (var i = p - 1; i >= 0; --i) {\n        if ((modulus >> (q + i)) & 1) modulus ^= genpoly << i;\n    }\n    return (poly << q) | modulus;\n};\n\n// creates the base matrix for given version. it returns two matrices, one of\n// them is the actual one and the another represents the \"reserved\" portion\n// (e.g. finder and timing patterns) of the matrix.\n//\n// some entries in the matrix may be undefined, rather than 0 or 1. this is\n// intentional (no initialization needed!), and putdata below will fill\n// the remaining ones.\nvar makebasematrix = function (ver) {\n    var v = VERSIONS[ver], n = getsizebyver(ver);\n    var matrix = [], reserved = [];\n    for (var i = 0; i < n; ++i) {\n        matrix.push([]);\n        reserved.push([]);\n    }\n\n    var blit = function (y, x, h, w, bits) {\n        for (var i = 0; i < h; ++i) {\n            for (var j = 0; j < w; ++j) {\n                matrix[y + i][x + j] = (bits[i] >> j) & 1;\n                reserved[y + i][x + j] = 1;\n            }\n        }\n    };\n\n    // finder patterns and a part of timing patterns\n    // will also mark the format information area (not yet written) as reserved.\n    blit(0, 0, 9, 9, [0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x17f, 0x00, 0x40]);\n    blit(n - 8, 0, 8, 9, [0x100, 0x7f, 0x41, 0x5d, 0x5d, 0x5d, 0x41, 0x7f]);\n    blit(0, n - 8, 9, 8, [0xfe, 0x82, 0xba, 0xba, 0xba, 0x82, 0xfe, 0x00, 0x00]);\n\n    // the rest of timing patterns\n    for (var i = 9; i < n - 8; ++i) {\n        matrix[6][i] = matrix[i][6] = ~i & 1;\n        reserved[6][i] = reserved[i][6] = 1;\n    }\n\n    // alignment patterns\n    var aligns = v[2], m = aligns.length;\n    for (var i = 0; i < m; ++i) {\n        var minj = (i == 0 || i == m - 1 ? 1 : 0), maxj = (i == 0 ? m - 1 : m);\n        for (var j = minj; j < maxj; ++j) {\n            blit(aligns[i], aligns[j], 5, 5, [0x1f, 0x11, 0x15, 0x11, 0x1f]);\n        }\n    }\n\n    // version information\n    if (needsverinfo(ver)) {\n        var code = augumentbch(ver, 6, 0x1f25, 12);\n        var k = 0;\n        for (var i = 0; i < 6; ++i) {\n            for (var j = 0; j < 3; ++j) {\n                matrix[i][(n - 11) + j] = matrix[(n - 11) + j][i] = (code >> k++) & 1;\n                reserved[i][(n - 11) + j] = reserved[(n - 11) + j][i] = 1;\n            }\n        }\n    }\n\n    return { matrix: matrix, reserved: reserved };\n};\n\n// fills the data portion (i.e. unmarked in reserved) of the matrix with given\n// code words. the size of code words should be no more than available bits,\n// and remaining bits are padded to 0 (cf. JIS X 0510:2004 sec 8.7.3).\nvar putdata = function (matrix, reserved, buf) {\n    var n = matrix.length;\n    var k = 0, dir = -1;\n    for (var i = n - 1; i >= 0; i -= 2) {\n        if (i == 6) --i; // skip the entire timing pattern column\n        var jj = (dir < 0 ? n - 1 : 0);\n        for (var j = 0; j < n; ++j) {\n            for (var ii = i; ii > i - 2; --ii) {\n                if (!reserved[jj][ii]) {\n                    // may overflow, but (undefined >> x)\n                    // is 0 so it will auto-pad to zero.\n                    matrix[jj][ii] = (buf[k >> 3] >> (~k & 7)) & 1;\n                    ++k;\n                }\n            }\n            jj += dir;\n        }\n        dir = -dir;\n    }\n    return matrix;\n};\n\n// XOR-masks the data portion of the matrix. repeating the call with the same\n// arguments will revert the prior call (convenient in the matrix evaluation).\nvar maskdata = function (matrix, reserved, mask) {\n    var maskf = MASKFUNCS[mask];\n    var n = matrix.length;\n    for (var i = 0; i < n; ++i) {\n        for (var j = 0; j < n; ++j) {\n            //@ts-ignore\n            if (!reserved[i][j]) matrix[i][j] ^= maskf(i, j);\n        }\n    }\n    return matrix;\n}\n\n// puts the format information.\nvar putformatinfo = function (matrix, reserved, ecclevel, mask) {\n    var n = matrix.length;\n    var code = augumentbch((ecclevel << 3) | mask, 5, 0x537, 10) ^ 0x5412;\n    for (var i = 0; i < 15; ++i) {\n        var r = [0, 1, 2, 3, 4, 5, 7, 8, n - 7, n - 6, n - 5, n - 4, n - 3, n - 2, n - 1][i];\n        var c = [n - 1, n - 2, n - 3, n - 4, n - 5, n - 6, n - 7, n - 8, 7, 5, 4, 3, 2, 1, 0][i];\n        matrix[r][8] = matrix[8][c] = (code >> i) & 1;\n        // we don't have to mark those bits reserved; always done\n        // in makebasematrix above.\n    }\n    return matrix;\n};\n\n// evaluates the resulting matrix and returns the score (lower is better).\n// (cf. JIS X 0510:2004 sec 8.8.2)\n//\n// the evaluation procedure tries to avoid the problematic patterns naturally\n// occuring from the original matrix. for example, it penaltizes the patterns\n// which just look like the finder pattern which will confuse the decoder.\n// we choose the mask which results in the lowest score among 8 possible ones.\n//\n// note: zxing seems to use the same procedure and in many cases its choice\n// agrees to ours, but sometimes it does not. practically it doesn't matter.\nvar evaluatematrix = function (matrix) {\n    // N1+(k-5) points for each consecutive row of k same-colored modules,\n    // where k >= 5. no overlapping row counts.\n    var PENALTY_CONSECUTIVE = 3;\n    // N2 points for each 2x2 block of same-colored modules.\n    // overlapping block does count.\n    var PENALTY_TWOBYTWO = 3;\n    // N3 points for each pattern with >4W:1B:1W:3B:1W:1B or\n    // 1B:1W:3B:1W:1B:>4W, or their multiples (e.g. highly unlikely,\n    // but 13W:3B:3W:9B:3W:3B counts).\n    var PENALTY_FINDERLIKE = 40;\n    // N4*k points for every (5*k)% deviation from 50% black density.\n    // i.e. k=1 for 55~60% and 40~45%, k=2 for 60~65% and 35~40%, etc.\n    var PENALTY_DENSITY = 10;\n\n    var evaluategroup = function (groups) { // assumes [W,B,W,B,W,...,B,W]\n        var score = 0;\n        for (var i = 0; i < groups.length; ++i) {\n            if (groups[i] >= 5) score += PENALTY_CONSECUTIVE + (groups[i] - 5);\n        }\n        for (var i = 5; i < groups.length; i += 2) {\n            var p = groups[i];\n            if (groups[i - 1] == p && groups[i - 2] == 3 * p && groups[i - 3] == p &&\n                groups[i - 4] == p && (groups[i - 5] >= 4 * p || groups[i + 1] >= 4 * p)) {\n                // this part differs from zxing...\n                score += PENALTY_FINDERLIKE;\n            }\n        }\n        return score;\n    };\n\n    var n = matrix.length;\n    var score = 0, nblacks = 0;\n    for (var i = 0; i < n; ++i) {\n        var row = matrix[i];\n        var groups;\n\n        // evaluate the current row\n        groups = [0]; // the first empty group of white\n        for (var j = 0; j < n;) {\n            var k;\n            for (k = 0; j < n && row[j]; ++k) ++j;\n            groups.push(k);\n            for (k = 0; j < n && !row[j]; ++k) ++j;\n            groups.push(k);\n        }\n        score += evaluategroup(groups);\n\n        // evaluate the current column\n        groups = [0];\n        for (var j = 0; j < n;) {\n            var k;\n            for (k = 0; j < n && matrix[j][i]; ++k) ++j;\n            groups.push(k);\n            for (k = 0; j < n && !matrix[j][i]; ++k) ++j;\n            groups.push(k);\n        }\n        score += evaluategroup(groups);\n\n        // check the 2x2 box and calculate the density\n        var nextrow = matrix[i + 1] || [];\n        nblacks += row[0];\n        for (var j = 1; j < n; ++j) {\n            var p = row[j];\n            nblacks += p;\n            // at least comparison with next row should be strict...\n            if (row[j - 1] == p && nextrow[j] === p && nextrow[j - 1] === p) {\n                score += PENALTY_TWOBYTWO;\n            }\n        }\n    }\n\n    score += PENALTY_DENSITY * ((Math.abs(nblacks / n / n - 0.5) / 0.05) | 0);\n    return score;\n};\n\n// returns the fully encoded QR code matrix which contains given data.\n// it also chooses the best mask automatically when mask is -1.\nvar generate = function (data, ver, mode, ecclevel, mask) {\n    var v = VERSIONS[ver];\n    var buf = encode(ver, mode, data, ndatabits(ver, ecclevel) >> 3);\n    buf = augumenteccs(buf, v[1][ecclevel], GF256_GENPOLY[v[0][ecclevel]]);\n\n    var result = makebasematrix(ver);\n    var matrix = result.matrix, reserved = result.reserved;\n    putdata(matrix, reserved, buf);\n\n    if (mask < 0) {\n        // find the best mask\n        maskdata(matrix, reserved, 0);\n        putformatinfo(matrix, reserved, ecclevel, 0);\n        var bestmask = 0, bestscore = evaluatematrix(matrix);\n        maskdata(matrix, reserved, 0);\n        for (mask = 1; mask < 8; ++mask) {\n            maskdata(matrix, reserved, mask);\n            putformatinfo(matrix, reserved, ecclevel, mask);\n            var score = evaluatematrix(matrix);\n            if (bestscore > score) {\n                bestscore = score;\n                bestmask = mask;\n            }\n            maskdata(matrix, reserved, mask);\n        }\n        mask = bestmask;\n    }\n\n    maskdata(matrix, reserved, mask);\n    putformatinfo(matrix, reserved, ecclevel, mask);\n    return matrix;\n};\n\n// the public interface is trivial; the options available are as follows:\n//\n// - version: an integer in [1,40]. when omitted (or -1) the smallest possible\n//   version is chosen.\n// - mode: one of 'numeric', 'alphanumeric', 'octet'. when omitted the smallest\n//   possible mode is chosen.\n// - ecclevel: one of 'L', 'M', 'Q', 'H'. defaults to 'L'.\n// - mask: an integer in [0,7]. when omitted (or -1) the best mask is chosen.\n//\n// for generate{HTML,PNG}:\n//\n// - modulesize: a number. this is a size of each modules in pixels, and\n//   defaults to 5px.\n// - margin: a number. this is a size of margin in *modules*, and defaults to\n//   4 (white modules). the specficiation mandates the margin no less than 4\n//   modules, so it is better not to alter this value unless you know what\n//   you're doing.\nvar QRCode = {\n    'generate': function (data, options) {\n        var MODES = {\n            'numeric': MODE_NUMERIC, 'alphanumeric': MODE_ALPHANUMERIC,\n            'octet': MODE_OCTET\n        };\n        var ECCLEVELS = {\n            'L': ECCLEVEL_L, 'M': ECCLEVEL_M, 'Q': ECCLEVEL_Q,\n            'H': ECCLEVEL_H\n        };\n\n        options = options || {};\n        var ver = options.version || -1;\n        var ecclevel = ECCLEVELS[(options.ecclevel || 'L').toUpperCase()];\n        var mode = options.mode ? MODES[options.mode.toLowerCase()] : -1;\n        var mask = 'mask' in options ? options.mask : -1;\n\n        if (mode < 0) {\n            if (typeof data === 'string') {\n                if (data.match(NUMERIC_REGEXP)) {\n                    mode = MODE_NUMERIC;\n                } else if (data.match(ALPHANUMERIC_OUT_REGEXP)) {\n                    // while encode supports case-insensitive\n                    // encoding, we restrict the data to be\n                    // uppercased when auto-selecting the mode.\n                    mode = MODE_ALPHANUMERIC;\n                } else {\n                    mode = MODE_OCTET;\n                }\n            } else {\n                mode = MODE_OCTET;\n            }\n        } else if (!(mode == MODE_NUMERIC || mode == MODE_ALPHANUMERIC ||\n            mode == MODE_OCTET)) {\n            throw 'invalid or unsupported mode';\n        }\n\n        data = validatedata(mode, data);\n        if (data === null) throw 'invalid data format';\n\n        if (ecclevel < 0 || ecclevel > 3) throw 'invalid ECC level';\n\n        if (ver < 0) {\n            for (ver = 1; ver <= 40; ++ver) {\n                if (data.length <= getmaxdatalen(ver, mode, ecclevel)) break;\n            }\n            if (ver > 40) throw 'too large data';\n        } else if (ver < 1 || ver > 40) {\n            throw 'invalid version';\n        }\n\n        if (mask != -1 && (mask < 0 || mask > 8)) throw 'invalid mask';\n\n        return generate(data, ver, mode, ecclevel, mask);\n    },\n\n    'generateHTML': function (data, options) {\n        options = options || {};\n        var matrix = QRCode['generate'](data, options);\n        var modsize = Math.max(options.modulesize || 5, 0.5);\n        var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);\n\n        var e = document.createElement('div');\n        var n = matrix.length;\n        var html = ['<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"border:' +\n            modsize * margin + 'px solid #fff;background:#fff\">'];\n        for (var i = 0; i < n; ++i) {\n            html.push('<tr>');\n            for (var j = 0; j < n; ++j) {\n                html.push('<td style=\"width:' + modsize + 'px;height:' + modsize + 'px' +\n                    (matrix[i][j] ? ';background:#000' : '') + '\" ' +\n                    'part=\"' + (matrix[i][j] ? 'module-fg' : 'module-bg') + '\" ' + '></td>');\n            }\n            html.push('</tr>');\n        }\n        e.className = 'qrcode';\n        e.innerHTML = html.join('') + '</table>';\n        return e;\n    },\n\n    'generateSVG': function (data, options: { background?: string, modulesize?: number, margin?: number }) {\n        options = options || {};\n        var matrix = QRCode['generate'](data, options);\n        var n = matrix.length;\n        var modsize = Math.max(options.modulesize || 5, 0.5);\n        var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);\n        var size = modsize * (n + 2 * margin);\n\n        var common = ' class= \"fg\"' + ' width=\"' + modsize + '\" height=\"' + modsize + '\"/>';\n\n        var e = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n        e.setAttribute('viewBox', '0 0 ' + size + ' ' + size);\n        e.setAttribute('style', 'shape-rendering:crispEdges');\n        if (options.modulesize) {\n            e.setAttribute('width', size.toString());\n            e.setAttribute('height', size.toString());\n        }\n\n        var svg = [\n            '<style scoped>.bg{fill:' + (options?.background ?? '#FFF') + '}.fg{fill:#000}</style>',\n            '<rect class=\"bg\" x=\"0\" y=\"0\"',\n            'width=\"' + size + '\" height=\"' + size + '\"/>',\n        ];\n\n        var yo = margin * modsize;\n        for (var y = 0; y < n; ++y) {\n            var xo = margin * modsize;\n            for (var x = 0; x < n; ++x) {\n                if (matrix[y][x])\n                    svg.push('<rect x=\"' + xo + '\" y=\"' + yo + '\"', common);\n                xo += modsize;\n            }\n            yo += modsize;\n        }\n        e.innerHTML = svg.join('');\n        return e;\n    },\n\n    'generatePNG': function (data, options) {\n        options = options || {};\n        var matrix = QRCode['generate'](data, options);\n        var modsize = Math.max(options.modulesize || 5, 0.5);\n        var margin = Math.max(options.margin !== null ? options.margin : 4, 0.0);\n        var n = matrix.length;\n        var size = modsize * (n + 2 * margin);\n\n        var canvas = document.createElement('canvas'), context;\n        canvas.width = canvas.height = size;\n        context = canvas.getContext('2d');\n        if (!context) throw 'canvas support is needed for PNG output';\n\n        context.fillStyle = '#fff';\n        context.fillRect(0, 0, size, size);\n        context.fillStyle = '#000';\n        for (var i = 0; i < n; ++i) {\n            for (var j = 0; j < n; ++j) {\n                if (matrix[i][j]) {\n                    context.fillRect(modsize * (margin + j),\n                        modsize * (margin + i),\n                        modsize, modsize);\n                }\n            }\n        }\n        //context.fillText('evaluation: ' + evaluatematrix(matrix), 10, 10);\n        return canvas.toDataURL();\n    }\n};\n\nexport default QRCode;"
  },
  {
    "path": "packages/web-component-designer-zpl/src/services/ZplImageDrop.ts",
    "content": "import { DesignItem, IDesignerCanvas, IExternalDragDropService, InsertAction } from \"@node-projects/web-component-designer\";\nimport { ZplImage } from \"../widgets/zpl-image.js\";\n\nexport class ZplImageDrop implements IExternalDragDropService {\n\n    public dragOver(designerCanvas: IDesignerCanvas, event: DragEvent): 'none' | 'copy' | 'link' | 'move' {\n        if (event.dataTransfer.items[0].type.startsWith('image/'))\n            return 'copy';\n        return 'none';\n    }\n\n    drop(designerCanvas: IDesignerCanvas, event: DragEvent) {\n        if (event.dataTransfer.files[0].type.startsWith('image/')) {\n            let name = event.dataTransfer.files[0].name;\n            let reader = new FileReader();\n            reader.onloadend = () => {\n                const img = document.createElement('img');\n                img.src = <string>reader.result;\n                img.onload = () => {\n                    let zplImage = new ZplImage();\n                    let zpl = this._convertImage(img, name);\n                    const targetRect = (<HTMLElement>event.target).getBoundingClientRect();\n                    let x = event.offsetX + targetRect.left - designerCanvas.containerBoundingRect.x + 'px'\n                    let y = event.offsetY + targetRect.top - designerCanvas.containerBoundingRect.y + 'px';\n                    zplImage.style.position = \"absolute\";\n                    zplImage.style.left = x;\n                    zplImage.style.top = y;\n                    zplImage.setAttribute(\"total-bytes\", zpl.totalBytes.toString());\n                    zplImage.setAttribute(\"bytes-per-row\", zpl.bytesPerRow.toString());\n                    zplImage.setAttribute(\"image-name\", zpl.name);\n                    zplImage.setAttribute(\"hex-image\", zpl.hexData);\n                    zplImage.setAttribute(\"scale-x\", \"1\");\n                    zplImage.setAttribute(\"scale-y\", \"1\");\n                    const di = DesignItem.createDesignItemFromInstance(zplImage, designerCanvas.serviceContainer, designerCanvas.instanceServiceContainer);\n                    let grp = di.openGroup(\"Insert of &lt;img&gt;\");\n                    designerCanvas.instanceServiceContainer.undoService.execute(new InsertAction(designerCanvas.rootDesignItem, designerCanvas.rootDesignItem.childCount, di));\n                    grp.commit();\n                    requestAnimationFrame(() => designerCanvas.instanceServiceContainer.selectionService.setSelectedElements([di]));\n\n                }\n            }\n            reader.readAsDataURL(event.dataTransfer.files[0]);\n        }\n    }\n\n    private _convertImage(img: any, name: string) {\n        let black = 50;\n        let rot = 'N';\n        let notrim = true;\n\n        // Get the image and convert to Z64\n        let res;\n\n\n        res = this._imageToACS(img, { black: black, rotate: rot, notrim: notrim });\n        let imgName = name.split('.')[0].substring(0, 8);\n        let bytesPerRow = res.rowlen;\n        let totalBytes = res.length;\n\n        return {\n            name: imgName,\n            totalBytes,\n            bytesPerRow,\n            hexData: res.acs\n        }\n    }\n\n    private _imageToACS(img, opts) {\n        // Draw the image to a temp canvas so we can access its RGBA data\n        let cvs = document.createElement('canvas');\n        let ctx = cvs.getContext('2d');\n\n        cvs.width = +img.width || img.offsetWidth;\n        cvs.height = +img.height || img.offsetHeight;\n        ctx.imageSmoothingQuality = 'high'; // in case canvas needs to scale image\n        ctx.drawImage(img, 0, 0, cvs.width, cvs.height);\n\n        let pixels = ctx.getImageData(0, 0, cvs.width, cvs.height);\n        return this._rgbaToACS(pixels.data, pixels.width, opts);\n    }\n\n    private _rgbaToACS(rgba, width, opts) {\n        const hexmap = (() => {\n            let arr = Array(256);\n            for (let i = 0; i < 16; i++) {\n                arr[i] = '0' + i.toString(16);\n            }\n            for (let i = 16; i < 256; i++) {\n                arr[i] = i.toString(16);\n            }\n            return arr;\n        })();\n\n\n        opts = opts || {};\n        width = width | 0;\n        if (!width || width < 0) {\n            throw new Error('Invalid width');\n        }\n        let height = ~~(rgba.length / width / 4);\n\n        // Create a monochome image, cropped to remove padding.\n        // The return is a Uint8Array with extra properties width and height.\n        let mono = this._monochrome(rgba, width, height, +opts.black || 50, opts.notrim);\n\n        let buf = this._normal(mono);\n\n        // Encode in hex and apply the \"Alternative Data Compression Scheme\"\n        //\n        //      G   H   I   J   K   L   M   N   O   P   Q   R   S   T   U   V   W   X   Y\n        //      1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19\n        //\n        //      g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v   w   x   y   z   \n        //     20  40  60  80 100 120 140 160 180 200 220 240 260 280 300 320 340 360 380 400\n        //\n        let imgw = buf.width;\n        let imgh = buf.height;\n        let rowl = ~~((imgw + 7) / 8);\n\n        let hex = '';\n        for (let i = 0, l = buf.length; i < l; i++) {\n            hex += hexmap[buf[i]];\n        }\n        let acs = '';\n        let re = /([0-9a-fA-F])\\1{2,}/g;\n        let match = re.exec(hex);\n        let offset = 0;\n        while (match) {\n            acs += hex.substring(offset, match.index);\n            let l = match[0].length;\n            while (l >= 400) {\n                acs += 'z';\n                l -= 400;\n            }\n            if (l >= 20) {\n                acs += '_ghijklmnopqrstuvwxy'[((l / 20) | 0)];\n                l = l % 20;\n            }\n            if (l) {\n                acs += '_GHIJKLMNOPQRSTUVWXY'[l];\n            }\n            acs += match[1];\n            offset = re.lastIndex;\n            match = re.exec(hex);\n        }\n        acs += hex.substr(offset);\n\n        // Example usage of the return value `rv`:\n        //\t\t'^GFA,' + rv.length + ',' + rv.length + ',' + rv.rowlen + ',' + rv.acs\n        return {\n            length: buf.length,\t// uncompressed number of bytes\n            rowlen: rowl,\t\t// number of packed bytes per row\n            width: imgw,\t\t// rotated image width in pixels\n            height: imgh,\t\t// rotated image height in pixels\n            acs: acs,\n        };\n    }\n\n    private _monochrome(rgba, width, height, black, notrim) {\n        // Convert black from percent to 0..255 value\n        black = 255 * black / 100;\n\n        let minx, maxx, miny, maxy;\n        if (notrim) {\n            minx = miny = 0;\n            maxx = width - 1;\n            maxy = height - 1;\n        } else {\n            // Run through the image and determine bounding box\n            maxx = maxy = 0;\n            minx = width;\n            miny = height;\n            let x = 0, y = 0;\n            for (let i = 0, n = width * height * 4; i < n; i += 4) {\n                // Alpha blend with white.\n                let a = rgba[i + 3] / 255;\n                let r = rgba[i] * .3 * a + 255 * (1 - a);\n                let g = rgba[i + 1] * .59 * a + 255 * (1 - a);\n                let b = rgba[i + 2] * .11 * a + 255 * (1 - a);\n                let gray = r + g + b;\n\n                if (gray <= black) {\n                    if (minx > x) minx = x;\n                    if (miny > y) miny = y;\n                    if (maxx < x) maxx = x;\n                    if (maxy < y) maxy = y;\n                }\n                if (++x == width) {\n                    x = 0;\n                    y++;\n                }\n            }\n        }\n\n        // One more time through the data, this time we create the cropped image.\n        let cx = maxx - minx + 1;\n        let cy = maxy - miny + 1;\n        let buf: Uint8Array & { width?: number, height?: number } = new Uint8Array(cx * cy);\n        let idx = 0;\n        for (let y = miny; y <= maxy; y++) {\n            let i = (y * width + minx) * 4;\n            for (let x = minx; x <= maxx; x++) {\n                // Alpha blend with white.\n                let a = rgba[i + 3] / 255;\n                let r = rgba[i] * .3 * a + 255 * (1 - a);\n                let g = rgba[i + 1] * .59 * a + 255 * (1 - a);\n                let b = rgba[i + 2] * .11 * a + 255 * (1 - a);\n                let gray = r + g + b;\n\n                buf[idx++] = gray <= black ? 1 : 0;\n                i += 4;\n            }\n        }\n\n        // Return the monochrome image\n        buf.width = cx;\n        buf.height = cy;\n        return buf;\n    }\n\n    private _normal(mono) {\n        let width = mono.width;\n        let height = mono.height;\n\n        let buf: Uint8Array & { width?: number, height?: number } = new Uint8Array(~~((width + 7) / 8) * height);\n        let idx = 0;\t// index into buf\n        let byte = 0;\t// current byte of image data\n        let bitx = 0;\t// bit index\n        for (let i = 0, n = mono.length; i < n; i++) {\n            byte |= mono[i] << (7 - (bitx++ & 7));\n\n            if (bitx == width || !(bitx & 7)) {\n                buf[idx++] = byte;\n                byte = 0;\n                if (bitx == width) {\n                    bitx = 0;\n                }\n            }\n        }\n        buf.width = width;\n        buf.height = height;\n        return buf;\n    }\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/services/ZplLayoutCopyPasteService.ts",
    "content": "import { ICopyPasteService, IDesignItem, InstanceServiceContainer, ServiceContainer, IRect, getTextFromClipboard, copyTextToClipboard, DefaultHtmlParserService } from '@node-projects/web-component-designer';\n\nexport class ZplLayoutCopyPasteService implements ICopyPasteService {\n    constructor() {\n    }\n\n    async copyItems(designItems: IDesignItem[]): Promise<void> {\n        let savedata = \"\";\n        for (let d of designItems) {\n            savedata += d.element.outerHTML;\n        }\n        await copyTextToClipboard(savedata);\n    }\n\n    async getPasteItems(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer): Promise<[designItems: IDesignItem[], positions?: IRect[]]> {\n        let result: IDesignItem[] = [];\n        const text = await getTextFromClipboard();\n        let htmlParser = new DefaultHtmlParserService();\n        result = await htmlParser.parse(text, serviceContainer, instanceServiceContainer, true);\n        return [result, null];\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/services/ZplLayoutPlacementService.ts",
    "content": "import { DefaultPlacementService, IDesignItem, IDesignerCanvas, IPoint, filterChildPlaceItems } from '@node-projects/web-component-designer';\n\nexport class ZplLayoutPlacementService extends DefaultPlacementService {\n\n    constructor() {\n        super();\n    }\n\n    override serviceForContainer(container: IDesignItem) {\n        return true;\n    }\n\n    override canEnter(container: IDesignItem, items: IDesignItem[]) {\n        return false;\n    }\n\n    override enterContainer(container: IDesignItem, items: IDesignItem[]) {\n        let filterdItems = filterChildPlaceItems(items);\n        for (let i of filterdItems) {\n            container.insertChild(i);\n        }\n    }\n\n    override leaveContainer(container: IDesignItem, items: IDesignItem[]) {\n    }\n\n    override finishPlace(event: MouseEvent, designerCanvas: IDesignerCanvas, container: IDesignItem, startPoint: IPoint, offsetInControl: IPoint, newPoint: IPoint, items: IDesignItem[]) {\n        {\n            super.finishPlace(event, designerCanvas, container, startPoint, offsetInControl, newPoint, items);\n        }\n    }\n}\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/services/ZplParserService.ts",
    "content": "import { DesignItem, ITextWriter, IDesignItem, IHtmlParserService, IHtmlWriterOptions, IHtmlWriterService, InstanceServiceContainer, ServiceContainer } from \"@node-projects/web-component-designer\";\nimport { ZplBarcode } from \"../widgets/zpl-barcode.js\";\nimport { ZplGraphicBox } from \"../widgets/zpl-graphic-box.js\";\nimport { ZplGraphicDiagonalLine } from \"../widgets/zpl-graphic-diagonal-line.js\";\nimport { ZplGraphicCircle } from \"../widgets/zpl-graphic-circle.js\";\nimport { ZplText } from \"../widgets/zpl-text.js\";\nimport { ZplImage } from \"../widgets/zpl-image.js\";\nimport { ZplComment } from \"../widgets/zpl-comment.js\";\n\nfunction getSetValue(...args) {\n    for (let a of args)\n        if (a != '' && a != null && a != \"NaN\")\n            return a;\n}\n\ntype image = {\n    name: string;\n    totalBytes: number;\n    bytesPerRow: number;\n    hexData: string;\n}\n\nexport class ZplParserService implements IHtmlParserService, IHtmlWriterService {\n\n    options: IHtmlWriterOptions = {};\n\n    createTransform(char: string, el: HTMLElement) {\n        switch (char) {\n            case 'R': {\n                el.style.transform = 'rotate(90deg) translateY(-100%)';\n                el.style.transformOrigin = '0% 0%'\n                return;\n            };\n            case 'I': {\n                el.style.transform = 'rotate(180deg)';\n                el.style.transformOrigin = '50% 50%'\n                return;\n            }\n            case 'B': {\n                el.style.transform = 'rotate(270deg)';\n                el.style.transformOrigin = '100% 100%'\n                return;\n            };\n        }\n        return;\n    }\n\n    async parse(html: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\n        let parts = html.split(\"^\");\n        let images: Record<string, image> = {};\n        if (parts[0][0] == \"~\") {\n            let imgStrings = parts[0].split(\"~\");\n            for (let img of imgStrings) {\n                if (img == \"\")\n                    continue\n                let imgParts = img.split(\",\");\n                images[imgParts[0].substring(2)] = {\n                    name: imgParts[0].substring(2),\n                    totalBytes: parseInt(imgParts[1]),\n                    bytesPerRow: parseInt(imgParts[2]),\n                    hexData: imgParts[3]\n                }\n            }\n        }\n        let designItems: IDesignItem[] = [];\n        let fontName: string;\n        let fontHeight: number;\n        let fontWidth: number;\n        let rotation: string = 'N';\n        let x: number;\n        let y: number;\n        let bc: boolean;\n        let qr: boolean;\n        let bw: number;\n        let bh: number;\n        let br: number;\n        // let bo: string;\n        let barcode: ZplBarcode;\n\n        //let a: number;\n        for (let p of parts) {\n            p = p.replaceAll(\"\\n\", \"\");\n            p = p.replaceAll(\"\\r\", \"\");\n            let command = p.substring(0, 2);\n            let fieldString = p.substring(2);\n            let fields = fieldString.split(\",\");\n\n            switch (command) {\n                case \"XA\":\n                case \"XZ\":\n                    break;\n                case \"FX\":\n                    let comment = new ZplComment();\n                    comment.style.position = \"absolute\";\n                    comment.style.left = x + \"px\";\n                    comment.style.top = y + \"px\";\n                    comment.setAttribute(\"content\", fieldString);\n                    designItems.push(DesignItem.createDesignItemFromInstance(comment, serviceContainer, instanceServiceContainer));\n                    break;\n                case \"AA\": //Ax x=fontname\n                case \"A0\": {\n                    fontName = command[1];\n                    rotation = getSetValue(fields[0], 'N');\n                    let defaultFontWidth = 15;\n                    switch (fontName) {\n                        case \"A\":\n                            defaultFontWidth = 15;\n                            break;\n                        case \"0\":\n                            defaultFontWidth = fontHeight;\n                    }\n                    fontHeight = parseInt(getSetValue(fields[1], defaultFontWidth));\n                    fontWidth = parseInt(getSetValue(fields[2], defaultFontWidth));\n                    break;\n                }\n                case \"CF\": //we should switch to use A, CF is default font\n                    rotation = 'N';\n                    fontName = fields[0];\n                    fontHeight = parseInt(fields[1]);\n                    let defaultFontWidth = 15;\n                    switch (fontName) {\n                        case \"A\":\n                            defaultFontWidth = 15;\n                            break;\n                        case \"0\":\n                            defaultFontWidth = fontHeight;\n                    }\n                    fontWidth = parseInt(getSetValue(fields[2], defaultFontWidth));\n                    break;\n                case \"FO\":\n                    x = parseInt(fields[0]);\n                    y = parseInt(fields[1]);\n                    //a = parseInt(fields[2]);\n                    break;\n                case \"GB\":\n                    let rect = new ZplGraphicBox();\n                    rect.style.position = \"absolute\";\n                    rect.style.left = x + \"px\";\n                    rect.style.top = y + \"px\";\n                    rect.style.width = getSetValue(fields[0], fields[2], '1') + \"px\";\n                    rect.style.height = getSetValue(fields[1], fields[2], '1') + \"px\";\n                    rect.setAttribute(\"stroke-width\", getSetValue(fields[2], '1'));\n                    rect.setAttribute(\"stroke-color\", getSetValue(fields[3], 'B') == \"B\" ? \"black\" : \"white\");\n                    rect.setAttribute(\"corner-rounding\", getSetValue(fields[4], '0'));\n                    designItems.push(DesignItem.createDesignItemFromInstance(rect, serviceContainer, instanceServiceContainer));\n                    break;\n                case \"GD\":\n                    let line = new ZplGraphicDiagonalLine();\n                    line.style.position = \"absolute\";\n                    line.style.left = x + \"px\";\n                    line.style.top = y + \"px\";\n                    line.style.width = getSetValue(fields[0], fields[2], '1') + \"px\";\n                    line.style.height = getSetValue(fields[1], fields[2], '1') + \"px\";\n                    line.setAttribute(\"stroke-width\", getSetValue(fields[2], '1'));\n                    line.setAttribute(\"stroke-color\", getSetValue(fields[3], 'B') == \"B\" ? \"black\" : \"white\");\n                    line.setAttribute(\"orientation\", getSetValue(fields[4], 'R'));\n                    designItems.push(DesignItem.createDesignItemFromInstance(line, serviceContainer, instanceServiceContainer));\n                    break;\n                case \"GE\":\n                    let circle = new ZplGraphicCircle();\n                    circle.style.position = \"absolute\";\n                    circle.style.left = x + \"px\";\n                    circle.style.top = y + \"px\";\n                    circle.style.width = getSetValue(fields[0], fields[2], '1') + \"px\";\n                    circle.style.height = getSetValue(fields[1], fields[2], '1') + \"px\";\n                    circle.setAttribute(\"stroke-width\", getSetValue(fields[2], '1'));\n                    circle.setAttribute(\"stroke-color\", getSetValue(fields[3], 'B') == \"B\" ? \"black\" : \"white\");\n                    designItems.push(DesignItem.createDesignItemFromInstance(circle, serviceContainer, instanceServiceContainer));\n                    break;\n                case \"BY\":\n                    bw = parseInt(getSetValue(fields[0], \"2\"));\n                    br = parseFloat(getSetValue(fields[1], \"3\"));\n                    bh = parseInt(getSetValue(fields[2], \"10\"));\n                    break;\n                case \"BC\":\n                    bc = true;\n                    // bo = getSetValue(fields[0], 'N');\n                    barcode = new ZplBarcode();\n                    barcode.style.position = \"absolute\";\n                    barcode.style.left = x + \"px\";\n                    barcode.style.top = y + \"px\";\n                    barcode.setAttribute(\"type\", \"CODE128\");\n                    if (bw)\n                        barcode.setAttribute(\"width\", bw.toString());\n                    if (br)\n                        barcode.setAttribute(\"ratio\", br.toString());\n                    barcode.setAttribute(\"height\", getSetValue(fields[1], bh).toString());\n                    break;\n                case \"BQ\":\n                    qr = true;\n                    bc = true;\n                    // bo = getSetValue(fields[0], 'N');\n                    barcode = new ZplBarcode();\n                    barcode.style.position = \"absolute\";\n                    barcode.style.left = x + \"px\";\n                    barcode.style.top = y + \"px\";\n                    barcode.setAttribute(\"type\", \"QR\");\n                    if (bw)\n                        barcode.setAttribute(\"width\", bw.toString());\n                    if (br)\n                        barcode.setAttribute(\"ratio\", br.toString());\n                    barcode.setAttribute(\"height\", getSetValue(fields[2], bh).toString());\n                    break;\n                case \"FD\":\n                    if (bc) {\n                        if (qr)\n                            barcode.setAttribute(\"content\", fieldString.substring(3));\n                        else\n                            barcode.setAttribute(\"content\", fieldString);\n                        designItems.push(DesignItem.createDesignItemFromInstance(barcode, serviceContainer, instanceServiceContainer));\n                        bc = false;\n                        qr = false;\n                    }\n                    else {\n                        let text = new ZplText();\n                        text.style.position = \"absolute\";\n                        text.style.left = x + \"px\";\n                        text.style.top = y + \"px\";\n                        text.setAttribute(\"font-name\", fontName);\n                        text.setAttribute(\"font-height\", fontHeight.toString());\n                        text.setAttribute(\"font-width\", fontWidth.toString());\n                        text.setAttribute(\"content\", fieldString);\n                        if (rotation) {\n                            this.createTransform(rotation, text);\n                        }\n                        designItems.push(DesignItem.createDesignItemFromInstance(text, serviceContainer, instanceServiceContainer));\n                    }\n                    break;\n                case \"XG\":\n                    let image = new ZplImage();\n                    let nm = fields[0].substring(2);\n                    image.style.position = \"absolute\";\n                    image.style.left = x + \"px\";\n                    image.style.top = y + \"px\";\n                    image.setAttribute(\"total-bytes\", images[nm].totalBytes.toString());\n                    image.setAttribute(\"bytes-per-row\", images[nm].bytesPerRow.toString());\n                    image.setAttribute(\"image-name\", nm);\n                    image.setAttribute(\"hex-image\", images[nm].hexData);\n                    image.setAttribute(\"scale-x\", getSetValue(fields[1], \"1\"));\n                    image.setAttribute(\"scale-y\", getSetValue(fields[2], \"1\"));\n                    designItems.push(DesignItem.createDesignItemFromInstance(image, serviceContainer, instanceServiceContainer));\n                    break;\n            }\n        }\n\n        return designItems;\n    }\n\n    write(textWriter: ITextWriter, designItems: IDesignItem[], rootContainerKeepInline: boolean, updatePositions?: boolean) {\n        let tx = \"^XA\\n\";\n        for (let d of designItems) {\n            if (d.element.nodeName == \"ZPL-IMAGE\") {\n                //@ts-ignore\n                textWriter.writeLine(d.element.createZplImage());\n                //@ts-ignore\n                tx += d.element.createZplImage() + '\\n';\n            }\n        }\n        for (let d of designItems) {\n            //@ts-ignore\n            tx += d.element.createZpl(); +'\\n'\n        }\n        tx += \"^XZ\";\n\n        textWriter.writeLine(\"^XA\");\n        // textWriter.writeLine(\"^FX For better view visit http://labelary.com/viewer.html?zpl=\" + encodeURIComponent(tx));\n        for (let d of designItems) {\n            let start = textWriter.position;\n            //@ts-ignore\n            textWriter.writeLine(d.element.createZpl())\n            let end = textWriter.position;\n            if (updatePositions && d.instanceServiceContainer.designItemDocumentPositionService) {\n                d.instanceServiceContainer.designItemDocumentPositionService.setPosition(d, { start: start, length: end - start });\n            }\n        }\n        textWriter.writeLine(\"^XZ\");\n    }\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/setupZplServiceContainer.ts",
    "content": "import { ExtensionType, DefaultModelCommandService, DefaultHtmlParserService, JsonFileElementsService, ServiceContainer, PositionExtensionProvider, SelectionDefaultExtensionProvider, 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, DefaultEditorTypesService, TransformToolButtonProvider, DesignItemService, DeletionService } from '@node-projects/web-component-designer';\nimport { ZplLayoutPlacementService } from './services/ZplLayoutPlacementService.js';\nimport { ZplParserService } from './services/ZplParserService.js';\nimport { ZplImageDrop } from './services/ZplImageDrop.js';\nimport { ZplLayoutCopyPasteService } from './services/ZplLayoutCopyPasteService.js';\nimport { ZplLayoutResizeExtensionProvider } from './extensions/ZplLayoutResizeExtensionProvider.js';\nimport { ZplDemoView } from './widgets/views/zpl-demo-view.js';\n\nexport function createZplDesignerServiceContainer() {\n    let serviceContainer = new ServiceContainer();\n\n    serviceContainer.register(\"instanceService\", new DefaultInstanceService());\n    serviceContainer.register(\"containerService\", new ZplLayoutPlacementService());\n    serviceContainer.register(\"snaplinesProviderService\", new SnaplinesProviderService());\n    serviceContainer.register(\"htmlParserService\", new DefaultHtmlParserService());\n    serviceContainer.register(\"htmlParserService\", new ZplParserService());\n    serviceContainer.register(\"htmlWriterService\", new ZplParserService());\n    serviceContainer.register(\"elementAtPointService\", new ElementAtPointService());\n    serviceContainer.register(\"externalDragDropService\", new ZplImageDrop());\n    serviceContainer.register(\"dragDropService\", new DragDropService());\n    serviceContainer.register(\"copyPasteService\", new ZplLayoutCopyPasteService());\n    serviceContainer.register(\"modelCommandService\", new DefaultModelCommandService());\n    serviceContainer.register('editorTypesService', new DefaultEditorTypesService());\n    serviceContainer.register(\"propertyGroupsService\", new PropertyGroupsService());\n    serviceContainer.register(\"propertyService\", new BaseCustomWebComponentPropertiesService(true));\n    serviceContainer.register(\"designItemService\", new DesignItemService());\n    serviceContainer.register(\"deletionService\", new DeletionService());\n\n    serviceContainer.register(\"undoService\", (designerCanvas: IDesignerCanvas) => new UndoService(designerCanvas));\n    serviceContainer.register(\"selectionService\", (designerCanvas: IDesignerCanvas) => new SelectionService(designerCanvas, false));\n    serviceContainer.register(\"designItemDocumentPositionService\", (designerCanvas: IDesignerCanvas) => new DesignItemDocumentPositionService(designerCanvas));\n\n    serviceContainer.designerExtensions.set(ExtensionType.Permanent, [\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.PrimarySelection, [\n        new ElementDragTitleExtensionProvider(),\n        new PositionExtensionProvider(),\n        new ZplLayoutResizeExtensionProvider(true)\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.Selection, [\n        new SelectionDefaultExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.PrimarySelectionContainer, [\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.MouseOver, [\n        new HighlightElementExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerDrag, [\n        new GrayOutExtensionProvider()\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerDragOverAndCanBeEntered, [\n        new AltToEnterContainerExtensionProvider(),\n        new GrayOutDragOverContainerExtensionProvider(),\n    ]);\n    serviceContainer.designerExtensions.set(ExtensionType.ContainerExternalDragOverAndCanBeEntered, [\n        new GrayOutDragOverContainerExtensionProvider(),\n    ]);\n\n    serviceContainer.designerTools.set(NamedTools.Pointer, new PointerTool());\n    serviceContainer.designerTools.set(NamedTools.DrawSelection, new RectangleSelectorTool());\n    serviceContainer.designerTools.set(NamedTools.Zoom, new ZoomTool());\n    serviceContainer.designerTools.set(NamedTools.Pan, new PanTool());\n    serviceContainer.designerTools.set(NamedTools.RectangleSelector, new RectangleSelectorTool());\n    serviceContainer.designerTools.set(NamedTools.MagicWandSelector, new MagicWandSelectorTool());\n\n    serviceContainer.designerContextMenuExtensions = [\n        new CopyPasteContextMenu(),\n        new ZMoveContextMenu(),\n        new MultipleItemsSelectedContextMenu(),\n        new ItemsBelowContextMenu()\n    ];\n\n    serviceContainer.designViewToolbarButtons.push(\n        new PointerToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new SelectorToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new ZoomToolButtonProvider(),\n        new SeperatorToolProvider(22),\n        new TransformToolButtonProvider()\n    );\n\n    serviceContainer.config.demoViewWidget = ZplDemoView;\n\n    serviceContainer.register('elementsService', new JsonFileElementsService('zpl', new URL(\"./widgets/elements.json\", import.meta.url)));\n\n    return serviceContainer;\n}\n\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/elements.json",
    "content": "{\n    \"elements\": [\n        {\n            \"name\": \"barcode\",\n            \"tag\": \"zpl-barcode\",\n            \"type\": \"polymer\",\n            \"defaultAttributes\": {\n                \"content\": \"barcode\",\n                \"type\": \"CODE128\",\n                \"width\": \"2\",\n                \"ratio\": \"20\",\n                \"height\": \"200\"\n            }\n        },\n        {\n            \"name\": \"qr-code\",\n            \"tag\": \"zpl-barcode\",\n            \"type\": \"polymer\",\n            \"defaultAttributes\": {\n                \"content\": \"barcode\",\n                \"type\": \"QR\",\n                \"width\": \"0\",\n                \"ratio\": \"0\",\n                \"height\": \"5\"\n            }\n        },\n        {\n            \"name\": \"graphic box\",\n            \"tag\": \"zpl-graphic-box\",\n            \"type\": \"polymer\",\n            \"defaultWidth\": \"300px\",\n            \"defaultHeight\": \"100px\",\n            \"defaultAttributes\": {\n                \"stroke-width\": \"5\",\n                \"stroke-color\": \"black\",\n                \"corner-rounding\": \"0\"\n            }\n        },\n        {\n            \"name\": \"graphic circle\",\n            \"tag\": \"zpl-graphic-circle\",\n            \"type\": \"polymer\",\n            \"defaultWidth\": \"200px\",\n            \"defaultHeight\": \"200px\",\n            \"defaultAttributes\": {\n                \"stroke-width\": \"5\",\n                \"stroke-color\": \"black\"\n            }\n        },\n        {\n            \"name\": \"graphic diagonal line\",\n            \"tag\": \"zpl-graphic-diagonal-line\",\n            \"type\": \"polymer\",\n            \"defaultWidth\": \"300px\",\n            \"defaultHeight\": \"100px\",\n            \"defaultAttributes\": {\n                \"stroke-width\": \"5\",\n                \"stroke-color\": \"black\",\n                \"orientation\": \"R\"\n            }\n        },\n        {\n            \"name\": \"text\",\n            \"tag\": \"zpl-text\",\n            \"type\": \"polymer\",\n            \"defaultAttributes\": {\n                \"content\": \"Text...\",\n                \"font-name\": \"0\",\n                \"font-height\": \"30\",\n                \"font-width\": \"30\"\n            }\n        },\n        {\n            \"name\": \"image\",\n            \"tag\": \"zpl-image\",\n            \"type\": \"polymer\",\n            \"defaultAttributes\": {\n                \"image-name\": \"\",\n                \"hex-image\": \"\",\n                \"scale-x\": \"1\",\n                \"scale-y\": \"1\"\n            }\n        }\n    ]\n}"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/views/zpl-demo-view.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from '@node-projects/base-custom-webcomponent';\nimport { IUiCommand, InstanceServiceContainer, ServiceContainer } from '@node-projects/web-component-designer';\nimport { IDemoView } from '@node-projects/web-component-designer/src/elements/widgets/demoView/IDemoView.js';\n\nexport class ZplDemoView extends BaseCustomWebComponentConstructorAppend implements IDemoView {\n\n    static override readonly template = html`<h2>Label generated via http://api.labelary.com/</h2><br><img id=\"image\">`;\n\n    static override readonly style = css`\n        :host {\n            display: block;\n            overflow: hidden;\n            background: white;\n            height: 100%;\n            width: 100%;\n            position: relative;\n        }\n        #image {\n            border: solid 1px black;\n        }`;\n\n    constructor() {\n        super();\n    }\n\n    executeCommand: (command: IUiCommand) => void;\n    canExecuteCommand: (command: IUiCommand) => boolean;\n\n    dispose(): void { }\n\n    async display(serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, code: string, style: string) {\n        const width = 4;\n        const height = 6;\n        const dpmm = '24dpmm';\n\n        const response = await fetch(`https://api.labelary.com/v1/printers/${dpmm}/labels/${width}x${height}/${0}`, {\n            method: \"POST\",\n            body: code,\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded',\n                'Accept': 'image/png'\n            },\n        });\n        (<HTMLIFrameElement>this._getDomElement('image')).src = URL.createObjectURL(await response.blob());;\n    }\n}\n\ncustomElements.define('node-projects-zpl-demo-view', ZplDemoView);"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-barcode.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, LazyLoader, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { BarcodeFormat, BarcodeOptions } from \"../jsBarcodeOptions.js\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\nimport QRCode from \"../qr.js\";\n\nexport class ZplBarcode extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css` *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"barcode-div\" style=\"width: 100%; height: 100%;\">\n    </div>\n    `;\n\n    static readonly is = 'zpl-barcode';\n\n    public content: string;\n    public type: BarcodeFormat;\n    public width: number;\n    public ratio: number;\n    public height: number;\n\n    private _barcode: HTMLDivElement;\n    private _barcodeOptions: BarcodeOptions;\n\n    static readonly properties = {\n        content: String,\n        type: BarcodeFormat,\n        width: Number,\n        ratio: Number,\n        height: Number,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._barcode = this._getDomElement<HTMLDivElement>(\"barcode-div\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        if (this.width > 10)\n            this.width = 10;\n        if (this.width < 1)\n            this.width = 1;\n        if (this.ratio > 3.0)\n            this.ratio = 3.0;\n        if (this.ratio < 2.0)\n            this.ratio = 2.0;\n        if (this.type == BarcodeFormat.QR) {\n            const svg = QRCode.generateSVG(this.content, {\n                modulesize: this.height,\n                margin: 0,\n                background: 'transparent'\n            });\n            this._barcode.appendChild(svg);\n            this._barcode.style.marginTop = \"10px\";\n        } else {\n            const packageurl = new URL(\"../../../../jsbarcode/dist/JsBarcode.all.js\", import.meta.url);\n            await LazyLoader.LoadJavascript(packageurl.toString());\n            let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n            this._barcode.appendChild(svg);\n            this._barcode.style.marginTop = \"\";\n\n            this._createOptions();\n            //@ts-ignore\n            JsBarcode(svg, this.content, this._barcodeOptions);\n        }\n    }\n\n    private _createOptions() {\n        this._barcodeOptions = {}\n        this._barcodeOptions.format = this.type;\n        this._barcodeOptions.width = this.width;\n        this._barcodeOptions.height = this.height;\n        this._barcodeOptions.margin = 0;\n        this._barcodeOptions.background = 'transparent';\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        if (this.type != BarcodeFormat.QR)\n            zpl += \"^BY\" + this.width + \",\" + this.ratio;\n        switch (this.type) {\n            case BarcodeFormat.CODE128:\n                zpl += \"^BCN,\" + this.height + \",Y,N,Y,N\";\n                break;\n            case BarcodeFormat.EAN13:\n                zpl += \"^BCN,\" + this.height + \",Y,N\";\n                break;\n            case BarcodeFormat.QR:\n                zpl += \"^BQN,2,\" + this.height + \"\";\n                break;\n        }\n\n        zpl += \"^FD\";\n        if (this.type == BarcodeFormat.QR) \n            zpl += 'QA,'; // bar code configuration prefix\n        zpl += this.content;\n        zpl += \"^FS\";\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplBarcode.is, ZplBarcode);\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-comment.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\n\nexport class ZplComment extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css`\n    :host {\n        display: none;\n    }`;\n\n    static override readonly template = html``;\n\n    static readonly is = 'zpl-comment';\n\n    public content: string;\n\n    static readonly properties = {\n        content: String\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += \"^FX\" + this.content\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplComment.is, ZplComment);"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-graphic-box.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\n\nenum StrokeColor {\n    black = \"black\",\n    white = \"white\",\n}\n\nexport class ZplGraphicBox extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css` *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"box-div\" style=\"width: 100%; height: 100%; overflow: hidden\">\n    </div>\n    `;\n\n    static readonly is = 'zpl-graphic-box';\n\n    public strokeWidth: number = 1;\n    public strokeColor: string = 'black';\n    public cornerRounding: number;\n\n    private _box: HTMLDivElement;\n    private _observer: ResizeObserver;\n\n    static readonly properties = {\n        strokeWidth: Number,\n        strokeColor: StrokeColor,\n        cornerRounding: Number,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._box = this._getDomElement<HTMLDivElement>(\"box-div\");\n        this._observer = new ResizeObserver(() => this._drawSvg());\n        this._observer.observe(this);\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        if (this.cornerRounding > 8)\n            this.cornerRounding = 8;\n        if (this.cornerRounding < 0)\n            this.cornerRounding = 0;\n        this._drawSvg();\n    }\n\n    private _drawSvg() {\n        let x = this.strokeWidth / 2;\n        let width = parseInt(this.style.width.replace(\"px\", \"\")) - this.strokeWidth;\n        if (width < this.strokeWidth)\n            width = this.strokeWidth;\n        let height = parseInt(this.style.height.replace(\"px\", \"\")) - this.strokeWidth;\n        if (height < this.strokeWidth)\n            height = this.strokeWidth;\n        let smallerLength = width;\n        if(smallerLength > height)\n        smallerLength = height;\n        let radius = (1/8) * this.cornerRounding * smallerLength / 2;\n        let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n        let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');\n        if (this.strokeColor == StrokeColor.black)\n            rect.setAttribute(\"stroke\", \"black\");\n        else\n            rect.setAttribute(\"stroke\", \"white\");\n        rect.setAttribute(\"fill\", \"white\");\n        rect.setAttribute(\"fill-opacity\", \"0.0\");\n        rect.setAttribute(\"stroke-width\", this.strokeWidth.toString());\n        rect.setAttribute(\"x\", x.toString());\n        rect.setAttribute(\"y\", x.toString());\n        rect.setAttribute(\"rx\", radius.toString());\n        rect.setAttribute(\"ry\", radius.toString());\n        rect.setAttribute(\"width\", width.toString());\n        rect.setAttribute(\"height\", height.toString());\n        svg.style.overflow = \"visible\";\n        if (this._box.childElementCount > 0)\n            this._box.removeChild(this._box.children[0]);\n        svg.appendChild(rect);\n        this._box.appendChild(svg);\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        zpl += \"^GB\"\n            + this.style.width.replace(\"px\", \"\") + \",\"\n            + this.style.height.replace(\"px\", \"\") + \",\"\n            + this.strokeWidth + \",\"\n            + (this.strokeColor == StrokeColor.black ? \"B\" : \"W\") + \",\"\n            + this.cornerRounding;\n        zpl += \"^FS\";\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplGraphicBox.is, ZplGraphicBox);\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-graphic-circle.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\n\nenum StrokeColor {\n    black = \"black\",\n    white = \"white\",\n}\n\nexport class ZplGraphicCircle extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css` *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"circle-div\" style=\"width: 100%; height: 100%;\">\n    </div>\n    `;\n\n    static readonly is = 'zpl-graphic-circle';\n\n    public strokeWidth: number = 1;\n    public strokeColor: string = 'black';\n\n    private _circle: HTMLDivElement;\n    private _observer: ResizeObserver;\n\n    static readonly properties = {\n        strokeWidth: Number,\n        strokeColor: StrokeColor,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._circle = this._getDomElement<HTMLDivElement>(\"circle-div\");\n        this._observer = new ResizeObserver(() => this._drawSvg());\n        this._observer.observe(this);\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._drawSvg();\n    }\n\n    private _drawSvg() {\n        let w = parseInt(this.style.width.replace(\"px\", \"\"));\n        let h = parseInt(this.style.height.replace(\"px\", \"\"));\n\n        let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n        let circle = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');\n        if (this.strokeColor == StrokeColor.black)\n            circle.setAttribute(\"stroke\", \"black\");\n        else\n            circle.setAttribute(\"stroke\", \"white\");\n        circle.setAttribute(\"fill\", \"white\");\n        circle.setAttribute(\"fill-opacity\", \"0.0\");\n        circle.setAttribute(\"stroke-width\", this.strokeWidth.toString());\n        circle.setAttribute(\"cx\", (w / 2).toString());\n        circle.setAttribute(\"cy\", (h / 2).toString());\n        circle.setAttribute(\"rx\", ((w / 2) - this.strokeWidth / 2).toString());\n        circle.setAttribute(\"ry\", ((h / 2) - this.strokeWidth / 2).toString());\n        if (this._circle.childElementCount > 0)\n            this._circle.removeChild(this._circle.children[0]);\n        svg.appendChild(circle);\n        svg.style.overflow = \"visible\";\n        this._circle.appendChild(svg);\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        zpl += \"^GE\"\n            + this.style.width.replace(\"px\", \"\") + \",\"\n            + this.style.height.replace(\"px\", \"\") + \",\"\n            + this.strokeWidth + \",\"\n            + (this.strokeColor == StrokeColor.black ? \"B\" : \"W\")\n        zpl += \"^FS\";\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplGraphicCircle.is, ZplGraphicCircle);\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-graphic-diagonal-line.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\n\nenum StrokeColor {\n    black = \"black\",\n    white = \"white\",\n}\nenum Orientation {\n    L = \"L\",\n    R = \"R\",\n}\n\nexport class ZplGraphicDiagonalLine extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css` *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"box-div\" style=\"width: 100%; height: 100%;\">\n    </div>\n    `;\n\n    static readonly is = 'zpl-graphic-diagonal-line';\n\n    public strokeWidth: number = 1;\n    public strokeColor: string = \"black\";\n    public orientation: string;\n\n    private _line: HTMLDivElement;\n    private _observer: ResizeObserver;\n\n    static readonly properties = {\n        strokeWidth: Number,\n        strokeColor: StrokeColor,\n        orientation: Orientation,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._line = this._getDomElement<HTMLDivElement>(\"box-div\");\n        this._observer = new ResizeObserver(() => this._drawSvg());\n        this._observer.observe(this);\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._drawSvg();\n    }\n\n    private _drawSvg() {\n        let w = parseInt(this.style.width.replace(\"px\", \"\"));\n        let h = parseInt(this.style.height.replace(\"px\", \"\")) - this.strokeWidth;\n        let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n        let line = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n        if (this.strokeColor == StrokeColor.black)\n            line.setAttribute(\"stroke\", \"black\");\n        else\n            line.setAttribute(\"stroke\", \"white\");\n        line.setAttribute(\"fill\", \"white\");\n        line.setAttribute(\"fill-opacity\", \"0.0\");\n        line.setAttribute(\"stroke-width\", this.strokeWidth.toString());\n        if (this.orientation == Orientation.L) {\n            line.setAttribute(\"x1\", \"0\");\n            line.setAttribute(\"y1\", \"0\");\n            line.setAttribute(\"x2\", w.toString());\n            line.setAttribute(\"y2\", h.toString());\n        }\n        else {\n            line.setAttribute(\"x1\", \"0\");\n            line.setAttribute(\"y1\", h.toString());\n            line.setAttribute(\"x2\", w.toString());\n            line.setAttribute(\"y2\", \"0\");\n        }\n        svg.style.overflow = \"visible\";\n        if (this._line.childElementCount > 0)\n            this._line.removeChild(this._line.children[0]);\n        svg.appendChild(line);\n        this._line.appendChild(svg);\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        zpl += \"^GD\"\n            + this.style.width.replace(\"px\", \"\") + \",\"\n            + this.style.height.replace(\"px\", \"\") + \",\"\n            + this.strokeWidth + \",\"\n            + (this.strokeColor == StrokeColor.black ? \"B\" : \"W\") + \",\"\n            + this.orientation;\n        zpl += \"^FS\";\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplGraphicDiagonalLine.is, ZplGraphicDiagonalLine);\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-image.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { requestAnimationFramePromise } from \"@node-projects/web-component-designer\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\n\nexport class ZplImage extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css` *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"wrapper\">\n        <canvas id=\"image-div\">\n        </canvas>\n    </div>\n    `;\n\n    static readonly is = 'zpl-image';\n\n\n    private _image: HTMLCanvasElement;\n    private _wrapper: HTMLDivElement;\n\n    public totalBytes: number;\n    public bytesPerRow: number;\n    public imageName: string;\n    public hexImage: string;\n    public scaleX: number;\n    public scaleY: number;\n\n    static readonly properties = {\n        totalBytes: Number,\n        bytesPerRow: Number,\n        imageName: String,\n        hexImage: String,\n        scaleX: Number,\n        scaleY: Number,\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._image = this._getDomElement<HTMLCanvasElement>(\"image-div\");\n        this._wrapper = this._getDomElement<HTMLDivElement>(\"wrapper\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        if (this.scaleX < 1)\n            this.scaleX = 1\n        if (this.scaleY < 1)\n            this.scaleY = 1\n        if (this.scaleX > 10)\n            this.scaleX = 10\n        if (this.scaleX > 10)\n            this.scaleX = 10\n        if (this.hexImage && this.bytesPerRow)\n            this.acsToCanvas(this.hexImage, this.bytesPerRow);\n        // this._image.style.width = 100 * this.scaleX + \"%\";\n        // this._image.style.aspectRatio = (this.scaleX / this.scaleY).toString();\n        this._image.style.transformOrigin = \"0 0\";\n        this._image.style.transform = \"scaleX(\" + this.scaleX + \") \" + \"scaleY(\" + this.scaleY + \")\";\n        await requestAnimationFramePromise();\n        let rect = this._image.getBoundingClientRect();\n        this._wrapper.style.width = rect.width + \"px\";\n        this._wrapper.style.height = rect.height + \"px\";\n\n    }\n\n    public createZplImage() {\n        let zpl = \"\";\n        zpl += \"~DG\" + this.imageName + \",\";\n        zpl += this.totalBytes + \",\";\n        zpl += this.bytesPerRow + \",\";\n        zpl += this.hexImage;\n        zpl = zpl.replaceAll(\"\\n\", \"\");\n        return zpl;\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        zpl += \"^XG\" + \"R:\" + this.imageName + \",\" + this.scaleX + \",\" + this.scaleY;\n        zpl += \"^FS\";\n        return zpl;\n    }\n\n    private acsToCanvas(imageData, bytesPerRow) {\n        let hex = imageData.replaceAll(\" \", \"\").replaceAll(\"\\n\", \"\").replaceAll(\"\\r\", \"\");\n        hex = hex.replace(/[g-zG-Y]+([0-9a-fA-F])/g, ($0, $1) => {\n            let rep = 0;\n            for (let i = 0, l = $0.length - 1; i < l; i++) {\n                let cd = $0.charCodeAt(i);\n                if (cd < 90) { // 'Z'\n                    rep += cd - 70;\n                } else {\n                    rep += (cd - 102) * 20;\n                }\n            }\n            return $1.repeat(rep);\n        });\n\n        let bytes = Array(hex.length / 2);\n        for (let i = 0, l = hex.length; i < l; i += 2) {\n            bytes[i >> 1] = parseInt(hex.substr(i, 2), 16);\n        }\n\n        let l = bytes.length;\n        let w = bytesPerRow * 8;\t\t// rowl is in bytes\n        let h = ~~(l / bytesPerRow);\n\n        // Render the GRF to a canvas\n        let cvs = this._image;\n        cvs.width = w;\n        cvs.height = h;\n\n        let ctx = cvs.getContext('2d');\n        let bmap = ctx.getImageData(0, 0, w, h);\n        let data = bmap.data;\n        let offs = 0;\n        for (let i = 0; i < l; i++) {\n            let byte = bytes[i];\n            for (let bit = 0x80; bit; bit = bit >>> 1, offs += 4) {\n                if (bit & byte) {\n                    data[offs] = 0;\n                    data[offs + 1] = 0;\n                    data[offs + 2] = 0;\n                    data[offs + 3] = 255;\t// Fully opaque\n                }\n            }\n        }\n        ctx.putImageData(bmap, 0, 0);\n        return cvs;\n    }\n}\n\ncustomElements.define(ZplImage.is, ZplImage);\n"
  },
  {
    "path": "packages/web-component-designer-zpl/src/widgets/zpl-text.ts",
    "content": "import { BaseCustomWebComponentConstructorAppend, css, html } from \"@node-projects/base-custom-webcomponent\";\nimport { getZplCoordinates } from \"../zplHelper.js\";\n\nenum FontNames {\n    Font_0 = \"0\",\n    Font_A = \"A\",\n}\n\nexport class ZplText extends BaseCustomWebComponentConstructorAppend {\n\n    static override readonly style = css`\n    *{\n        box-sizing: border-box;\n    }\n    `;\n\n    static override readonly template = html`\n    <div id=\"text-div\" style=\"width: 100%; height: 100%; pointer-events: none\">\n    </div>\n    `;\n\n    static readonly is = 'zpl-text';\n\n    public content: string;\n    public fontName: string;\n    public fontHeight: number;\n    public fontWidth: number;\n\n    private _text: HTMLDivElement;\n\n    static readonly properties = {\n        content: String,\n        fontName: FontNames,\n        fontHeight: Number,\n        fontWidth: Number\n    }\n\n    constructor() {\n        super();\n        this._restoreCachedInititalValues();\n        this._text = this._getDomElement<HTMLDivElement>(\"text-div\");\n    }\n\n    async ready() {\n        this._parseAttributesToProperties();\n        this._text.innerHTML = this.content;\n        this._text.style.transformOrigin = \"0 0\";\n        switch (this.fontName) {\n            case FontNames.Font_0:\n                this._text.style.fontSize = \"9px\";\n                this._text.style.fontFamily = \"RobotoCn, Verdana\";\n                this._text.style.fontKerning = \"none\";\n                this._text.style.transform = \"scaleX(\" + this.fontWidth / 9 + \") scaleY(\" + this.fontHeight / 9 + \") translate(0px, -3px)\";\n                break;\n            case FontNames.Font_A:\n                this._text.style.fontSize = \"9px\";\n                this._text.style.fontFamily = \"monospace\";\n                this._text.style.transform = \"scaleX(\" + this.fontWidth / 5 + \") scaleY(\" + this.fontHeight / 8 + \") translate(0px, -3px)\";\n                break;\n        }\n        this.style.width = '';\n        this.style.height = '';\n        requestAnimationFrame(() => {\n            let rect = this._text.getBoundingClientRect()\n            this.style.width = rect.width + 'px';\n            this.style.height = rect.height / 2 + 'px';\n        });\n    }\n\n    public createZpl() {\n        let zpl = \"\";\n        zpl += getZplCoordinates(this, 0);\n        zpl += \"^CF\" + this.fontName + \",\" + this.fontHeight + \",\" + this.fontWidth;\n        zpl += \"^FD\" + this.content\n        zpl += \"^FS\";\n        return zpl;\n    }\n}\n\ncustomElements.define(ZplText.is, ZplText);"
  },
  {
    "path": "packages/web-component-designer-zpl/src/zplHelper.ts",
    "content": "export function getZplCoordinates(obj: any, alignment: 0|1|2) {\n    let x = obj.style.left.replace(\"px\", \"\");\n    let y= obj.style.top.replace(\"px\", \"\");\n    return \"^FO\" + x + \",\" + y + \",\" + alignment;\n}\n"
  },
  {
    "path": "packages/web-component-designer-zpl/tsconfig.json",
    "content": "{\r\n  \"extends\": \"../../tsconfig.json\",\r\n  \"compilerOptions\": {\r\n    \"composite\": true,\r\n    \"outDir\": \"./dist\",\r\n    \"rootDir\": \"./src\",\r\n    \"noEmit\": false,\r\n    \"resolveJsonModule\": true\r\n  },\r\n  \"include\": [\r\n    \"src/**/*.ts\",\r\n    \"src/**/*.js\",\r\n    \"src/**/*.json\"\r\n  ],\r\n  \"references\": [\r\n    {\r\n      \"path\": \"../web-component-designer/tsconfig.json\"\r\n    },\r\n  ]\r\n}\r\n"
  },
  {
    "path": "todos/VueParserService.ts",
    "content": "/*\r\nWIP a parser for vue elements\r\n\r\nimport { InstanceServiceContainer } from '../InstanceServiceContainer.js';\r\nimport { ServiceContainer } from '../ServiceContainer.js';\r\nimport { IHtmlParserService } from './IHtmlParserService.js';\r\nimport { IDesignItem } from '../../item/IDesignItem.js';\r\n\r\nexport class VueParserService implements IHtmlParserService {\r\n  private htmlParser: IHtmlParserService;\r\n\r\n  constructor(htmlParser: IHtmlParserService) {\r\n    this.htmlParser = htmlParser;\r\n  }\r\n\r\n  async parse(code: string, serviceContainer: ServiceContainer, instanceServiceContainer: InstanceServiceContainer, parseSnippet: boolean): Promise<IDesignItem[]> {\r\n    const parsed = await this.htmlParser.parse(code, serviceContainer, instanceServiceContainer, parseSnippet);\r\n    return [parsed.find(x => x.name == 'template')];\r\n  }\r\n}\r\n*/"
  },
  {
    "path": "todos/todo.md",
    "content": "DesignItem.SetProperty should use a service?\nDesitnItem should know if a Control should be recreated (when property or attribute is set)\nThis should be removed from property service\n\nWe recreate a Control somewhere inside (via parser etc), but do not use Instanceservice, should be changed"
  },
  {
    "path": "tsconfig.build.json",
    "content": "{\r\n    \"compilerOptions\": {\r\n        \"module\": \"esnext\",\r\n        \"target\": \"esnext\",\r\n        \"allowJs\": true, /* Allow javascript files to be compiled. */\r\n        \"declaration\": true, /* Generates corresponding '.d.ts' file. */\r\n        \"sourceMap\": false, /* Generates corresponding '.map' file. */\r\n        \"composite\": true, /* Enable project compilation */\r\n        \"noImplicitOverride\": true,\r\n        \"noImplicitThis\": true, /* Raise error on 'this' expressions with an implied 'any' type. */\r\n        \"alwaysStrict\": true, /* Parse in strict mode and emit \"use strict\" for each source file. */\r\n        \"resolveJsonModule\": true,\r\n        \"noUnusedLocals\": true, /* Report errors on unused locals. */\r\n        \"noImplicitReturns\": true, /* Report error when not all code paths in function return a value. */\r\n        \"noFallthroughCasesInSwitch\": true, /* Report errors for fallthrough cases in switch statement. */\r\n        \"moduleResolution\": \"bundler\", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\r\n        \"esModuleInterop\": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\r\n        \"experimentalDecorators\": true, /* Enables experimental support for ES7 decorators. */\r\n        \"skipLibCheck\": true,\r\n        \"noImplicitAny\": false,\r\n        \"strictNullChecks\": false,\r\n        \"types\": [\"node\"]\r\n    }\r\n}"
  },
  {
    "path": "tsconfig.json",
    "content": "{\r\n  \"extends\": \"./tsconfig.build.json\",\r\n  \"compilerOptions\": {\r\n    \"noEmit\": true\r\n  }\r\n}"
  }
]