@timeax/service-builder 0.0.6 → 0.0.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.
package/dist/index.js CHANGED
@@ -2934,11 +2934,6 @@ function deriveCanvasPresentation(args) {
2934
2934
  };
2935
2935
  }
2936
2936
 
2937
- // src/panels/canvas/index.tsx
2938
- import { useCanvas as useCanvas4, useWorkspace as useWorkspace5 } from "@timeax/digital-service-engine/workspace";
2939
- import { useEffect as useEffect8, useMemo as useMemo8, useRef as useRef5, useState as useState8 } from "react";
2940
- import { FiAlertCircle as FiAlertCircle3 } from "react-icons/fi";
2941
-
2942
2937
  // src/workspace/boot.ts
2943
2938
  var WORKSPACE_BOOT_SECTIONS = [
2944
2939
  "authors",
@@ -2992,7 +2987,7 @@ function getBootSectionTone(sectionState) {
2992
2987
  }
2993
2988
  function getWorkspaceBootHeadline(boot) {
2994
2989
  if (boot.isReady && boot.hasPartialFailure) return "Workspace loaded with issues";
2995
- if (boot.isReady) return "Workspace ready";
2990
+ if (boot.isReady) return "ready";
2996
2991
  if (boot.hasErrors && !boot.isBooting) return "Workspace load needs attention";
2997
2992
  if (boot.isSeededView) return "Syncing live workspace data";
2998
2993
  return "Loading workspace";
@@ -3029,6 +3024,11 @@ function getActiveBootSections(boot) {
3029
3024
  return WORKSPACE_BOOT_SECTIONS.filter((section) => boot.sections[section].status === "loading");
3030
3025
  }
3031
3026
 
3027
+ // src/panels/canvas/index.tsx
3028
+ import { useCanvas as useCanvas4, useWorkspace as useWorkspace5 } from "@timeax/digital-service-engine/workspace";
3029
+ import { useEffect as useEffect8, useMemo as useMemo8, useRef as useRef5, useState as useState8 } from "react";
3030
+ import { FiAlertCircle as FiAlertCircle3 } from "react-icons/fi";
3031
+
3032
3032
  // src/panels/canvas/app-toolbar.tsx
3033
3033
  import { FiAlertCircle, FiEye, FiEyeOff, FiSave, FiTerminal } from "react-icons/fi";
3034
3034
  import { LuGitCommitHorizontal, LuHistory, LuRedo2, LuUndo2 } from "react-icons/lu";
@@ -5043,16 +5043,20 @@ function CanvasPanel({
5043
5043
  offGraph();
5044
5044
  };
5045
5045
  }, [canvas.api]);
5046
+ function setSnapshot(event) {
5047
+ const nextSnapshot = event?.snapshot;
5048
+ if (!nextSnapshot) return;
5049
+ const nextHash = hashSnapshot(nextSnapshot);
5050
+ setCurrentSnapshotHash((current) => current === nextHash ? current : nextHash);
5051
+ if (nextHash !== lastSyncedSnapshotHashRef.current) {
5052
+ lastSyncedSnapshotHashRef.current = nextHash;
5053
+ ws.snapshot.set(() => nextSnapshot);
5054
+ }
5055
+ }
5046
5056
  useEffect8(() => {
5057
+ const catalogChange = canvas.api.on("catalog:change", setSnapshot);
5047
5058
  const offEditorChange = canvas.api.on("editor:change", (event) => {
5048
- const nextSnapshot = event?.snapshot;
5049
- if (!nextSnapshot) return;
5050
- const nextHash = hashSnapshot(nextSnapshot);
5051
- setCurrentSnapshotHash((current) => current === nextHash ? current : nextHash);
5052
- if (nextHash !== lastSyncedSnapshotHashRef.current) {
5053
- lastSyncedSnapshotHashRef.current = nextHash;
5054
- ws.snapshot.set(() => nextSnapshot);
5055
- }
5059
+ setSnapshot(event);
5056
5060
  if (event?.reason === "mutation" || event?.reason === "transaction") {
5057
5061
  setHistoryState({ canUndo: true, canRedo: false });
5058
5062
  }
@@ -5077,11 +5081,12 @@ function CanvasPanel({
5077
5081
  offEditorChange?.();
5078
5082
  offUndo?.();
5079
5083
  offRedo?.();
5084
+ catalogChange?.();
5080
5085
  };
5081
5086
  }, [canvas.api, ws.snapshot]);
5082
5087
  const hasChanges = currentSnapshotHash !== persistedBaselineHash;
5083
- const canSave = hasChanges || !!ws.snapshot.draft;
5084
- const canPublish = hasChanges || !!ws.snapshot.draft;
5088
+ const canSave = hasChanges;
5089
+ const canPublish = !!ws.snapshot.draft && !hasChanges;
5085
5090
  const bootActionReason = getBootActionReason(ws.boot);
5086
5091
  const failedBlockingSections = getFailedBlockingSections(ws.boot);
5087
5092
  const resolvedCurrentTagId = useMemo8(() => {
@@ -6590,19 +6595,61 @@ var drafts_default = Drafts;
6590
6595
 
6591
6596
  // src/panels/left/components/header.tsx
6592
6597
  import { useWorkspace as useWorkspace7 } from "@timeax/digital-service-engine/workspace";
6593
- import { GoChevronDown } from "react-icons/go";
6598
+ import { useState as useState11 } from "react";
6599
+ import { BsChevronDown, BsChevronRight } from "react-icons/bs";
6594
6600
  import { MdMenuOpen, MdOutlineSpaceDashboard } from "react-icons/md";
6595
6601
  import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
6596
- var Header = () => {
6602
+ var MenuRenderer = ({ items, depth = 0 }) => {
6603
+ const [openSubmenus, setOpenSubmenus] = useState11({});
6604
+ const toggleSubmenu = (name) => {
6605
+ setOpenSubmenus((prev) => ({ ...prev, [name]: !prev[name] }));
6606
+ };
6607
+ return /* @__PURE__ */ jsx26("ul", { className: "flex flex-col gap-1", children: items.map((item, index) => {
6608
+ const hasChildren = item.children && item.children.length > 0;
6609
+ const isOpen = openSubmenus[item.name];
6610
+ if (item.render) {
6611
+ return /* @__PURE__ */ jsx26("li", { children: item.render() }, index);
6612
+ }
6613
+ return /* @__PURE__ */ jsxs19("li", { className: "flex flex-col", children: [
6614
+ /* @__PURE__ */ jsxs19(
6615
+ "button",
6616
+ {
6617
+ type: "button",
6618
+ onClick: () => {
6619
+ if (hasChildren) {
6620
+ toggleSubmenu(item.name);
6621
+ } else if (item.command) {
6622
+ item.command();
6623
+ }
6624
+ },
6625
+ className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm text-slate-700 transition hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800",
6626
+ children: [
6627
+ item.icon && /* @__PURE__ */ jsx26("span", { className: "flex h-4 w-4 items-center justify-center text-slate-500", children: item.icon }),
6628
+ /* @__PURE__ */ jsx26("span", { className: "flex-1 text-left", children: item.name }),
6629
+ hasChildren && /* @__PURE__ */ jsx26("span", { className: `transition-transform ${isOpen ? "rotate-90" : ""}`, children: /* @__PURE__ */ jsx26(BsChevronRight, { className: "h-3 w-3" }) })
6630
+ ]
6631
+ }
6632
+ ),
6633
+ hasChildren && isOpen && /* @__PURE__ */ jsx26("div", { className: "mt-1 ml-4 border-l border-slate-200 pl-2 dark:border-slate-800", children: /* @__PURE__ */ jsx26(MenuRenderer, { items: item.children, depth: depth + 1 }) })
6634
+ ] }, index);
6635
+ }) });
6636
+ };
6637
+ var Header = ({ menu = [] }) => {
6597
6638
  const ws = useWorkspace7();
6598
6639
  const currentBranch = ws.branches.data?.find((branch) => branch.id === ws.branches.currentId);
6599
6640
  const workspaceStatus = getWorkspaceBootHeadline(ws.boot);
6641
+ const fullMenu = [
6642
+ {
6643
+ name: "Status",
6644
+ render: () => /* @__PURE__ */ jsx26("div", { className: "px-2 py-1.5 text-xs font-medium tracking-wider text-slate-500 uppercase", children: workspaceStatus })
6645
+ },
6646
+ ...menu
6647
+ ];
6600
6648
  return /* @__PURE__ */ jsx26("div", { className: "border-b border-slate-200 px-4 py-3 dark:border-slate-800", children: /* @__PURE__ */ jsxs19("div", { className: "flex items-start justify-between gap-3", children: [
6601
6649
  /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3", children: [
6602
6650
  /* @__PURE__ */ jsx26("span", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-blue-600 text-white shadow-sm", children: /* @__PURE__ */ jsx26(MdOutlineSpaceDashboard, { className: "text-base" }) }),
6603
- /* @__PURE__ */ jsxs19("div", { children: [
6604
- /* @__PURE__ */ jsx26("div", { className: "text-[10px] uppercase tracking-[0.24em] text-slate-400 dark:text-slate-500", children: "Workspace" }),
6605
- /* @__PURE__ */ jsx26("h1", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "Service Builder" }),
6651
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col justify-center gap-0", children: [
6652
+ /* @__PURE__ */ jsx26("h1", { className: "text-base leading-tight font-semibold text-slate-900 dark:text-slate-100", children: "Service Builder" }),
6606
6653
  /* @__PURE__ */ jsx26(
6607
6654
  drafts_default,
6608
6655
  {
@@ -6610,14 +6657,13 @@ var Header = () => {
6610
6657
  "button",
6611
6658
  {
6612
6659
  type: "button",
6613
- className: "mt-0.5 inline-flex items-center gap-1 rounded-md px-1 text-xs text-slate-500 transition hover:bg-slate-100 hover:text-slate-700 dark:text-slate-400 dark:hover:bg-slate-900 dark:hover:text-slate-200",
6660
+ className: "inline-flex cursor-pointer items-center gap-1 rounded-md text-xs text-slate-500 transition hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200",
6614
6661
  children: [
6615
6662
  /* @__PURE__ */ jsxs19("span", { children: [
6616
- currentBranch?.name ?? "Main branch",
6617
- " \xB7 ",
6618
- workspaceStatus
6663
+ "Active branch - ",
6664
+ /* @__PURE__ */ jsx26("b", { children: currentBranch?.name ?? "Main branch" })
6619
6665
  ] }),
6620
- /* @__PURE__ */ jsx26(GoChevronDown, { className: "text-sm" })
6666
+ /* @__PURE__ */ jsx26(BsChevronDown, {})
6621
6667
  ]
6622
6668
  }
6623
6669
  )
@@ -6625,7 +6671,10 @@ var Header = () => {
6625
6671
  )
6626
6672
  ] })
6627
6673
  ] }),
6628
- /* @__PURE__ */ jsx26(BuilderIconButton, { title: "Collapse left panel", children: /* @__PURE__ */ jsx26(MdMenuOpen, {}) })
6674
+ /* @__PURE__ */ jsxs19(Popover, { children: [
6675
+ /* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx26("span", { children: /* @__PURE__ */ jsx26(BuilderIconButton, { title: "Menu", children: /* @__PURE__ */ jsx26(MdMenuOpen, {}) }) }) }),
6676
+ /* @__PURE__ */ jsx26(PopoverContent, { className: "w-56 p-2", align: "end", children: /* @__PURE__ */ jsx26(MenuRenderer, { items: fullMenu }) })
6677
+ ] })
6629
6678
  ] }) });
6630
6679
  };
6631
6680
  var header_default = Header;
@@ -6691,7 +6740,7 @@ function ScrollBar({
6691
6740
  // src/panels/left/assets/index.tsx
6692
6741
  import { useInputs } from "@timeax/digital-service-engine/react";
6693
6742
  import { useWorkspace as useWorkspace8 } from "@timeax/digital-service-engine/workspace";
6694
- import { useMemo as useMemo11, useState as useState11 } from "react";
6743
+ import { useMemo as useMemo11, useState as useState12 } from "react";
6695
6744
  import { CiSearch } from "react-icons/ci";
6696
6745
  import { LuBox, LuShapes } from "react-icons/lu";
6697
6746
  import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
@@ -6701,7 +6750,7 @@ function toTitle(value) {
6701
6750
  function AssetsPanel() {
6702
6751
  const ws = useWorkspace8();
6703
6752
  const { registry } = useInputs();
6704
- const [query, setQuery] = useState11("");
6753
+ const [query, setQuery] = useState12("");
6705
6754
  const normalizedBuiltins = useMemo11(() => {
6706
6755
  const items = [];
6707
6756
  for (const [kind, variants] of registry._store.entries()) {
@@ -8931,7 +8980,7 @@ Panel.displayName = "Panel";
8931
8980
 
8932
8981
  // src/panels/left/layers/index.tsx
8933
8982
  import { useCanvas as useCanvas5 } from "@timeax/digital-service-engine/workspace";
8934
- import { useMemo as useMemo17, useRef as useRef10, useState as useState19 } from "react";
8983
+ import { useMemo as useMemo17, useRef as useRef10, useState as useState20 } from "react";
8935
8984
  import { BsXDiamondFill } from "react-icons/bs";
8936
8985
  import { CiSearch as CiSearch2 } from "react-icons/ci";
8937
8986
  import { LuTags as LuTags3 } from "react-icons/lu";
@@ -8961,10 +9010,10 @@ var Layers = () => {
8961
9010
  const fieldsRef = useRef10(null);
8962
9011
  const tagSearchInputRef = useRef10(null);
8963
9012
  const fieldSearchInputRef = useRef10(null);
8964
- const [tagSearch, setTagSearch] = useState19("");
8965
- const [fieldSearch, setFieldSearch] = useState19("");
8966
- const [isTagSearchOpen, setIsTagSearchOpen] = useState19(false);
8967
- const [isFieldSearchOpen, setIsFieldSearchOpen] = useState19(false);
9013
+ const [tagSearch, setTagSearch] = useState20("");
9014
+ const [fieldSearch, setFieldSearch] = useState20("");
9015
+ const [isTagSearchOpen, setIsTagSearchOpen] = useState20(false);
9016
+ const [isFieldSearchOpen, setIsFieldSearchOpen] = useState20(false);
8968
9017
  const handleSelection = (ids, meta) => {
8969
9018
  if (meta?.via === "api") return;
8970
9019
  const primary = ids[0];
@@ -8992,6 +9041,10 @@ var Layers = () => {
8992
9041
  detail.preventDefault();
8993
9042
  canvas.api.editor.editLabel(detail.node.id, detail.node.title);
8994
9043
  }
9044
+ if (detail.type === "insert" && detail.context.via == "ui.create") {
9045
+ const parent = detail.to.parent.id;
9046
+ canvas.api.editor.addOption(parent, { label: detail.node.title });
9047
+ }
8995
9048
  };
8996
9049
  const tags = useMemo17(() => filterTree(canvas.layers.tags, tagSearch), [canvas.layers.tags, tagSearch]);
8997
9050
  const fields = useMemo17(() => filterTree(canvas.layers.fields, fieldSearch), [canvas.layers.fields, fieldSearch]);
@@ -9298,7 +9351,7 @@ function TabsContent({
9298
9351
  import { CiGrid32 } from "react-icons/ci";
9299
9352
  import { MdOutlineLayers } from "react-icons/md";
9300
9353
  import { jsx as jsx40, jsxs as jsxs27 } from "react/jsx-runtime";
9301
- var Left = () => {
9354
+ var Left = ({ menu }) => {
9302
9355
  const tabClassName = cn(
9303
9356
  "rounded-xl! py-2! shadow-none!",
9304
9357
  "text-slate-500 data-[state=active]:bg-white data-[state=active]:text-slate-900 data-[state=active]:shadow-sm",
@@ -9312,7 +9365,7 @@ var Left = () => {
9312
9365
  maxWidth: 420,
9313
9366
  minWidth: 320,
9314
9367
  children: /* @__PURE__ */ jsxs27("div", { className: "flex h-full flex-col", children: [
9315
- /* @__PURE__ */ jsx40(header_default, {}),
9368
+ /* @__PURE__ */ jsx40(header_default, { menu }),
9316
9369
  /* @__PURE__ */ jsx40("div", { className: "flex min-h-0 flex-1 flex-col gap-2 py-2", children: /* @__PURE__ */ jsxs27(Tabs, { defaultValue: "layers", className: "flex min-h-0 flex-1 flex-col px-3", children: [
9317
9370
  /* @__PURE__ */ jsxs27(TabsList, { className: "grid h-auto grid-cols-2 rounded-2xl bg-slate-100 p-1 dark:bg-slate-900", children: [
9318
9371
  /* @__PURE__ */ jsx40(TabsTrigger, { className: tabClassName, value: "layers", children: /* @__PURE__ */ jsxs27("span", { className: "flex items-center gap-2", children: [
@@ -9336,7 +9389,7 @@ var left_default = Left;
9336
9389
  // src/panels/right/components/users.tsx
9337
9390
  import "@timeax/digital-service-engine/workspace";
9338
9391
  import { useMemo as useMemo18 } from "react";
9339
- import { GoChevronDown as GoChevronDown2 } from "react-icons/go";
9392
+ import { GoChevronDown } from "react-icons/go";
9340
9393
  import { HiOutlineDotsHorizontal } from "react-icons/hi";
9341
9394
  import { PiUserPlusThin } from "react-icons/pi";
9342
9395
  import { jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
@@ -9421,7 +9474,7 @@ var Users = ({ users, onViewUser, onSetMain, onToggleAdmin, onRemoveUser, onInvi
9421
9474
  extra
9422
9475
  ] }) : null
9423
9476
  ] }),
9424
- /* @__PURE__ */ jsx41(GoChevronDown2, { className: "shrink-0 text-slate-500" })
9477
+ /* @__PURE__ */ jsx41(GoChevronDown, { className: "shrink-0 text-slate-500" })
9425
9478
  ]
9426
9479
  }
9427
9480
  ) }),
@@ -9789,13 +9842,13 @@ function ActiveDot() {
9789
9842
  // src/panels/right/tabs/comments.tsx
9790
9843
  import { useWorkspace as useWorkspace10 } from "@timeax/digital-service-engine/workspace";
9791
9844
  import { InputField as InputField4 } from "@timeax/form-palette";
9792
- import { useMemo as useMemo21, useState as useState21 } from "react";
9845
+ import { useMemo as useMemo21, useState as useState22 } from "react";
9793
9846
  import { MdOutlineSend } from "react-icons/md";
9794
9847
  import { jsx as jsx45, jsxs as jsxs31 } from "react/jsx-runtime";
9795
9848
  var Comments = () => {
9796
9849
  const ws = useWorkspace10();
9797
- const [value, setValue] = useState21("");
9798
- const [query, setQuery] = useState21("");
9850
+ const [value, setValue] = useState22("");
9851
+ const [query, setQuery] = useState22("");
9799
9852
  const threads = useMemo21(() => {
9800
9853
  const items = ws.comments.threads.data ?? [];
9801
9854
  const lower = query.trim().toLowerCase();
@@ -10088,7 +10141,7 @@ function RenderIf({ data, emptyMessage = null, children, when }) {
10088
10141
  // src/panels/right/partials/global/add-service.tsx
10089
10142
  import { useCanvas as useCanvas7, useWorkspace as useWorkspace11 } from "@timeax/digital-service-engine/workspace";
10090
10143
  import { InputField as InputField5 } from "@timeax/form-palette";
10091
- import { useMemo as useMemo23, useState as useState23 } from "react";
10144
+ import { useMemo as useMemo23, useState as useState24 } from "react";
10092
10145
  import { BsPlus } from "react-icons/bs";
10093
10146
  import { Fragment as Fragment9, jsx as jsx50, jsxs as jsxs33 } from "react/jsx-runtime";
10094
10147
  function AddService({ className, data, disabled, disabledMessage, readOnly, readOnlyMessage, emptyMessage }) {
@@ -10162,9 +10215,9 @@ function ServiceSummary({ service, nodeId, disabled }) {
10162
10215
  }
10163
10216
  function AddServicePopover({ data, services: services2 }) {
10164
10217
  const canvas = useCanvas7();
10165
- const [open, setOpen] = useState23(false);
10166
- const [query, setQuery] = useState23("");
10167
- const [selected, setSelected] = useState23("");
10218
+ const [open, setOpen] = useState24(false);
10219
+ const [query, setQuery] = useState24("");
10220
+ const [selected, setSelected] = useState24("");
10168
10221
  const options = useMemo23(() => {
10169
10222
  const lower = query.trim().toLowerCase();
10170
10223
  return services2.filter((service) => {
@@ -10222,11 +10275,11 @@ var add_service_default = AddService;
10222
10275
  import { resolveInputDescriptor, useInputs as useInputs2 } from "@timeax/digital-service-engine/react";
10223
10276
  import { useCanvas as useCanvas12 } from "@timeax/digital-service-engine/workspace";
10224
10277
  import { InputField as InputField11 } from "@timeax/form-palette";
10225
- import { useEffect as useEffect17, useMemo as useMemo28, useState as useState27 } from "react";
10278
+ import { useEffect as useEffect17, useMemo as useMemo28, useState as useState28 } from "react";
10226
10279
 
10227
10280
  // src/panels/right/partials/properties/components/descriptor-settings.tsx
10228
10281
  import { InputField as InputField6 } from "@timeax/form-palette";
10229
- import { useMemo as useMemo24, useState as useState24 } from "react";
10282
+ import { useMemo as useMemo24, useState as useState25 } from "react";
10230
10283
 
10231
10284
  // src/panels/right/partials/properties/components/meta.ts
10232
10285
  function mergeNodeMeta(meta, patch) {
@@ -10524,9 +10577,9 @@ function DescriptorPrimitiveField({ schema, value, hasOverride, onSet, onClear,
10524
10577
  function DescriptorObjectField({ schema, value, hasOverride, onSet, onClear, path, allowClear = true }) {
10525
10578
  const objectValue = asRecord(value);
10526
10579
  const shapeEntries = useMemo24(() => Object.entries(schema.shape ?? {}), [schema.shape]);
10527
- const [draftKey, setDraftKey] = useState24("");
10528
- const [draftShape, setDraftShape] = useState24(shapeEntries[0]?.[0] ?? "");
10529
- const [activeShapes, setActiveShapes] = useState24({});
10580
+ const [draftKey, setDraftKey] = useState25("");
10581
+ const [draftShape, setDraftShape] = useState25(shapeEntries[0]?.[0] ?? "");
10582
+ const [activeShapes, setActiveShapes] = useState25({});
10530
10583
  const orderedEntries = useMemo24(() => getOrderedObjectEntries(schema), [schema]);
10531
10584
  const dynamicKeys = Object.keys(objectValue).filter((key) => !hasOwn(schema.fields, key));
10532
10585
  const commitObject = (nextObject) => {
@@ -10684,8 +10737,8 @@ function DescriptorObjectField({ schema, value, hasOverride, onSet, onClear, pat
10684
10737
  function DescriptorArrayField({ schema, value, hasOverride, onSet, onClear, path, allowClear = true }) {
10685
10738
  const arrayValue = asArray(value);
10686
10739
  const shapeEntries = useMemo24(() => Object.entries(schema.shape ?? {}), [schema.shape]);
10687
- const [draftShape, setDraftShape] = useState24(shapeEntries[0]?.[0] ?? "");
10688
- const [activeShapes, setActiveShapes] = useState24({});
10740
+ const [draftShape, setDraftShape] = useState25(shapeEntries[0]?.[0] ?? "");
10741
+ const [activeShapes, setActiveShapes] = useState25({});
10689
10742
  const commitArray = (nextArray) => {
10690
10743
  if (!nextArray.length) {
10691
10744
  onClear(path);
@@ -11660,15 +11713,15 @@ function ValidationSection({ node }) {
11660
11713
  import "@timeax/digital-service-engine/core";
11661
11714
  import { useCanvas as useCanvas11 } from "@timeax/digital-service-engine/workspace";
11662
11715
  import { keyBy } from "lodash";
11663
- import { useMemo as useMemo27, useState as useState26 } from "react";
11716
+ import { useMemo as useMemo27, useState as useState27 } from "react";
11664
11717
 
11665
11718
  // src/panels/right/partials/properties/components/AddIncludesPopover.tsx
11666
11719
  import { InputField as InputField10 } from "@timeax/form-palette";
11667
- import { useState as useState25 } from "react";
11720
+ import { useState as useState26 } from "react";
11668
11721
  import { BsPlus as BsPlus5 } from "react-icons/bs";
11669
11722
  import { jsx as jsx55, jsxs as jsxs38 } from "react/jsx-runtime";
11670
11723
  function AddIncludesPopover({ open, onOpenChange, onSelect, options }) {
11671
- const [value, setValue] = useState25();
11724
+ const [value, setValue] = useState26();
11672
11725
  return /* @__PURE__ */ jsxs38(Popover, { open, onOpenChange, children: [
11673
11726
  /* @__PURE__ */ jsx55(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx55(SectionActionTriggerButton, { icon: /* @__PURE__ */ jsx55(BsPlus5, {}), children: "Add" }) }),
11674
11727
  /* @__PURE__ */ jsx55(PopoverContent, { children: /* @__PURE__ */ jsxs38("div", { className: "flex flex-col gap-2", children: [
@@ -11705,7 +11758,7 @@ function AddIncludesPopover({ open, onOpenChange, onSelect, options }) {
11705
11758
  import { jsx as jsx56, jsxs as jsxs39 } from "react/jsx-runtime";
11706
11759
  function IncExcludeSection({ node, mode, capability }) {
11707
11760
  const canvas = useCanvas11();
11708
- const [open, setOpen] = useState26(false);
11761
+ const [open, setOpen] = useState27(false);
11709
11762
  const canEdit = capability?.canEdit ?? true;
11710
11763
  const helperMessage = capability?.message;
11711
11764
  const fields = useMemo27(() => keyBy(canvas.props.fields, "id"), [canvas.props]);
@@ -11806,9 +11859,9 @@ function FieldProperties({ className, node }) {
11806
11859
  const nameValue = node.raw.name ?? "";
11807
11860
  const placeholderValue = defaults.placeholder ?? "";
11808
11861
  const helpTextValue = defaults.helpText ?? "";
11809
- const [nameDraft, setNameDraft] = useState27(nameValue);
11810
- const [placeholderDraft, setPlaceholderDraft] = useState27(placeholderValue);
11811
- const [helpTextDraft, setHelpTextDraft] = useState27(helpTextValue);
11862
+ const [nameDraft, setNameDraft] = useState28(nameValue);
11863
+ const [placeholderDraft, setPlaceholderDraft] = useState28(placeholderValue);
11864
+ const [helpTextDraft, setHelpTextDraft] = useState28(helpTextValue);
11812
11865
  const descriptor = useMemo28(() => resolveInputDescriptor(registry, currentType, currentVariant), [currentType, currentVariant, registry]);
11813
11866
  const descriptorUi = descriptor?.ui ?? {};
11814
11867
  const descriptorKeySet = useMemo28(() => new Set(getDescriptorUiKeys(descriptorUi)), [descriptorUi]);
@@ -12128,7 +12181,7 @@ import "@timeax/digital-service-engine/core";
12128
12181
  import { useCanvas as useCanvas14 } from "@timeax/digital-service-engine/workspace";
12129
12182
  import { InputField as InputField14 } from "@timeax/form-palette";
12130
12183
  import { ArrowUpRight } from "lucide-react";
12131
- import { useMemo as useMemo29, useState as useState28 } from "react";
12184
+ import { useMemo as useMemo29, useState as useState29 } from "react";
12132
12185
  import { TiDelete } from "react-icons/ti";
12133
12186
 
12134
12187
  // src/panels/right/partials/properties/tag/AddConstraintsPopover.tsx
@@ -12254,7 +12307,7 @@ import { Fragment as Fragment12, jsx as jsx62, jsxs as jsxs45 } from "react/jsx-
12254
12307
  function TagConstraintsSection({ node }) {
12255
12308
  const constraints = Object.keys(node.raw.constraints ?? {});
12256
12309
  const canvas = useCanvas14();
12257
- const [open, setOpen] = useState28(false);
12310
+ const [open, setOpen] = useState29(false);
12258
12311
  const allConstraints = useMemo29(() => canvas.api.getConstraints() ?? [], [canvas.props]);
12259
12312
  return /* @__PURE__ */ jsx62(Fragment12, { children: /* @__PURE__ */ jsxs45(Section, { children: [
12260
12313
  /* @__PURE__ */ jsxs45(Section.Header, { children: [
@@ -12371,7 +12424,7 @@ function TagProperties({ node }) {
12371
12424
  // src/panels/right/tabs/properties.tsx
12372
12425
  import { useCanvas as useCanvas15, useWorkspace as useWorkspace12 } from "@timeax/digital-service-engine/workspace";
12373
12426
  import { InputField as InputField15 } from "@timeax/form-palette";
12374
- import { useMemo as useMemo30, useState as useState29 } from "react";
12427
+ import { useMemo as useMemo30, useState as useState30 } from "react";
12375
12428
  import { AiOutlineLoading3Quarters } from "react-icons/ai";
12376
12429
  import { MdOutlineContentCopy } from "react-icons/md";
12377
12430
  import { jsx as jsx66, jsxs as jsxs47 } from "react/jsx-runtime";
@@ -12387,7 +12440,7 @@ var Properties = () => {
12387
12440
  if (!canvas.activeId) return { kind: "none" };
12388
12441
  return canvas.selector.getNode(canvas.activeId);
12389
12442
  }, [canvas.activeId, canvas.props, canvas.selection]);
12390
- const [copying, setCopying] = useState29(false);
12443
+ const [copying, setCopying] = useState30(false);
12391
12444
  const Kind = kinds[node.kind];
12392
12445
  if (!Kind) {
12393
12446
  return /* @__PURE__ */ jsx66("div", { className: "p-4", children: /* @__PURE__ */ jsx66(EmptyState, { title: "No active node selected", description: "Pick a tag, field, or option from the layers panel or canvas to inspect its core settings." }) });
@@ -12960,7 +13013,7 @@ function describeCheck(check, state) {
12960
13013
  // src/workspace/fallback-editor-modal.tsx
12961
13014
  import { useCanvas as useCanvas18, useWorkspace as useWorkspace14 } from "@timeax/digital-service-engine/workspace";
12962
13015
  import cloneDeep2 from "lodash/cloneDeep";
12963
- import { Suspense, lazy, createContext as createContext4, useCallback as useCallback17, useContext as useContext4, useEffect as useEffect18, useMemo as useMemo32, useState as useState30 } from "react";
13016
+ import { Suspense, lazy, createContext as createContext4, useCallback as useCallback17, useContext as useContext4, useEffect as useEffect18, useMemo as useMemo32, useState as useState31 } from "react";
12964
13017
  import { createPortal as createPortal3 } from "react-dom";
12965
13018
  import { FiX as FiX3 } from "react-icons/fi";
12966
13019
  import { jsx as jsx70, jsxs as jsxs51 } from "react/jsx-runtime";
@@ -12980,9 +13033,9 @@ var FallbackEditorModalContext = createContext4(defaultContextValue2);
12980
13033
  function FallbackEditorModalProvider({ children }) {
12981
13034
  const canvas = useCanvas18();
12982
13035
  const ws = useWorkspace14();
12983
- const [launch, setLaunch] = useState30(null);
12984
- const [sessionId, setSessionId] = useState30(0);
12985
- const [surfaceError, setSurfaceError] = useState30(null);
13036
+ const [launch, setLaunch] = useState31(null);
13037
+ const [sessionId, setSessionId] = useState31(0);
13038
+ const [surfaceError, setSurfaceError] = useState31(null);
12986
13039
  const close = useCallback17(() => {
12987
13040
  setLaunch(null);
12988
13041
  setSurfaceError(null);
@@ -13151,7 +13204,7 @@ function useFallbackEditorModal() {
13151
13204
 
13152
13205
  // src/workspace/bottom-panel/index.tsx
13153
13206
  import { useCanvas as useCanvas21, useWorkspace as useWorkspace15 } from "@timeax/digital-service-engine/workspace";
13154
- import { useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo34, useRef as useRef11, useState as useState33 } from "react";
13207
+ import { useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo34, useRef as useRef11, useState as useState34 } from "react";
13155
13208
  import { FiEye as FiEye3, FiEyeOff as FiEyeOff2, FiSearch, FiTerminal as FiTerminal2, FiX as FiX5 } from "react-icons/fi";
13156
13209
  import { LuGripHorizontal, LuLayers3 } from "react-icons/lu";
13157
13210
  import { MdOutlineSync } from "react-icons/md";
@@ -13606,7 +13659,7 @@ function LogCard({ row, onRemove }) {
13606
13659
 
13607
13660
  // src/workspace/bottom-panel/service-picker-dialog.tsx
13608
13661
  import { InputField as InputField16 } from "@timeax/form-palette";
13609
- import { useEffect as useEffect19, useMemo as useMemo33, useState as useState31 } from "react";
13662
+ import { useEffect as useEffect19, useMemo as useMemo33, useState as useState32 } from "react";
13610
13663
  import { createPortal as createPortal4 } from "react-dom";
13611
13664
 
13612
13665
  // src/workspace/bottom-panel/service-picker.ts
@@ -13735,8 +13788,8 @@ function ServicePickerDialog({
13735
13788
  onOpenChange,
13736
13789
  onConfirm
13737
13790
  }) {
13738
- const [filters, setFilters] = useState31(() => createDefaultServicePickerFilters());
13739
- const [selectedIds, setSelectedIds] = useState31(/* @__PURE__ */ new Set());
13791
+ const [filters, setFilters] = useState32(() => createDefaultServicePickerFilters());
13792
+ const [selectedIds, setSelectedIds] = useState32(/* @__PURE__ */ new Set());
13740
13793
  useEffect19(() => {
13741
13794
  if (!open) return;
13742
13795
  setFilters(createDefaultServicePickerFilters());
@@ -14077,7 +14130,7 @@ function ResizableHandle({
14077
14130
 
14078
14131
  // src/workspace/bottom-panel/services-split-pane.tsx
14079
14132
  import { InputField as InputField17 } from "@timeax/form-palette";
14080
- import { useEffect as useEffect20, useState as useState32 } from "react";
14133
+ import { useEffect as useEffect20, useState as useState33 } from "react";
14081
14134
  import { FaFolderOpen } from "react-icons/fa";
14082
14135
  import { FiEdit2, FiFilter, FiFolderPlus, FiPlus as FiPlus2, FiTrash2 as FiTrash22 } from "react-icons/fi";
14083
14136
 
@@ -14562,7 +14615,6 @@ function CatalogToolbar({
14562
14615
  {
14563
14616
  variant: "treeselect",
14564
14617
  mode: "button",
14565
- label: "Catalog group selector",
14566
14618
  className: "w-fit!",
14567
14619
  multiple: false,
14568
14620
  value: treeValue,
@@ -14587,8 +14639,8 @@ function GroupInputPopoverButton({
14587
14639
  disabled = false,
14588
14640
  onSubmit
14589
14641
  }) {
14590
- const [open, setOpen] = useState32(false);
14591
- const [value, setValue] = useState32(initialValue);
14642
+ const [open, setOpen] = useState33(false);
14643
+ const [value, setValue] = useState33(initialValue);
14592
14644
  useEffect20(() => {
14593
14645
  if (open) setValue(initialValue);
14594
14646
  }, [initialValue, open]);
@@ -14654,7 +14706,7 @@ function ConfirmPopoverButton({
14654
14706
  disabled = false,
14655
14707
  onConfirm
14656
14708
  }) {
14657
- const [open, setOpen] = useState32(false);
14709
+ const [open, setOpen] = useState33(false);
14658
14710
  return /* @__PURE__ */ jsxs55(Popover, { open, onOpenChange: setOpen, children: [
14659
14711
  /* @__PURE__ */ jsx76(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx76(
14660
14712
  "button",
@@ -14745,26 +14797,26 @@ function BottomConsolePanel({ controller, errors, activeServices, allServices, o
14745
14797
  const policies2 = ws.policies.policies.data ?? [];
14746
14798
  const currentTagId = canvas.api.selection.currentTag?.();
14747
14799
  const preferredTagId = currentTagId != null ? String(currentTagId) : canvas.layers.tags[0]?.id != null ? String(canvas.layers.tags[0]?.id) : null;
14748
- const [activeSearch, setActiveSearch] = useState33("");
14749
- const [allSearch, setAllSearch] = useState33("");
14750
- const [compatibleOnly, setCompatibleOnly] = useState33(false);
14751
- const [hideActiveServices, setHideActiveServices] = useState33(false);
14752
- const [filterOpen, setFilterOpen] = useState33(false);
14753
- const [searchOpen, setSearchOpen] = useState33(false);
14754
- const [contextLinked, setContextLinked] = useState33(false);
14755
- const [servicePickerOpen, setServicePickerOpen] = useState33(false);
14756
- const [selectedActiveServiceId, setSelectedActiveServiceId] = useState33(null);
14757
- const [selectedAllServiceId, setSelectedAllServiceId] = useState33(null);
14758
- const [catalogContextDraft, setCatalogContextDraft] = useState33(
14800
+ const [activeSearch, setActiveSearch] = useState34("");
14801
+ const [allSearch, setAllSearch] = useState34("");
14802
+ const [compatibleOnly, setCompatibleOnly] = useState34(false);
14803
+ const [hideActiveServices, setHideActiveServices] = useState34(false);
14804
+ const [filterOpen, setFilterOpen] = useState34(false);
14805
+ const [searchOpen, setSearchOpen] = useState34(false);
14806
+ const [contextLinked, setContextLinked] = useState34(false);
14807
+ const [servicePickerOpen, setServicePickerOpen] = useState34(false);
14808
+ const [selectedActiveServiceId, setSelectedActiveServiceId] = useState34(null);
14809
+ const [selectedAllServiceId, setSelectedAllServiceId] = useState34(null);
14810
+ const [catalogContextDraft, setCatalogContextDraft] = useState34(
14759
14811
  () => createDefaultServiceContext(canvas.props, preferredTagId)
14760
14812
  );
14761
- const [catalogState, setCatalogState] = useState33(null);
14762
- const [panelPosition, setPanelPosition] = useState33(() => readStoredPanelPosition());
14763
- const [draggingPanel, setDraggingPanel] = useState33(false);
14764
- const [consoleSubTab, setConsoleSubTab] = useState33("validation");
14765
- const [consoleScopeFilter, setConsoleScopeFilter] = useState33("all");
14766
- const [consoleSeverityFilter, setConsoleSeverityFilter] = useState33("all");
14767
- const [consoleIntroState, setConsoleIntroState] = useState33({
14813
+ const [catalogState, setCatalogState] = useState34(null);
14814
+ const [panelPosition, setPanelPosition] = useState34(() => readStoredPanelPosition());
14815
+ const [draggingPanel, setDraggingPanel] = useState34(false);
14816
+ const [consoleSubTab, setConsoleSubTab] = useState34("validation");
14817
+ const [consoleScopeFilter, setConsoleScopeFilter] = useState34("all");
14818
+ const [consoleSeverityFilter, setConsoleSeverityFilter] = useState34("all");
14819
+ const [consoleIntroState, setConsoleIntroState] = useState34({
14768
14820
  validation: { minimized: false, closed: false },
14769
14821
  logs: { minimized: false, closed: false },
14770
14822
  notices: { minimized: false, closed: false }
@@ -15366,7 +15418,7 @@ function CatalogModeSwitch({ mode, onChange }) {
15366
15418
  // src/index.tsx
15367
15419
  import { createInputRegistry, Provider as InputsProvider, registerEntries } from "@timeax/digital-service-engine/react";
15368
15420
  import { useCanvas as useCanvas22, useErrors, useWorkspace as useWorkspace16, Workspace } from "@timeax/digital-service-engine/workspace";
15369
- import { useMemo as useMemo35, useState as useState34 } from "react";
15421
+ import { useMemo as useMemo35, useState as useState35 } from "react";
15370
15422
 
15371
15423
  // backend/memory/create-backend.ts
15372
15424
  import { createMemoryWorkspaceBackend } from "@timeax/digital-service-engine/workspace";
@@ -15991,20 +16043,20 @@ var workspaceBackend = createMemoryWorkspaceBackend({
15991
16043
  });
15992
16044
 
15993
16045
  // src/index.tsx
15994
- import { jsx as jsx78, jsxs as jsxs57 } from "react/jsx-runtime";
16046
+ import { Fragment as Fragment17, jsx as jsx78, jsxs as jsxs57 } from "react/jsx-runtime";
15995
16047
  var inputRegistry = createInputRegistry();
15996
16048
  registerEntries(inputRegistry);
15997
- function WorkspaceLayout({ onShare, onPlay }) {
16049
+ function WorkspaceLayout({ onShare, onPlay, menu }) {
15998
16050
  const canvas = useCanvas22();
15999
16051
  const ws = useWorkspace16();
16000
16052
  const errors = useErrors();
16001
16053
  const bottomPanel = useBottomConsolePanel();
16002
- const [draggingServiceId, setDraggingServiceId] = useState34(null);
16054
+ const [draggingServiceId, setDraggingServiceId] = useState35(null);
16003
16055
  const serviceSummary = useMemo35(() => buildServiceSummaries(canvas, ws), [canvas.props, canvas.selectionInfo, canvas.activeId, ws.services.data]);
16004
16056
  return /* @__PURE__ */ jsx78(FallbackQuickAddProvider, { children: /* @__PURE__ */ jsx78(NodeContextMenuProvider, { children: /* @__PURE__ */ jsx78(FallbackEditorModalProvider, { children: /* @__PURE__ */ jsxs57("div", { className: "relative flex h-screen flex-col overflow-hidden *:font-display", children: [
16005
16057
  /* @__PURE__ */ jsx78("div", { className: "pointer-events-none absolute top-4 left-1/2 z-30 w-full max-w-2xl -translate-x-1/2 px-4", children: /* @__PURE__ */ jsx78("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx78(WorkspaceBootStatusSurface, { boot: ws.boot }) }) }),
16006
16058
  /* @__PURE__ */ jsxs57("div", { className: "flex grow overflow-hidden", children: [
16007
- /* @__PURE__ */ jsx78(left_default, {}),
16059
+ /* @__PURE__ */ jsx78(left_default, { menu }),
16008
16060
  /* @__PURE__ */ jsx78(
16009
16061
  CanvasPanel,
16010
16062
  {
@@ -16028,11 +16080,89 @@ function WorkspaceLayout({ onShare, onPlay }) {
16028
16080
  ) })
16029
16081
  ] }) }) }) });
16030
16082
  }
16031
- function ServiceBuilder({ backend, actor, onShare, onPlay }) {
16032
- return /* @__PURE__ */ jsx78(InputsProvider, { initialRegistry: inputRegistry, children: /* @__PURE__ */ jsx78(Workspace, { backend, actor, children: () => /* @__PURE__ */ jsx78(WorkspaceLayout, { onShare, onPlay }) }) });
16083
+ var Styles = `
16084
+ /* line theming via CSS vars (can be overridden upstream) */
16085
+ :root {
16086
+ --tree-line-color: #d1d5db; /* gray-300 */
16087
+ --tree-line-width: 1px;
16088
+ --tree-line-style: solid; /* solid | dashed | dotted */
16089
+ --elbow-v-h: 100%
16090
+ }
16091
+
16092
+ /* container for each row */
16093
+ .tree-row {
16094
+ display: flex;
16095
+ align-items: center;
16096
+ gap: 0.5rem; /* matches your gap-2 */
16097
+ /*padding: 0.25rem 0.5rem; !* py-1 px-2 *!*/
16098
+ }
16099
+
16100
+ /* left rail: N ancestor columns (width given inline) */
16101
+ .tree-rail {
16102
+ display: flex;
16103
+ position: relative;
16104
+ }
16105
+
16106
+ /* a single ancestor column; width is set inline to \`indent\` */
16107
+ .tree-col {
16108
+ position: relative;
16109
+ }
16110
+
16111
+ /* draw vertical guideline IF data-show="1" */
16112
+ .tree-col[data-show="1"]::before {
16113
+ content: "";
16114
+ position: absolute;
16115
+ top: 0;
16116
+ bottom: 0;
16117
+ left: 50%;
16118
+ transform: translateX(-50%);
16119
+ border-left: var(--tree-line-width) var(--tree-line-style) var(--tree-line-color);
16120
+ }
16121
+
16122
+ /* the elbow cell for the current node (one per row) */
16123
+ .tree-elbow {
16124
+ position: relative;
16125
+ display: flex;
16126
+ justify-content: center;
16127
+ align-items: center;
16128
+ }
16129
+
16130
+ /* vertical segment (full for mid siblings, half for last) */
16131
+ .tree-elbow::before {
16132
+ content: "";
16133
+ position: absolute;
16134
+ left: 50%;
16135
+ transform: translateX(-50%);
16136
+ top: 0;
16137
+ height: var(--elbow-v-h, 100%); /* "50%" for last sibling, else "100%" */
16138
+ border-left: var(--tree-line-width) var(--tree-line-style) var(--tree-line-color);
16139
+ }
16140
+
16141
+ /* horizontal segment (elbow) from center to the content area */
16142
+ .tree-elbow::after {
16143
+ content: "";
16144
+ position: absolute;
16145
+ top: 50%;
16146
+ left: 50%;
16147
+ transform: translateY(-50%);
16148
+ width: 50%;
16149
+ border-top: var(--tree-line-width) var(--tree-line-style) var(--tree-line-color);
16150
+ }
16151
+
16152
+ /* apply to the element that receives selectedRowClassName / rowClassName */
16153
+ .tree-row-clip {
16154
+ background-clip: content-box; /* don\u2019t paint under the left padding (indent) */
16155
+ }
16156
+
16157
+ `;
16158
+ function ServiceBuilder({ backend, actor, onShare, onPlay, menu }) {
16159
+ return /* @__PURE__ */ jsxs57(Fragment17, { children: [
16160
+ /* @__PURE__ */ jsx78("style", { children: Styles }),
16161
+ /* @__PURE__ */ jsx78(InputsProvider, { initialRegistry: inputRegistry, children: /* @__PURE__ */ jsx78(Workspace, { backend, actor, children: () => /* @__PURE__ */ jsx78(WorkspaceLayout, { onShare, onPlay, menu }) }) })
16162
+ ] });
16033
16163
  }
16034
- function ServiceBuilderWorkspace({ onShare, onPlay }) {
16035
- return /* @__PURE__ */ jsx78(ServiceBuilder, { backend: workspaceBackend, actor: workspaceActor, onShare, onPlay });
16164
+ function ServiceBuilderWorkspace({ onShare, onPlay, menu }) {
16165
+ return /* @__PURE__ */ jsx78(ServiceBuilder, { backend: workspaceBackend, actor: workspaceActor, onShare, onPlay, menu });
16036
16166
  }
16037
16167
  export {
16038
16168
  ServiceBuilder,