@agent-native/core 0.90.5 → 0.90.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +34 -7
  2. package/corpus/README.md +1 -1
  3. package/corpus/core/CHANGELOG.md +20 -0
  4. package/corpus/core/docs/content/agent-native-toolkit.mdx +1 -1
  5. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
  6. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
  7. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
  8. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
  9. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
  10. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
  11. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
  12. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
  13. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
  14. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
  15. package/corpus/core/docs/content/toolkit-agent-ux.mdx +1 -1
  16. package/corpus/core/docs/content/toolkit-app-adapters.mdx +1 -1
  17. package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +1 -1
  18. package/corpus/core/docs/content/toolkit-collaboration.mdx +1 -1
  19. package/corpus/core/docs/content/toolkit-command-navigation.mdx +2 -2
  20. package/corpus/core/docs/content/toolkit-comments-review.mdx +1 -1
  21. package/corpus/core/docs/content/toolkit-history.mdx +1 -1
  22. package/corpus/core/docs/content/toolkit-observability.mdx +1 -1
  23. package/corpus/core/docs/content/toolkit-org-team.mdx +1 -1
  24. package/corpus/core/docs/content/toolkit-resources.mdx +1 -1
  25. package/corpus/core/docs/content/toolkit-settings.mdx +1 -1
  26. package/corpus/core/docs/content/toolkit-setup-connections.mdx +1 -1
  27. package/corpus/core/docs/content/toolkit-sharing-ui.mdx +1 -1
  28. package/corpus/core/docs/content/toolkit-sharing.mdx +1 -1
  29. package/corpus/core/docs/content/toolkit-shell-hooks.mdx +1 -1
  30. package/corpus/core/docs/content/toolkit-ui.mdx +1 -1
  31. package/corpus/core/package.json +1 -1
  32. package/corpus/core/src/client/use-db-sync.ts +99 -1
  33. package/corpus/core/src/collab/client.ts +49 -4
  34. package/corpus/core/src/deploy/build.ts +156 -0
  35. package/corpus/core/src/server/core-routes-plugin.ts +137 -8
  36. package/corpus/templates/clips/changelog/2026-07-07-faster-local-whisper-dictation-start.md +6 -0
  37. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +311 -31
  38. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +2 -0
  39. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +9 -0
  40. package/corpus/templates/design/actions/read-source-file.ts +28 -0
  41. package/corpus/templates/design/actions/update-file.ts +76 -7
  42. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +181 -1
  43. package/corpus/templates/design/app/components/design/EditPanel.tsx +4 -0
  44. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +387 -25
  45. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1110 -88
  46. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +368 -16
  47. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +24 -0
  48. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +27 -6
  49. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +18 -0
  50. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +5 -4
  51. package/corpus/templates/design/app/components/design/multi-screen/handle-hit-zones.ts +124 -0
  52. package/corpus/templates/design/app/components/design/multi-screen/types.ts +23 -0
  53. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  54. package/corpus/templates/design/app/i18n-data.ts +19 -0
  55. package/corpus/templates/design/changelog/2026-07-07-dragging-a-free-floating-element-into-a-layout-column-or-row.md +6 -0
  56. package/corpus/templates/design/changelog/2026-07-07-dragging-a-repeated-list-item-like-a-to-do-card-generated-fr.md +6 -0
  57. package/corpus/templates/design/changelog/2026-07-07-dragging-an-element-onto-a-button-or-chip-s-middle-no-longer.md +6 -0
  58. package/corpus/templates/design/changelog/2026-07-07-elements-dropped-into-another-board-shape-now-stay-exactly-w.md +6 -0
  59. package/corpus/templates/design/changelog/2026-07-07-fixed-drag-reordering-elements-within-a-screen-no-longer-sil.md +6 -0
  60. package/corpus/templates/design/changelog/2026-07-07-fixed-dragging-an-element-from-a-screen-onto-the-canvas-now-.md +6 -0
  61. package/corpus/templates/design/changelog/2026-07-07-fixed-dragging-elements-between-screens-into-ai-generated-co.md +6 -0
  62. package/corpus/templates/design/changelog/2026-07-07-fixed-moving-elements-between-screens-no-longer-bakes-intern.md +6 -0
  63. package/corpus/templates/design/changelog/2026-07-07-fixed-the-cross-screen-drop-indicator-now-points-at-the-corr.md +6 -0
  64. package/corpus/templates/design/changelog/2026-07-07-fixed-the-zoom-menu-no-longer-leaves-the-canvas-unresponsive.md +6 -0
  65. package/corpus/templates/design/changelog/2026-07-07-text-drawn-on-the-canvas-is-now-readable-white-instead-of-in.md +6 -0
  66. package/corpus/templates/design/server/source-workspace.ts +27 -3
  67. package/corpus/templates/design/shared/board-file.ts +359 -0
  68. package/corpus/templates/design/shared/breakpoint-media.ts +21 -3
  69. package/corpus/templates/design/shared/code-layer.ts +145 -10
  70. package/dist/client/use-db-sync.d.ts +18 -0
  71. package/dist/client/use-db-sync.d.ts.map +1 -1
  72. package/dist/client/use-db-sync.js +88 -1
  73. package/dist/client/use-db-sync.js.map +1 -1
  74. package/dist/collab/client.d.ts.map +1 -1
  75. package/dist/collab/client.js +43 -4
  76. package/dist/collab/client.js.map +1 -1
  77. package/dist/deploy/build.d.ts +2 -0
  78. package/dist/deploy/build.d.ts.map +1 -1
  79. package/dist/deploy/build.js +96 -0
  80. package/dist/deploy/build.js.map +1 -1
  81. package/dist/observability/routes.d.ts +3 -3
  82. package/dist/server/core-routes-plugin.d.ts +10 -0
  83. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  84. package/dist/server/core-routes-plugin.js +103 -9
  85. package/dist/server/core-routes-plugin.js.map +1 -1
  86. package/docs/content/agent-native-toolkit.mdx +1 -1
  87. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +1 -1
  88. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +1 -1
  89. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +1 -1
  90. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +1 -1
  91. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +1 -1
  92. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +1 -1
  93. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +1 -1
  94. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +1 -1
  95. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +1 -1
  96. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +1 -1
  97. package/docs/content/toolkit-agent-ux.mdx +1 -1
  98. package/docs/content/toolkit-app-adapters.mdx +1 -1
  99. package/docs/content/toolkit-collaboration-ui.mdx +1 -1
  100. package/docs/content/toolkit-collaboration.mdx +1 -1
  101. package/docs/content/toolkit-command-navigation.mdx +2 -2
  102. package/docs/content/toolkit-comments-review.mdx +1 -1
  103. package/docs/content/toolkit-history.mdx +1 -1
  104. package/docs/content/toolkit-observability.mdx +1 -1
  105. package/docs/content/toolkit-org-team.mdx +1 -1
  106. package/docs/content/toolkit-resources.mdx +1 -1
  107. package/docs/content/toolkit-settings.mdx +1 -1
  108. package/docs/content/toolkit-setup-connections.mdx +1 -1
  109. package/docs/content/toolkit-sharing-ui.mdx +1 -1
  110. package/docs/content/toolkit-sharing.mdx +1 -1
  111. package/docs/content/toolkit-shell-hooks.mdx +1 -1
  112. package/docs/content/toolkit-ui.mdx +1 -1
  113. package/package.json +1 -1
@@ -612,6 +612,365 @@ function _inferSvgPrimitiveKind(inner: string): string | null {
612
612
  return null;
613
613
  }
614
614
 
615
+ // ---------------------------------------------------------------------------
616
+ // Poisoned nested-coordinate normalization
617
+ // ---------------------------------------------------------------------------
618
+
619
+ /**
620
+ * Half-size of the board's oversized iframe surface (131072 / 2). The board
621
+ * renderer shifts top-level board elements by this amount via a
622
+ * `body > [data-agent-native-node-id]{translate:65536px 65536px}` rule
623
+ * (DesignCanvas.tsx embeddedContentOffsetStyle) so board coordinate (0,0)
624
+ * lands at the center of the surface.
625
+ *
626
+ * Historic bugs wrote NESTED board children's left/top in board-IFRAME
627
+ * viewport coordinates (board coordinate + 65536) instead of parent-relative
628
+ * coordinates — and, before the offset rule was scoped to top-level children,
629
+ * the compounding translate could stack the offset more than once. The
630
+ * helpers below detect and repair those persisted values.
631
+ */
632
+ export const BOARD_SURFACE_CONTENT_OFFSET_PX = 65_536;
633
+
634
+ /**
635
+ * How far from an exact 65536-multiple a coordinate may sit and still be
636
+ * classified as "poisoned by the surface offset". Real poisoned values look
637
+ * like `k * 65536 + boardCoordinate` where the board coordinate of actual
638
+ * content is at most a few thousand px from origin; real parent-relative
639
+ * coordinates of nested children are bounded by their parent's size (far
640
+ * below this). A quarter of the offset is a comfortable margin on both sides.
641
+ */
642
+ const BOARD_COORD_POISON_REMAINDER_MAX_PX = 16_384;
643
+
644
+ /**
645
+ * Largest magnitude a rebased nested coordinate may have (when the parent's
646
+ * size is unknown) before it is considered garbage and clamped back into the
647
+ * parent's box. No sane nested child sits 16k px from its parent's origin.
648
+ */
649
+ const BOARD_NESTED_COORD_SANE_MAX_PX = 16_384;
650
+
651
+ /**
652
+ * Returns true when a persisted left/top value carries the board-surface
653
+ * offset fingerprint: within BOARD_COORD_POISON_REMAINDER_MAX_PX of a
654
+ * non-zero multiple of 65536.
655
+ */
656
+ export function isBoardSurfacePoisonedCoord(value: number): boolean {
657
+ if (!Number.isFinite(value)) return false;
658
+ const k = Math.round(value / BOARD_SURFACE_CONTENT_OFFSET_PX);
659
+ if (k === 0) return false;
660
+ return (
661
+ Math.abs(value - k * BOARD_SURFACE_CONTENT_OFFSET_PX) <=
662
+ BOARD_COORD_POISON_REMAINDER_MAX_PX
663
+ );
664
+ }
665
+
666
+ /**
667
+ * Strips the board-surface offset from a coordinate when (and only when) it
668
+ * carries the poison fingerprint; all other values pass through untouched.
669
+ * `66904 → 1368`, `-65420 → 116`, `131172 → 100`, `250 → 250`.
670
+ */
671
+ export function stripBoardSurfaceOffsetFromCoord(value: number): number {
672
+ if (!isBoardSurfacePoisonedCoord(value)) return value;
673
+ const k = Math.round(value / BOARD_SURFACE_CONTENT_OFFSET_PX);
674
+ return value - k * BOARD_SURFACE_CONTENT_OFFSET_PX;
675
+ }
676
+
677
+ /**
678
+ * Parent-relative position for a node being reparented INTO a target
679
+ * container, from the two elements' persisted absolute positions in the same
680
+ * coordinate space. This is the pure seam used by
681
+ * `handleOverviewPrimitiveReparent` (DesignEditor.tsx).
682
+ *
683
+ * Both inputs are normalized with `stripBoardSurfaceOffsetFromCoord` first:
684
+ * a source that was persisted in board-iframe viewport coordinates
685
+ * (boardCoord + 65536 — see BOARD_SURFACE_CONTENT_OFFSET_PX) is rebased to
686
+ * board space before subtracting the target's origin, so the child's new
687
+ * left/top always comes out parent-relative regardless of which upstream
688
+ * path produced the source position. For in-screen reparenting (small
689
+ * screen-root coordinates on both sides) the strip is a no-op and this is a
690
+ * plain subtraction.
691
+ */
692
+ export function computeReparentedChildPosition(
693
+ source: { x: number; y: number },
694
+ target: { x: number; y: number },
695
+ ): { x: number; y: number } {
696
+ return {
697
+ x:
698
+ stripBoardSurfaceOffsetFromCoord(source.x) -
699
+ stripBoardSurfaceOffsetFromCoord(target.x),
700
+ y:
701
+ stripBoardSurfaceOffsetFromCoord(source.y) -
702
+ stripBoardSurfaceOffsetFromCoord(target.y),
703
+ };
704
+ }
705
+
706
+ interface BoardWalkFrame {
707
+ tagName: string;
708
+ isNodeIdElement: boolean;
709
+ left: number | null;
710
+ top: number | null;
711
+ width: number | null;
712
+ height: number | null;
713
+ }
714
+
715
+ const BOARD_HTML_TAG_RE =
716
+ /<!--[\s\S]*?-->|<\/?([a-zA-Z][\w:-]*)((?:"[^"]*"|'[^']*'|[^"'<>])*)\/?>/g;
717
+
718
+ /** Tags whose raw text content must not be tag-walked. */
719
+ const BOARD_RAW_TEXT_TAGS = new Set(["script", "style", "textarea"]);
720
+
721
+ function parseInlineStylePx(
722
+ style: string,
723
+ prop: "left" | "top" | "width" | "height",
724
+ ): number | null {
725
+ const match = style.match(
726
+ new RegExp(
727
+ `(?:^|;)\\s*${prop}\\s*:\\s*(-?\\d+(?:\\.\\d+)?)px\\s*(?=;|$)`,
728
+ "i",
729
+ ),
730
+ );
731
+ if (!match?.[1]) return null;
732
+ const parsed = Number(match[1]);
733
+ return Number.isFinite(parsed) ? parsed : null;
734
+ }
735
+
736
+ function replaceInlineStylePx(
737
+ style: string,
738
+ prop: "left" | "top",
739
+ next: number,
740
+ ): string {
741
+ return style.replace(
742
+ new RegExp(`((?:^|;)\\s*${prop}\\s*:\\s*)(-?\\d+(?:\\.\\d+)?)px`, "i"),
743
+ `$1${next}px`,
744
+ );
745
+ }
746
+
747
+ function rebaseNestedBoardCoord(args: {
748
+ value: number;
749
+ ancestorOrigin: number;
750
+ parentSize: number | null;
751
+ childSize: number | null;
752
+ }): number {
753
+ const { value, ancestorOrigin, parentSize, childSize } = args;
754
+ if (!isBoardSurfacePoisonedCoord(value)) return value;
755
+ // Two distinct historic poison shapes, distinguishable by the sign of the
756
+ // offset multiple:
757
+ // - k >= 1 (positive): value = k*65536 + boardCoordinate — the child's
758
+ // board/viewport-space position was written verbatim (container-drop
759
+ // persist path). Parent-relative = board coordinate minus the parent's
760
+ // accumulated board-space origin.
761
+ // - k <= -1 (negative): value = parentRelative - 65536 — the bridge's
762
+ // rect-space nest rebase pre-compensated the (old, blanket) content
763
+ // translate. The stripped remainder already IS parent-relative; no
764
+ // ancestor subtraction. (A genuine board coordinate can never reach
765
+ // k <= -1 here: that would put content beyond the surface edge.)
766
+ const k = Math.round(value / BOARD_SURFACE_CONTENT_OFFSET_PX);
767
+ let rebased =
768
+ stripBoardSurfaceOffsetFromCoord(value) - (k > 0 ? ancestorOrigin : 0);
769
+ // Sanity clamp: exact recovery is possible for the common single-poison
770
+ // case, but compounded corruption (the parent moved after the child's
771
+ // position was written, multiple stacked offsets, user "fixes" made while
772
+ // the content rendered off-world) can leave a rebased value that is still
773
+ // far outside the parent. Pull those back into the parent's box so the
774
+ // child is at least visible and re-editable — the alternative is an
775
+ // element parked tens of thousands of px off-world.
776
+ const sane =
777
+ parentSize !== null
778
+ ? rebased >= -parentSize && rebased <= 2 * parentSize
779
+ : Math.abs(rebased) <= BOARD_NESTED_COORD_SANE_MAX_PX;
780
+ if (!sane) {
781
+ const max =
782
+ parentSize !== null ? Math.max(0, parentSize - (childSize ?? 0)) : 0;
783
+ rebased = Math.min(Math.max(0, rebased), max);
784
+ }
785
+ return Math.round(rebased);
786
+ }
787
+
788
+ /**
789
+ * Repairs already-persisted board content whose NESTED children carry
790
+ * left/top values poisoned by the board-surface offset (near-65536-multiple
791
+ * values — see BOARD_SURFACE_CONTENT_OFFSET_PX).
792
+ *
793
+ * For each element that (a) carries `data-agent-native-node-id`, (b) has at
794
+ * least one node-id-bearing ancestor inside `<body>` (i.e. is a nested board
795
+ * child, never a top-level one — top-level children legitimately use raw
796
+ * board coordinates), and (c) has a poisoned inline left and/or top:
797
+ *
798
+ * 1. the nearest 65536-multiple is stripped (recovering the board-space
799
+ * coordinate the value was written from),
800
+ * 2. the accumulated origin of its node-id ancestor chain (using
801
+ * already-normalized ancestor values, walking outer→inner) is subtracted
802
+ * to produce a parent-relative coordinate, and
803
+ * 3. values that are still implausible after rebasing are clamped into the
804
+ * parent's box.
805
+ *
806
+ * Everything else — non-poisoned coordinates, top-level children, elements
807
+ * without node ids, all other markup — passes through byte-identical. The
808
+ * function is pure, string-level (no DOM), and idempotent: normalized output
809
+ * contains no poisoned values, so a second pass returns `changed: false`.
810
+ *
811
+ * Applied on board-content load/adopt (DesignEditor) so designs corrupted by
812
+ * the historic nest-on-drop bugs self-heal the next time an editor opens
813
+ * them, and as a post-reparent safety net.
814
+ *
815
+ * Detectability (finding 4): this repair is a heuristic rewrite of
816
+ * already-persisted content with no built-in trace — a bad heuristic could
817
+ * silently mis-rebase real content and nobody would know. The return value
818
+ * includes `fixedNodeCount` and a small `samples` list (each node's original
819
+ * vs. rebased left/top) so callers can log what actually happened; this
820
+ * function itself stays side-effect-free (no console usage) so it remains
821
+ * safely callable from any context (including the post-reparent safety-net
822
+ * call sites, not just the load effect) — logging is the caller's job.
823
+ */
824
+ const NORMALIZE_POISONED_COORDS_SAMPLE_LIMIT = 5;
825
+
826
+ export function normalizePoisonedBoardNestedCoords(html: string): {
827
+ html: string;
828
+ changed: boolean;
829
+ fixedNodeCount: number;
830
+ samples: Array<{
831
+ nodeId: string | null;
832
+ before: { left: number | null; top: number | null };
833
+ after: { left: number | null; top: number | null };
834
+ }>;
835
+ } {
836
+ if (!html || !html.includes("data-agent-native-node-id")) {
837
+ return { html, changed: false, fixedNodeCount: 0, samples: [] };
838
+ }
839
+ // Cheap pre-scan: a poisoned coordinate needs at least 5 digits
840
+ // (|value| >= 65536 - 16384 = 49152).
841
+ if (!/(?:left|top)\s*:\s*-?\d{5,}/i.test(html)) {
842
+ return { html, changed: false, fixedNodeCount: 0, samples: [] };
843
+ }
844
+
845
+ const bodyStart = html.indexOf("<body");
846
+ const bodyTagEnd = bodyStart === -1 ? -1 : html.indexOf(">", bodyStart);
847
+ const walkStart = bodyTagEnd === -1 ? 0 : bodyTagEnd + 1;
848
+ const bodyClose = html.lastIndexOf("</body>");
849
+ const walkEnd = bodyClose === -1 ? html.length : bodyClose;
850
+
851
+ const stack: BoardWalkFrame[] = [];
852
+ let out = "";
853
+ let cursor = 0;
854
+ let changed = false;
855
+ let fixedNodeCount = 0;
856
+ const samples: Array<{
857
+ nodeId: string | null;
858
+ before: { left: number | null; top: number | null };
859
+ after: { left: number | null; top: number | null };
860
+ }> = [];
861
+
862
+ BOARD_HTML_TAG_RE.lastIndex = walkStart;
863
+ let match: RegExpExecArray | null;
864
+ while ((match = BOARD_HTML_TAG_RE.exec(html)) !== null) {
865
+ if (match.index >= walkEnd) break;
866
+ const token = match[0];
867
+ const tagName = match[1]?.toLowerCase();
868
+ if (!tagName) continue; // comment
869
+
870
+ if (token.startsWith("</")) {
871
+ const index = stack.map((frame) => frame.tagName).lastIndexOf(tagName);
872
+ if (index >= 0) stack.splice(index);
873
+ continue;
874
+ }
875
+
876
+ // Raw-text elements: skip their content wholesale so stray "<" inside
877
+ // scripts/styles can't desync the walk.
878
+ if (BOARD_RAW_TEXT_TAGS.has(tagName) && !token.endsWith("/>")) {
879
+ const close = html.indexOf(`</${tagName}`, BOARD_HTML_TAG_RE.lastIndex);
880
+ if (close === -1) break;
881
+ BOARD_HTML_TAG_RE.lastIndex = close;
882
+ continue;
883
+ }
884
+
885
+ const attrs = match[2] ?? "";
886
+ const isNodeIdElement = /\bdata-agent-native-node-id\s*=/.test(attrs);
887
+ const styleMatch = token.match(/(\bstyle\s*=\s*)("([^"]*)"|'([^']*)')/i);
888
+ const style = styleMatch?.[3] ?? styleMatch?.[4] ?? "";
889
+ let left = parseInlineStylePx(style, "left");
890
+ let top = parseInlineStylePx(style, "top");
891
+ const width = parseInlineStylePx(style, "width");
892
+ const height = parseInlineStylePx(style, "height");
893
+
894
+ const nodeIdAncestors = stack.filter((frame) => frame.isNodeIdElement);
895
+ const isNestedBoardChild = isNodeIdElement && nodeIdAncestors.length > 0;
896
+ const leftPoisoned =
897
+ isNestedBoardChild && left !== null && isBoardSurfacePoisonedCoord(left);
898
+ const topPoisoned =
899
+ isNestedBoardChild && top !== null && isBoardSurfacePoisonedCoord(top);
900
+
901
+ if ((leftPoisoned || topPoisoned) && styleMatch) {
902
+ const parent = nodeIdAncestors[nodeIdAncestors.length - 1];
903
+ const originX = nodeIdAncestors.reduce(
904
+ (sum, frame) => sum + (frame.left ?? 0),
905
+ 0,
906
+ );
907
+ const originY = nodeIdAncestors.reduce(
908
+ (sum, frame) => sum + (frame.top ?? 0),
909
+ 0,
910
+ );
911
+ const beforeLeft = left;
912
+ const beforeTop = top;
913
+ let nextStyle = style;
914
+ if (leftPoisoned && left !== null) {
915
+ left = rebaseNestedBoardCoord({
916
+ value: left,
917
+ ancestorOrigin: originX,
918
+ parentSize: parent?.width ?? null,
919
+ childSize: width,
920
+ });
921
+ nextStyle = replaceInlineStylePx(nextStyle, "left", left);
922
+ }
923
+ if (topPoisoned && top !== null) {
924
+ top = rebaseNestedBoardCoord({
925
+ value: top,
926
+ ancestorOrigin: originY,
927
+ parentSize: parent?.height ?? null,
928
+ childSize: height,
929
+ });
930
+ nextStyle = replaceInlineStylePx(nextStyle, "top", top);
931
+ }
932
+ if (nextStyle !== style) {
933
+ fixedNodeCount += 1;
934
+ if (samples.length < NORMALIZE_POISONED_COORDS_SAMPLE_LIMIT) {
935
+ const nodeIdMatch =
936
+ /\bdata-agent-native-node-id\s*=\s*("([^"]*)"|'([^']*)')/.exec(
937
+ attrs,
938
+ );
939
+ samples.push({
940
+ nodeId: nodeIdMatch?.[2] ?? nodeIdMatch?.[3] ?? null,
941
+ before: { left: beforeLeft, top: beforeTop },
942
+ after: { left, top },
943
+ });
944
+ }
945
+ const quote = styleMatch[2]?.startsWith("'") ? "'" : '"';
946
+ const rewrittenToken =
947
+ token.slice(0, styleMatch.index ?? 0) +
948
+ styleMatch[1] +
949
+ quote +
950
+ nextStyle +
951
+ quote +
952
+ token.slice((styleMatch.index ?? 0) + styleMatch[0].length);
953
+ out += html.slice(cursor, match.index) + rewrittenToken;
954
+ cursor = match.index + token.length;
955
+ changed = true;
956
+ }
957
+ }
958
+
959
+ const selfClosing = token.endsWith("/>") || VOID_ELEMENTS.has(tagName);
960
+ if (!selfClosing) {
961
+ stack.push({ tagName, isNodeIdElement, left, top, width, height });
962
+ }
963
+ }
964
+
965
+ if (!changed) return { html, changed: false, fixedNodeCount: 0, samples: [] };
966
+ return {
967
+ html: out + html.slice(cursor),
968
+ changed: true,
969
+ fixedNodeCount,
970
+ samples,
971
+ };
972
+ }
973
+
615
974
  // ---------------------------------------------------------------------------
616
975
  // Private helpers
617
976
  // ---------------------------------------------------------------------------
@@ -254,6 +254,12 @@ export function parseBreakpointMediaCss(css: string): BreakpointMediaModel {
254
254
  if (body === null) continue;
255
255
  mediaRe.lastIndex = bodyStart + body.length + 1;
256
256
 
257
+ // Matches the LAST [data-agent-native-node-id="…"] attribute directly
258
+ // before the `{`, so it accepts BOTH the legacy single-attribute selector
259
+ // and the current doubled-attribute selector (see
260
+ // serializeBreakpointMediaModel below) without double-counting: on a
261
+ // doubled selector the scan skips the first attribute occurrence (no `{`
262
+ // after it) and captures the nodeId exactly once from the second.
257
263
  const ruleRe =
258
264
  /\[data-agent-native-node-id="((?:\\.|[^"\\])*)"\]\s*\{([^}]*)\}/g;
259
265
  let ruleMatch: RegExpExecArray | null;
@@ -310,10 +316,22 @@ export function serializeBreakpointMediaModel(
310
316
  const lines = properties.map(
311
317
  (property) => ` ${property}: ${declarations[property]};`,
312
318
  );
319
+ // Doubled attribute selector → specificity (0,2,0), beating any single
320
+ // Tailwind utility class (0,1,0) REGARDLESS of stylesheet order. This
321
+ // matters because designs load Tailwind via the Play CDN script, which
322
+ // injects its generated utility sheet at RUNTIME — always after this
323
+ // static managed block — so an equal-specificity (0,1,0) single
324
+ // attribute selector always lost by source order and the breakpoint
325
+ // override silently never rendered (verified on real designs: the
326
+ // @media rule was live and matching, but .px-5 won). Doubling the
327
+ // attribute survives DOM reorder, Tailwind re-injection, and any
328
+ // future stylesheet ordering change; parseBreakpointMediaCss above
329
+ // accepts both formats, and because every write re-serializes the
330
+ // whole block, legacy single-attribute rules are upgraded to this
331
+ // format on the next edit automatically.
332
+ const attrSelector = `[data-agent-native-node-id="${escAttr(nodeId)}"]`;
313
333
  rules.push(
314
- ` [data-agent-native-node-id="${escAttr(nodeId)}"] {\n` +
315
- `${lines.join("\n")}\n` +
316
- ` }`,
334
+ ` ${attrSelector}${attrSelector} {\n${lines.join("\n")}\n }`,
317
335
  );
318
336
  }
319
337
  if (rules.length === 0) continue;
@@ -785,6 +785,11 @@ const STYLE_PROPERTY_ALIASES: Record<string, VisualStyleProperty> = {
785
785
  radius: "border-radius",
786
786
  rotation: "rotate",
787
787
  shadow: "box-shadow",
788
+ // Vendor-prefixed longhands need explicit aliases: the generic camel→kebab
789
+ // pass in normalizeStyleProperty yields "webkit-text-stroke-*" WITHOUT the
790
+ // required leading dash, which would miss the allow-list entirely.
791
+ webkitTextStrokeColor: "-webkit-text-stroke-color",
792
+ webkitTextStrokeWidth: "-webkit-text-stroke-width",
788
793
  };
789
794
 
790
795
  // Matches url(...) in double-quoted, single-quoted, or unquoted form so each
@@ -1299,19 +1304,107 @@ function findHtmlTagEnd(html: string, start: number): number {
1299
1304
  return html.length;
1300
1305
  }
1301
1306
 
1307
+ // Depth-aware closing-tag scan: used for NON_VISUAL_TAGS (script/style/
1308
+ // template/etc) whose interiors are skipped wholesale rather than descended
1309
+ // into by the main parser loop. A naive "first </tag> after `from`" search
1310
+ // (the previous implementation) breaks the moment the same tag nests inside
1311
+ // itself — e.g. `<template x-if><ul><template x-for>…</template></ul>
1312
+ // </template>` (a completely ordinary Alpine x-if-wrapping-x-for pattern) —
1313
+ // because it matches the INNER `</template>` and resumes the main loop right
1314
+ // after it, leaving the outer element's true `</ul></template>` closes to be
1315
+ // mis-parsed as stray/unmatched tags against whatever unrelated element is
1316
+ // on the stack. That corrupted contentEnd tracking for enclosing elements
1317
+ // (observed: body-append/insertion offsets computed from the wrong node,
1318
+ // splicing moved content into template interiors). Track same-tag open/close
1319
+ // depth so only the tag that actually balances the ORIGINAL opening tag is
1320
+ // returned, matching real nested-template documents correctly.
1302
1321
  function findClosingTag(
1303
1322
  html: string,
1304
1323
  tag: string,
1305
1324
  from: number,
1306
1325
  ): { closeStart: number; closeEnd: number } | null {
1307
- const closeRe = new RegExp(`<\\/\\s*${tag}\\s*>`, "gi");
1308
- closeRe.lastIndex = from;
1309
- const match = closeRe.exec(html);
1310
- if (!match) return null;
1311
- return {
1312
- closeStart: match.index,
1313
- closeEnd: match.index + match[0].length,
1314
- };
1326
+ const tagRe = new RegExp(`<(\\/?)\\s*${tag}\\b[^>]*>`, "gi");
1327
+ tagRe.lastIndex = from;
1328
+ let depth = 0;
1329
+ let match: RegExpExecArray | null;
1330
+ while ((match = tagRe.exec(html))) {
1331
+ const isClose = match[1] === "/";
1332
+ const selfClosing = !isClose && /\/\s*>$/.test(match[0]);
1333
+ if (isClose) {
1334
+ if (depth === 0) {
1335
+ return {
1336
+ closeStart: match.index,
1337
+ closeEnd: match.index + match[0].length,
1338
+ };
1339
+ }
1340
+ depth -= 1;
1341
+ } else if (!selfClosing) {
1342
+ depth += 1;
1343
+ }
1344
+ // Guard against zero-length matches causing an infinite loop (not
1345
+ // expected given the tag-name-anchored pattern, but cheap to keep safe).
1346
+ if (tagRe.lastIndex === match.index) {
1347
+ tagRe.lastIndex += 1;
1348
+ }
1349
+ }
1350
+ return null;
1351
+ }
1352
+
1353
+ // Defense-in-depth safety net for moveNodeBetweenDocuments: `<template>`
1354
+ // interiors (x-if/x-for/x-show templates and friends) are opaque to
1355
+ // parseHtmlElements (NON_VISUAL_TAGS) and, per the DOM spec, live in a
1356
+ // detached DocumentFragment (`template.content`) — a node inserted into a
1357
+ // template's raw markup range renders nowhere, can't be selected/queried by
1358
+ // the runtime DOM, and is invisible to every downstream querySelector-based
1359
+ // pass (getElementInfo, setAbsolutePositioningForNodeInHtml, etc). A correct
1360
+ // findClosingTag (see above) prevents the offset MISCALCULATION that used to
1361
+ // cause this, but this function is kept as an independent second guard —
1362
+ // even if some other insertion-point calculation ever computes an offset
1363
+ // that lands inside a real `<template>` block, this catches it and callers
1364
+ // redirect to a real DOM slot instead of silently splicing into markup that
1365
+ // will never render or be selectable again.
1366
+ //
1367
+ // Finding 8: when it fires, callers used to always redirect to the end of
1368
+ // <body> (or end of document) — a silent teleport that can land an anchored
1369
+ // insert far from where the user was working. `findEnclosingTemplateClose`
1370
+ // below returns the ENCLOSING outer template's closeEnd position (the
1371
+ // offset immediately after its `</template>`) when `offset` is inside a
1372
+ // template interior, so callers can redirect there instead: still a
1373
+ // guaranteed-safe real-DOM slot (immediately after a closing tag, a sibling
1374
+ // of the template rather than jumping to doc end), just much closer to the
1375
+ // anchor the caller actually asked for.
1376
+ function isOffsetInsideTemplateInterior(html: string, offset: number): boolean {
1377
+ return findEnclosingTemplateClose(html, offset) !== null;
1378
+ }
1379
+
1380
+ // Exported ONLY for the finding-8 redirect-target unit test below: with
1381
+ // findClosingTag's offset-miscalculation bug fixed (see the doc comment
1382
+ // above), every insertAt this module's own callers compute through
1383
+ // parseHtmlElements-derived positions (anchor.start/end/contentEnd,
1384
+ // bodyEl.contentEnd) already lands OUTSIDE template interiors in practice —
1385
+ // NON_VISUAL_TAGS like <template> are skipped wholesale, so a template can
1386
+ // never itself become part of another element's registered content range.
1387
+ // That makes this guard a true defense-in-depth backstop with no reachable
1388
+ // integration-level repro through moveNodeBetweenDocuments today; testing
1389
+ // the redirect target directly against a synthetic offset is the honest way
1390
+ // to pin its behavior instead of contriving a fragile call into the guard.
1391
+ export function findEnclosingTemplateClose(
1392
+ html: string,
1393
+ offset: number,
1394
+ ): { closeEnd: number } | null {
1395
+ const templateOpenRe = /<template\b[^>]*>/gi;
1396
+ let match: RegExpExecArray | null;
1397
+ while ((match = templateOpenRe.exec(html))) {
1398
+ const openEnd = match.index + match[0].length;
1399
+ if (openEnd > offset) break;
1400
+ const close = findClosingTag(html, "template", openEnd);
1401
+ const contentEnd = close ? close.closeStart : html.length;
1402
+ if (offset > openEnd && offset <= contentEnd) {
1403
+ return { closeEnd: close ? close.closeEnd : html.length };
1404
+ }
1405
+ templateOpenRe.lastIndex = close ? close.closeEnd : html.length;
1406
+ }
1407
+ return null;
1315
1408
  }
1316
1409
 
1317
1410
  function parseHtmlElements(html: string): ParsedElement[] {
@@ -4031,6 +4124,17 @@ export interface MoveNodeBetweenDocumentsResult {
4031
4124
  * Only present when status is "applied".
4032
4125
  */
4033
4126
  movedNodeId?: string;
4127
+ /**
4128
+ * Finding 8: true when the requested anchor placement fell inside a
4129
+ * `<template>` interior and the insert was redirected to a real DOM slot
4130
+ * instead (immediately after the enclosing template's `</template>` when
4131
+ * that could be located, otherwise the pre-existing doc-end/body-end
4132
+ * fallback). Hosts can use this to toast a "landed near, not exactly
4133
+ * where you dropped it" notice instead of the previous fully silent
4134
+ * teleport. Only meaningful when status is "applied" and an anchor was
4135
+ * requested.
4136
+ */
4137
+ anchorRedirected?: boolean;
4034
4138
  }
4035
4139
 
4036
4140
  /**
@@ -4117,6 +4221,7 @@ export function moveNodeBetweenDocuments(
4117
4221
 
4118
4222
  // --- Insert fragment into destHtml ---
4119
4223
  let nextDestHtml: string;
4224
+ let anchorRedirected = false;
4120
4225
 
4121
4226
  if (anchorNodeId) {
4122
4227
  const anchor = destElements.find(
@@ -4130,7 +4235,7 @@ export function moveNodeBetweenDocuments(
4130
4235
  message: `Anchor node with data-agent-native-node-id="${anchorNodeId}" not found in destHtml.`,
4131
4236
  };
4132
4237
  }
4133
- const insertAt =
4238
+ let insertAt =
4134
4239
  placement === "before"
4135
4240
  ? anchor.start
4136
4241
  : placement === "after"
@@ -4138,15 +4243,44 @@ export function moveNodeBetweenDocuments(
4138
4243
  : anchor.selfClosing
4139
4244
  ? anchor.end
4140
4245
  : anchor.contentEnd;
4246
+ // Never splice into a <template> interior — it renders nowhere and is
4247
+ // unselectable afterward (see isOffsetInsideTemplateInterior doc above).
4248
+ // Finding 8: redirect to immediately AFTER the ENCLOSING outer
4249
+ // </template> when it can be located — still a guaranteed-safe real-DOM
4250
+ // slot, just a sibling of the template instead of a jump all the way to
4251
+ // the end of <body>/the document. Falls back to the old doc-end/body-end
4252
+ // behavior only if the enclosing template's close somehow can't be
4253
+ // resolved (defense-in-depth for a guard that should always agree with
4254
+ // itself here).
4255
+ const enclosingTemplate = findEnclosingTemplateClose(destHtml, insertAt);
4256
+ if (enclosingTemplate) {
4257
+ insertAt = enclosingTemplate.closeEnd;
4258
+ anchorRedirected = true;
4259
+ } else if (isOffsetInsideTemplateInterior(destHtml, insertAt)) {
4260
+ const bodyEl = destElements.find((el) => el.tag === "body");
4261
+ insertAt = bodyEl
4262
+ ? bodyEl.selfClosing
4263
+ ? bodyEl.end
4264
+ : bodyEl.contentEnd
4265
+ : destHtml.length;
4266
+ anchorRedirected = true;
4267
+ }
4141
4268
  nextDestHtml = `${destHtml.slice(0, insertAt)}${fragment}${destHtml.slice(insertAt)}`;
4142
4269
  } else {
4143
4270
  // Default: find <body> and append inside it, or append at end of doc.
4144
4271
  const bodyEl = destElements.find((el) => el.tag === "body");
4145
- const insertAt = bodyEl
4272
+ let insertAt = bodyEl
4146
4273
  ? bodyEl.selfClosing
4147
4274
  ? bodyEl.end
4148
4275
  : bodyEl.contentEnd
4149
4276
  : destHtml.length;
4277
+ // Same template-interior guard as the anchored branch above — a
4278
+ // miscomputed bodyEl.contentEnd (or a body that itself is only reachable
4279
+ // through a template, e.g. a fragment being treated as a full document)
4280
+ // must never land inside template markup.
4281
+ if (isOffsetInsideTemplateInterior(destHtml, insertAt)) {
4282
+ insertAt = destHtml.length;
4283
+ }
4150
4284
  nextDestHtml = `${destHtml.slice(0, insertAt)}${fragment}${destHtml.slice(insertAt)}`;
4151
4285
  }
4152
4286
 
@@ -4158,5 +4292,6 @@ export function moveNodeBetweenDocuments(
4158
4292
  destHtml: nextDestHtml,
4159
4293
  status: "applied",
4160
4294
  movedNodeId,
4295
+ ...(anchorRedirected ? { anchorRedirected: true } : {}),
4161
4296
  };
4162
4297
  }
@@ -15,6 +15,24 @@ export type SyncEvent = {
15
15
  requestSource?: string;
16
16
  [k: string]: unknown;
17
17
  };
18
+ /**
19
+ * True for sync events that drive immediate, agent-initiated UI navigation
20
+ * or interaction rather than passive data invalidation — app-state writes in
21
+ * general (they back `["app-state"]` queries directly), and specifically the
22
+ * `navigate` / `show-questions` / `__set_url__` app-state keys that
23
+ * `invalidateForEvents` special-cases into their own query keys below.
24
+ *
25
+ * `useDbSync` batches ordinary invalidation-driving events (action/collab/db
26
+ * change events) into one flush per `INVALIDATE_COALESCE_MS` so a chatty doc
27
+ * doesn't refetch on every keystroke. That trade-off is wrong for these
28
+ * events: agent-driven navigation, `set-url`, and guided-questions prompts
29
+ * must land as close to instantly as possible, so any batch containing one
30
+ * of these bypasses the coalesce window and flushes immediately instead.
31
+ *
32
+ * Exported as a small pure predicate so this classification is unit-testable
33
+ * independent of the transport/timer plumbing around it.
34
+ */
35
+ export declare function isInteractionCriticalSyncEvent(event: SyncEvent): boolean;
18
36
  /** @internal */
19
37
  export declare function _resetSyncTransportRegistryForTests(): void;
20
38
  export interface SubscribeSyncEventsOptions {
@@ -1 +1 @@
1
- {"version":3,"file":"use-db-sync.d.ts","sourceRoot":"","sources":["../../src/client/use-db-sync.ts"],"names":[],"mappings":"AAUA,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CAC9B;AAED,UAAU,WAAW;IACnB,iBAAiB,CAAC,IAAI,CAAC,EAAE;QACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;KACvC,GAAG,IAAI,CAAC;CACV;AAeD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB,CAAC;AAqbF,gBAAgB;AAChB,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,MAAM,WAAW,0BAA0B;IACzC,gEAAgE;IAChE,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACrE,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,0BAA0B,GAClC,MAAM,IAAI,CAkBZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,SAAS,CACvB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACtD,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC,GACL,IAAI,CAsLN;AAED,wCAAwC;AACxC,eAAO,MAAM,cAAc,kBAAY,CAAC;AAExC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,GACL,MAAM,CAuDR"}
1
+ {"version":3,"file":"use-db-sync.d.ts","sourceRoot":"","sources":["../../src/client/use-db-sync.ts"],"names":[],"mappings":"AAUA,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CAC9B;AAED,UAAU,WAAW;IACnB,iBAAiB,CAAC,IAAI,CAAC,EAAE;QACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;KACvC,GAAG,IAAI,CAAC;CACV;AAsBD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB,CAAC;AAkEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAUxE;AAgYD,gBAAgB;AAChB,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,MAAM,WAAW,0BAA0B;IACzC,gEAAgE;IAChE,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACrE,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,0BAA0B,GAClC,MAAM,IAAI,CAkBZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,SAAS,CACvB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACtD,6BAA6B,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC,GACL,IAAI,CAyON;AAED,wCAAwC;AACxC,eAAO,MAAM,cAAc,kBAAY,CAAC;AAExC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,GACL,MAAM,CAuDR"}