@camstack/ui-library 1.1.24 → 1.1.26

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.cjs CHANGED
@@ -13842,6 +13842,17 @@ function AgentStepEditor(props) {
13842
13842
  }
13843
13843
  //#endregion
13844
13844
  //#region src/composites/pipeline-tree-matrix.tsx
13845
+ /**
13846
+ * Fixed column geometry (rem). The matrix uses a CSS grid with STATIC track
13847
+ * widths so a cell's content (a status chip, a long model id) can never resize
13848
+ * a column or shift its neighbours — cell content is truncated WITHIN the fixed
13849
+ * track instead. This kills the "table dances when the last column's status
13850
+ * changes" jitter. See `min-w-0` on every grid child, which lets the child
13851
+ * shrink to the track (a grid item's automatic minimum is its content size, so
13852
+ * without this a non-wrapping model id would blow the track wider than its max).
13853
+ */
13854
+ var STEP_COL_REM = 18;
13855
+ var AGENT_COL_REM = 13;
13845
13856
  function flattenTree(nodes) {
13846
13857
  const out = [];
13847
13858
  const walk = (list, depth) => {
@@ -13921,40 +13932,58 @@ function QuickToggle({ enabled, onChange, disabled }) {
13921
13932
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: cn("inline-block h-3 w-3 rounded-full bg-white shadow transition-transform", enabled ? "translate-x-3.5" : "translate-x-0.5") })
13922
13933
  });
13923
13934
  }
13935
+ /**
13936
+ * Cell content. Kept width-stable: the status dot is `shrink-0` (reserved
13937
+ * space) and the model id truncates inside a `min-w-0` flex row, so a longer
13938
+ * model id never widens the cell — it ellipsises instead.
13939
+ */
13924
13940
  function renderCellContent(state) {
13925
13941
  if (state.kind === "enabled") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
13926
- className: "inline-flex items-center gap-1",
13942
+ className: "flex min-w-0 items-center gap-1",
13927
13943
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-emerald-500 shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13928
- className: "font-mono text-foreground",
13944
+ className: "font-mono text-foreground truncate",
13929
13945
  children: state.modelId
13930
13946
  })]
13931
13947
  });
13932
13948
  if (state.kind === "disabled") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
13933
- className: "inline-flex items-center gap-1 text-foreground-subtle",
13934
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full border border-foreground-subtle/60 shrink-0" }), "off"]
13949
+ className: "flex min-w-0 items-center gap-1 text-foreground-subtle",
13950
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full border border-foreground-subtle/60 shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13951
+ className: "truncate",
13952
+ children: "off"
13953
+ })]
13935
13954
  });
13936
13955
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
13937
- className: "inline-flex items-center gap-1 text-foreground-subtle/70",
13938
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-muted shrink-0" }), "n/a"]
13956
+ className: "flex min-w-0 items-center gap-1 text-foreground-subtle/70",
13957
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "h-1.5 w-1.5 rounded-full bg-muted shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13958
+ className: "truncate",
13959
+ children: "n/a"
13960
+ })]
13961
+ });
13962
+ }
13963
+ function cellButtonClass(state, isSelected, extra) {
13964
+ return cn("text-left text-xs hover:bg-muted/40", state.kind === "enabled" && "bg-emerald-500/5", state.kind === "na" && "bg-muted/30", isSelected && "ring-2 ring-primary ring-inset", extra);
13965
+ }
13966
+ /** Step label (slot heading + addon name + class chips), shared by both views. */
13967
+ function StepLabel({ node }) {
13968
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13969
+ className: "flex flex-col gap-1 min-w-0 flex-1",
13970
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotHeading, { slot: node.slot }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13971
+ className: "flex items-center gap-2 flex-wrap",
13972
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13973
+ className: "font-semibold truncate",
13974
+ children: node.addonName
13975
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassChips, {
13976
+ inputs: node.inputClasses,
13977
+ outputs: node.outputClasses
13978
+ })]
13979
+ })]
13939
13980
  });
13940
13981
  }
13941
- function Row({ node, depth, agents, getCellState, onCellClick, selectedCell, toggleProps }) {
13982
+ function GridRow({ node, depth, agents, getCellState, onCellClick, selectedCell, toggleProps }) {
13942
13983
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13943
- className: "sticky left-0 z-10 bg-surface px-3 py-2 border-b border-border text-xs flex items-start gap-2",
13984
+ className: "sticky left-0 z-10 bg-surface px-3 py-2 border-b border-border text-xs flex items-start gap-2 min-w-0",
13944
13985
  style: { paddingLeft: `${12 + depth * 14}px` },
13945
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13946
- className: "flex flex-col gap-1 min-w-0 flex-1",
13947
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SlotHeading, { slot: node.slot }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13948
- className: "flex items-center gap-2 flex-wrap",
13949
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13950
- className: "font-semibold truncate",
13951
- children: node.addonName
13952
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClassChips, {
13953
- inputs: node.inputClasses,
13954
- outputs: node.outputClasses
13955
- })]
13956
- })]
13957
- }), toggleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13986
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), toggleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
13958
13987
  className: "pl-2 pt-0.5 shrink-0",
13959
13988
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickToggle, {
13960
13989
  enabled: toggleProps.enabled,
@@ -13964,24 +13993,28 @@ function Row({ node, depth, agents, getCellState, onCellClick, selectedCell, tog
13964
13993
  })]
13965
13994
  }), agents.map((a) => {
13966
13995
  const state = getCellState(node.addonId, a.agentNodeId);
13967
- const isSelected = selectedCell?.addonId === node.addonId && selectedCell.agentNodeId === a.agentNodeId;
13968
13996
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
13969
13997
  type: "button",
13970
13998
  onClick: () => onCellClick(node.addonId, a.agentNodeId),
13971
- className: cn("text-left px-3 py-1.5 border-b border-l border-border text-xs hover:bg-muted/40", state.kind === "enabled" && "bg-emerald-500/5", state.kind === "na" && "bg-muted/30", isSelected && "ring-2 ring-primary ring-inset"),
13999
+ className: cellButtonClass(state, selectedCell?.addonId === node.addonId && selectedCell.agentNodeId === a.agentNodeId, "min-w-0 overflow-hidden px-3 py-1.5 border-b border-l border-border"),
13972
14000
  children: renderCellContent(state)
13973
14001
  }, a.agentNodeId);
13974
14002
  })] });
13975
14003
  }
13976
- function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
13977
- const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
13978
- const gridTemplate = `minmax(320px, 1fr) repeat(${agents.length}, minmax(160px, 220px))`;
14004
+ /**
14005
+ * Wide layout: horizontally self-scrolling matrix. The scroll lives INSIDE
14006
+ * this `overflow-auto` box (sticky first column + sticky header row) so the
14007
+ * page body never scrolls sideways. Column tracks are fixed px widths — see
14008
+ * STEP_COL_REM / AGENT_COL_REM — so the grid is static regardless of content.
14009
+ */
14010
+ function MatrixGrid({ rows, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
14011
+ const gridTemplate = `${STEP_COL_REM}rem repeat(${agents.length}, ${AGENT_COL_REM}rem)`;
13979
14012
  const showToggle = onToggleEnabled !== void 0 && agents.length === 1;
13980
14013
  const onlyAgent = agents[0]?.agentNodeId;
13981
14014
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13982
14015
  className: "overflow-auto border border-border rounded",
13983
14016
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13984
- className: "grid",
14017
+ className: "grid w-max",
13985
14018
  style: { gridTemplateColumns: gridTemplate },
13986
14019
  children: [
13987
14020
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -13989,9 +14022,9 @@ function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedC
13989
14022
  children: "Step"
13990
14023
  }),
13991
14024
  agents.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13992
- className: "sticky top-0 z-10 bg-muted/60 px-3 py-2 border-b border-l border-border",
14025
+ className: "sticky top-0 z-10 min-w-0 bg-muted/60 px-3 py-2 border-b border-l border-border",
13993
14026
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13994
- className: "text-xs font-semibold text-foreground",
14027
+ className: "text-xs font-semibold text-foreground truncate",
13995
14028
  children: a.agentNodeId
13996
14029
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
13997
14030
  className: "text-[10px] text-foreground-subtle truncate",
@@ -14000,7 +14033,7 @@ function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedC
14000
14033
  }, a.agentNodeId)),
14001
14034
  rows.map(({ node, depth }) => {
14002
14035
  const cellState = showToggle && onlyAgent !== void 0 ? getCellState(node.addonId, onlyAgent) : null;
14003
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
14036
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridRow, {
14004
14037
  node,
14005
14038
  depth,
14006
14039
  agents,
@@ -14018,6 +14051,90 @@ function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedC
14018
14051
  })
14019
14052
  });
14020
14053
  }
14054
+ /**
14055
+ * Narrow layout: one card per agent column, each listing every step stacked
14056
+ * vertically. A wide fixed matrix is unusable on a phone; here the same cells
14057
+ * (click-to-edit, status, toggle) reflow into full-width rows so nothing scrolls
14058
+ * horizontally. Feature-parity with the matrix — same `onCellClick`, same
14059
+ * `renderCellContent`, same single-agent `onToggleEnabled`.
14060
+ */
14061
+ function StackedCards({ rows, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
14062
+ const showToggle = onToggleEnabled !== void 0 && agents.length === 1;
14063
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14064
+ className: "space-y-3",
14065
+ children: agents.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14066
+ className: "border border-border rounded overflow-hidden",
14067
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14068
+ className: "bg-muted/60 px-3 py-2 border-b border-border",
14069
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14070
+ className: "text-xs font-semibold text-foreground truncate",
14071
+ children: a.agentNodeId
14072
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14073
+ className: "text-[10px] text-foreground-subtle truncate",
14074
+ children: a.engineLabel
14075
+ })]
14076
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: rows.map(({ node, depth }) => {
14077
+ const state = getCellState(node.addonId, a.agentNodeId);
14078
+ const isSelected = selectedCell?.addonId === node.addonId && selectedCell.agentNodeId === a.agentNodeId;
14079
+ const toggleProps = showToggle && state !== null ? {
14080
+ enabled: state.kind === "enabled",
14081
+ onChange: (next) => onToggleEnabled?.(node.addonId, next),
14082
+ disabled: state.kind === "na"
14083
+ } : void 0;
14084
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
14085
+ className: "border-b border-border last:border-b-0",
14086
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14087
+ className: "flex items-center gap-2",
14088
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
14089
+ type: "button",
14090
+ onClick: () => onCellClick(node.addonId, a.agentNodeId),
14091
+ className: cellButtonClass(state, isSelected, "flex flex-1 min-w-0 items-start justify-between gap-3 px-3 py-2"),
14092
+ style: { paddingLeft: `${12 + depth * 14}px` },
14093
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StepLabel, { node }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14094
+ className: "shrink-0 max-w-[45%] pt-0.5",
14095
+ children: renderCellContent(state)
14096
+ })]
14097
+ }), toggleProps && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
14098
+ className: "pr-3 shrink-0",
14099
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuickToggle, {
14100
+ enabled: toggleProps.enabled,
14101
+ onChange: toggleProps.onChange,
14102
+ disabled: toggleProps.disabled
14103
+ })
14104
+ })]
14105
+ })
14106
+ }, node.addonId);
14107
+ }) })]
14108
+ }, a.agentNodeId))
14109
+ });
14110
+ }
14111
+ function PipelineTreeMatrix({ tree, agents, getCellState, onCellClick, selectedCell, onToggleEnabled }) {
14112
+ const rows = (0, react$1.useMemo)(() => flattenTree(tree), [tree]);
14113
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
14114
+ className: "@container/matrix w-full",
14115
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14116
+ className: "@2xl/matrix:hidden",
14117
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackedCards, {
14118
+ rows,
14119
+ agents,
14120
+ getCellState,
14121
+ onCellClick,
14122
+ selectedCell,
14123
+ onToggleEnabled
14124
+ })
14125
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
14126
+ className: "hidden @2xl/matrix:block",
14127
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MatrixGrid, {
14128
+ rows,
14129
+ agents,
14130
+ getCellState,
14131
+ onCellClick,
14132
+ selectedCell,
14133
+ onToggleEnabled
14134
+ })
14135
+ })]
14136
+ });
14137
+ }
14021
14138
  //#endregion
14022
14139
  //#region src/trpc-react.ts
14023
14140
  /**
@@ -15578,8 +15695,10 @@ function PTZOverlay({ controls, mode = "overlay", showPresets, showZoom = true,
15578
15695
  const confirm = useConfirm();
15579
15696
  const [presetsOpen, setPresetsOpen] = (0, react$1.useState)(false);
15580
15697
  const [presetName, setPresetName] = (0, react$1.useState)("");
15581
- const presetsVisible = (showPresets ?? presets.length > 0) && presets.length > 0;
15582
15698
  const isPanel = mode === "panel";
15699
+ const zoomVisible = showZoom && (options?.hasZoom ?? true);
15700
+ const supportsPresets = options?.supportsPresets ?? presets.length > 0;
15701
+ const presetsVisible = (showPresets ?? supportsPresets) && supportsPresets;
15583
15702
  const presetManagementVisible = isPanel && (options?.supportsPresets ?? false);
15584
15703
  const maxPresetsReached = options?.maxPresets !== void 0 && presets.length >= options.maxPresets;
15585
15704
  const handleSavePreset = (0, react$1.useCallback)(async () => {
@@ -15609,109 +15728,112 @@ function PTZOverlay({ controls, mode = "overlay", showPresets, showZoom = true,
15609
15728
  const sepClass = isPanel ? "h-12 w-px bg-border mx-1" : "h-12 w-px bg-white/15 mx-1";
15610
15729
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15611
15730
  className: cn(containerClass, className),
15612
- children: [
15613
- error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15614
- className: "rounded bg-danger/90 px-2 py-1 text-[10px] font-medium text-white shadow-lg max-w-[200px] self-center",
15615
- children: ["PTZ: ", error]
15616
- }),
15617
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15618
- className: cn(rowClass, isPanel && "justify-center"),
15619
- children: [
15620
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15621
- className: "grid grid-cols-3 gap-0.5",
15731
+ children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15732
+ className: "rounded bg-danger/90 px-2 py-1 text-[10px] font-medium text-white shadow-lg max-w-[200px] self-center",
15733
+ children: ["PTZ: ", error]
15734
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15735
+ className: cn(rowClass, isPanel && "justify-center"),
15736
+ children: [
15737
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15738
+ className: "grid grid-cols-3 gap-0.5",
15739
+ children: [
15740
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15741
+ "aria-hidden": true,
15742
+ className: isPanel ? "h-9 w-9" : "h-7 w-7"
15743
+ }),
15744
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15745
+ direction: "up",
15746
+ icon: ArrowUp,
15747
+ variant: mode,
15748
+ onMove: move,
15749
+ onStart: startContinuous,
15750
+ onStop: stopContinuous
15751
+ }),
15752
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15753
+ "aria-hidden": true,
15754
+ className: isPanel ? "h-9 w-9" : "h-7 w-7"
15755
+ }),
15756
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15757
+ direction: "left",
15758
+ icon: ArrowLeft,
15759
+ variant: mode,
15760
+ onMove: move,
15761
+ onStart: startContinuous,
15762
+ onStop: stopContinuous
15763
+ }),
15764
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15765
+ "aria-hidden": true,
15766
+ className: isPanel ? "h-9 w-9" : "h-7 w-7"
15767
+ }),
15768
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15769
+ direction: "right",
15770
+ icon: ArrowRight,
15771
+ variant: mode,
15772
+ onMove: move,
15773
+ onStart: startContinuous,
15774
+ onStop: stopContinuous
15775
+ }),
15776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15777
+ "aria-hidden": true,
15778
+ className: isPanel ? "h-9 w-9" : "h-7 w-7"
15779
+ }),
15780
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15781
+ direction: "down",
15782
+ icon: ArrowDown,
15783
+ variant: mode,
15784
+ onMove: move,
15785
+ onStart: startContinuous,
15786
+ onStop: stopContinuous
15787
+ }),
15788
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15789
+ "aria-hidden": true,
15790
+ className: isPanel ? "h-9 w-9" : "h-7 w-7"
15791
+ })
15792
+ ]
15793
+ }),
15794
+ (zoomVisible || showHome) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: sepClass }),
15795
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15796
+ className: "flex flex-col gap-0.5",
15797
+ children: [zoomVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15798
+ type: "button",
15799
+ onClick: () => zoom("in"),
15800
+ disabled: busy,
15801
+ title: "Zoom in",
15802
+ className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15803
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomIn, { className: sideIconSize })
15804
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15805
+ type: "button",
15806
+ onClick: () => zoom("out"),
15807
+ disabled: busy,
15808
+ title: "Zoom out",
15809
+ className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15810
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomOut, { className: sideIconSize })
15811
+ })] }), showHome && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15812
+ type: "button",
15813
+ onClick: () => goHome(),
15814
+ disabled: busy,
15815
+ title: "Go home",
15816
+ className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15817
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(House, { className: sideIconSize })
15818
+ })]
15819
+ }),
15820
+ presetsVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15821
+ className: "relative",
15822
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15823
+ type: "button",
15824
+ onClick: () => setPresetsOpen((v) => !v),
15825
+ disabled: busy,
15826
+ className: cn("flex items-center gap-1 rounded px-2 text-[10px] disabled:opacity-40", isPanel ? "h-9 text-foreground hover:bg-surface-hover" : "h-7 text-white hover:bg-white/15"),
15827
+ title: "Presets",
15828
+ children: ["Presets", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, { className: cn("h-3 w-3 transition-transform", presetsOpen && "rotate-180") })]
15829
+ }), presetsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15830
+ className: cn("absolute right-0 top-full mt-1 rounded-lg shadow-xl py-1 z-20", presetManagementVisible ? "min-w-[220px]" : "min-w-[140px]", isPanel ? "bg-surface border border-border" : "border border-white/30 bg-zinc-900/95 backdrop-blur-md"),
15622
15831
  children: [
15623
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15624
- "aria-hidden": true,
15625
- className: isPanel ? "h-9 w-9" : "h-7 w-7"
15626
- }),
15627
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15628
- direction: "up",
15629
- icon: ArrowUp,
15630
- variant: mode,
15631
- onMove: move,
15632
- onStart: startContinuous,
15633
- onStop: stopContinuous
15832
+ presets.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15833
+ className: cn("px-3 py-1.5 text-[10px]", isPanel ? "text-foreground-subtle" : "text-white/60"),
15834
+ children: "No presets yet"
15634
15835
  }),
15635
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15636
- "aria-hidden": true,
15637
- className: isPanel ? "h-9 w-9" : "h-7 w-7"
15638
- }),
15639
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15640
- direction: "left",
15641
- icon: ArrowLeft,
15642
- variant: mode,
15643
- onMove: move,
15644
- onStart: startContinuous,
15645
- onStop: stopContinuous
15646
- }),
15647
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15648
- "aria-hidden": true,
15649
- className: isPanel ? "h-9 w-9" : "h-7 w-7"
15650
- }),
15651
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15652
- direction: "right",
15653
- icon: ArrowRight,
15654
- variant: mode,
15655
- onMove: move,
15656
- onStart: startContinuous,
15657
- onStop: stopContinuous
15658
- }),
15659
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15660
- "aria-hidden": true,
15661
- className: isPanel ? "h-9 w-9" : "h-7 w-7"
15662
- }),
15663
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DPadButton, {
15664
- direction: "down",
15665
- icon: ArrowDown,
15666
- variant: mode,
15667
- onMove: move,
15668
- onStart: startContinuous,
15669
- onStop: stopContinuous
15670
- }),
15671
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
15672
- "aria-hidden": true,
15673
- className: isPanel ? "h-9 w-9" : "h-7 w-7"
15674
- })
15675
- ]
15676
- }),
15677
- (showZoom || showHome) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: sepClass }),
15678
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15679
- className: "flex flex-col gap-0.5",
15680
- children: [showZoom && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15681
- type: "button",
15682
- onClick: () => zoom("in"),
15683
- disabled: busy,
15684
- title: "Zoom in",
15685
- className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15686
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomIn, { className: sideIconSize })
15687
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15688
- type: "button",
15689
- onClick: () => zoom("out"),
15690
- disabled: busy,
15691
- title: "Zoom out",
15692
- className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15693
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ZoomOut, { className: sideIconSize })
15694
- })] }), showHome && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15695
- type: "button",
15696
- onClick: () => goHome(),
15697
- disabled: busy,
15698
- title: "Go home",
15699
- className: cn("flex items-center justify-center rounded disabled:opacity-40", sideButtonSize, sideButtonHover),
15700
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(House, { className: sideIconSize })
15701
- })]
15702
- }),
15703
- presetsVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15704
- className: "relative",
15705
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15706
- type: "button",
15707
- onClick: () => setPresetsOpen((v) => !v),
15708
- disabled: busy,
15709
- className: cn("flex items-center gap-1 rounded px-2 text-[10px] disabled:opacity-40", isPanel ? "h-9 text-foreground hover:bg-surface-hover" : "h-7 text-white hover:bg-white/15"),
15710
- title: "Presets",
15711
- children: ["Presets", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, { className: cn("h-3 w-3 transition-transform", presetsOpen && "rotate-180") })]
15712
- }), presetsOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
15713
- className: cn("absolute right-0 top-full mt-1 min-w-[140px] rounded-lg shadow-xl py-1 z-20", isPanel ? "bg-surface border border-border" : "border border-white/30 bg-zinc-900/95 backdrop-blur-md"),
15714
- children: presets.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15836
+ presets.map((p) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15715
15837
  className: cn("group flex items-center", isPanel ? "hover:bg-surface-hover" : "hover:bg-white/15"),
15716
15838
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15717
15839
  type: "button",
@@ -15730,44 +15852,45 @@ function PTZOverlay({ controls, mode = "overlay", showPresets, showZoom = true,
15730
15852
  className: cn("mr-1 flex h-5 w-5 shrink-0 items-center justify-center rounded", "text-foreground-subtle hover:bg-danger/15 hover:text-danger disabled:opacity-40"),
15731
15853
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(X, { className: "h-3 w-3" })
15732
15854
  })]
15733
- }, p.id))
15734
- })]
15735
- })
15736
- ]
15737
- }),
15738
- presetManagementVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15739
- className: "flex flex-col gap-1 rounded-lg border border-border p-2",
15740
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15741
- className: "flex items-center gap-2",
15742
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15743
- type: "text",
15744
- value: presetName,
15745
- onChange: (e) => setPresetName(e.target.value),
15746
- onKeyDown: (e) => {
15747
- if (e.key === "Enter") handleSavePreset();
15748
- },
15749
- disabled: busy || maxPresetsReached,
15750
- placeholder: "New preset name",
15751
- maxLength: 64,
15752
- className: cn("min-w-0 flex-1 rounded border border-border bg-surface px-2 py-1 text-[11px] text-foreground", "placeholder:text-foreground-subtle focus:outline-none focus:ring-1 focus:ring-primary/50", "disabled:opacity-40")
15753
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
15754
- type: "button",
15755
- onClick: () => void handleSavePreset(),
15756
- disabled: busy || maxPresetsReached || presetName.trim().length === 0,
15757
- title: maxPresetsReached ? `Camera preset limit reached (${options?.maxPresets})` : "Save current position as a new preset",
15758
- className: cn("flex h-7 shrink-0 items-center gap-1 rounded px-2 text-[10px] font-medium", "bg-primary/15 text-primary hover:bg-primary/25 disabled:opacity-40"),
15759
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3 w-3" }), "Save current position"]
15855
+ }, p.id)),
15856
+ presetManagementVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
15857
+ presets.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: cn("my-1 h-px", isPanel ? "bg-border" : "bg-white/15") }),
15858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
15859
+ className: "flex items-center gap-1 px-2 py-1",
15860
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
15861
+ type: "text",
15862
+ value: presetName,
15863
+ onChange: (e) => setPresetName(e.target.value),
15864
+ onKeyDown: (e) => {
15865
+ if (e.key === "Enter") handleSavePreset();
15866
+ },
15867
+ disabled: busy || maxPresetsReached,
15868
+ placeholder: "New preset name",
15869
+ maxLength: 64,
15870
+ className: cn("min-w-0 flex-1 rounded border px-2 py-1 text-[11px]", "focus:outline-none focus:ring-1 focus:ring-primary/50 disabled:opacity-40", isPanel ? "border-border bg-surface text-foreground placeholder:text-foreground-subtle" : "border-white/30 bg-zinc-800 text-white placeholder:text-white/40")
15871
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
15872
+ type: "button",
15873
+ onClick: () => void handleSavePreset(),
15874
+ disabled: busy || maxPresetsReached || presetName.trim().length === 0,
15875
+ title: maxPresetsReached ? `Camera preset limit reached (${options?.maxPresets})` : "Save current position as a new preset",
15876
+ className: cn("flex h-7 w-7 shrink-0 items-center justify-center rounded", "bg-primary/15 text-primary hover:bg-primary/25 disabled:opacity-40"),
15877
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Plus, { className: "h-3.5 w-3.5" })
15878
+ })]
15879
+ }),
15880
+ maxPresetsReached && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15881
+ className: cn("block px-2 pb-1 text-[10px]", isPanel ? "text-foreground-subtle" : "text-white/60"),
15882
+ children: [
15883
+ "Preset limit reached (",
15884
+ options?.maxPresets,
15885
+ ")."
15886
+ ]
15887
+ })
15888
+ ] })
15889
+ ]
15760
15890
  })]
15761
- }), maxPresetsReached && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
15762
- className: "text-[10px] text-foreground-subtle",
15763
- children: [
15764
- "Camera preset limit reached (",
15765
- options?.maxPresets,
15766
- "). Delete a preset to add a new one."
15767
- ]
15768
- })]
15769
- })
15770
- ]
15891
+ })
15892
+ ]
15893
+ })]
15771
15894
  });
15772
15895
  }
15773
15896
  //#endregion
@@ -16925,6 +17048,12 @@ var useCoverSetPosition = trpc.cover.setPosition.useMutation;
16925
17048
  var useCoverSetTiltPosition = trpc.cover.setTiltPosition.useMutation;
16926
17049
  /** Generated alias around `trpc.cover.getStatus.useQuery`. */
16927
17050
  var useCoverGetStatus = trpc.cover.getStatus.useQuery;
17051
+ /** Generated alias around `trpc.dayNight.getOptions.useQuery`. */
17052
+ var useDayNightGetOptions = trpc.dayNight.getOptions.useQuery;
17053
+ /** Generated alias around `trpc.dayNight.setSettings.useMutation`. */
17054
+ var useDayNightSetSettings = trpc.dayNight.setSettings.useMutation;
17055
+ /** Generated alias around `trpc.dayNight.getStatus.useQuery`. */
17056
+ var useDayNightGetStatus = trpc.dayNight.getStatus.useQuery;
16928
17057
  /** Generated alias around `trpc.decoder.supportsCodec.useQuery`. */
16929
17058
  var useDecoderSupportsCodec = trpc.decoder.supportsCodec.useQuery;
16930
17059
  /** Generated alias around `trpc.decoder.getInfo.useQuery`. */
@@ -17261,6 +17390,12 @@ var useHumidifierGetStatus = trpc.humidifier.getStatus.useQuery;
17261
17390
  var useHumiditySensorGetStatus = trpc.humiditySensor.getStatus.useQuery;
17262
17391
  /** Generated alias around `trpc.image.getStatus.useQuery`. */
17263
17392
  var useImageGetStatus = trpc.image.getStatus.useQuery;
17393
+ /** Generated alias around `trpc.imageSettings.getOptions.useQuery`. */
17394
+ var useImageSettingsGetOptions = trpc.imageSettings.getOptions.useQuery;
17395
+ /** Generated alias around `trpc.imageSettings.setSettings.useMutation`. */
17396
+ var useImageSettingsSetSettings = trpc.imageSettings.setSettings.useMutation;
17397
+ /** Generated alias around `trpc.imageSettings.getStatus.useQuery`. */
17398
+ var useImageSettingsGetStatus = trpc.imageSettings.getStatus.useQuery;
17264
17399
  /** Generated alias around `trpc.integrations.list.useQuery`. */
17265
17400
  var useIntegrationsList = trpc.integrations.list.useQuery;
17266
17401
  /** Generated alias around `trpc.integrations.get.useQuery`. */
@@ -41228,7 +41363,6 @@ var LEVEL_SEVERITY = {
41228
41363
  error: 3
41229
41364
  };
41230
41365
  var LEVEL_OPTIONS = [
41231
- "all",
41232
41366
  "debug",
41233
41367
  "info",
41234
41368
  "warn",
@@ -41243,7 +41377,7 @@ function passesLevel(logLevel, filterLevel) {
41243
41377
  function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: propsDeviceId, containerDeviceId: propsContainerDeviceId, integrationId: propsIntegrationId, requestId: propsRequestId, level: initialLevel, maxHeight = "max-h-96", showScope = false, showFilters = true, limit = 100, liveBuffer: externalBuffer, onClose, className }) {
41244
41378
  const [localAddonId, setLocalAddonId] = (0, react$1.useState)("");
41245
41379
  const [localDeviceId, setLocalDeviceId] = (0, react$1.useState)("");
41246
- const [levelFilter, setLevelFilter] = (0, react$1.useState)(initialLevel);
41380
+ const [levelFilter, setLevelFilter] = (0, react$1.useState)(initialLevel ?? "info");
41247
41381
  const agentId = propsAgentId;
41248
41382
  const addonId = propsAddonId ?? (localAddonId || void 0);
41249
41383
  const deviceId = propsDeviceId ?? (localDeviceId ? Number(localDeviceId) : void 0);
@@ -41444,11 +41578,11 @@ function LogStream({ agentId: propsAgentId, addonId: propsAddonId, deviceId: pro
41444
41578
  }),
41445
41579
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
41446
41580
  className: "flex rounded border border-border overflow-hidden text-[9px]",
41447
- children: LEVEL_OPTIONS.map((l) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
41581
+ children: LEVEL_OPTIONS.map((l, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
41448
41582
  type: "button",
41449
- onClick: () => setLevelFilter(l === "all" ? void 0 : l),
41450
- className: cn("px-1.5 py-0.5 capitalize transition-colors", l === "all" && !levelFilter || levelFilter === l ? "bg-primary/10 text-primary" : "text-foreground-subtle hover:bg-surface-hover", l !== "all" && "border-l border-border"),
41451
- children: [l, l !== "all" ? "+" : ""]
41583
+ onClick: () => setLevelFilter(l),
41584
+ className: cn("px-1.5 py-0.5 capitalize transition-colors", levelFilter === l ? "bg-primary/10 text-primary" : "text-foreground-subtle hover:bg-surface-hover", i > 0 && "border-l border-border"),
41585
+ children: [l, "+"]
41452
41586
  }, l))
41453
41587
  }),
41454
41588
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
@@ -45373,6 +45507,9 @@ exports.useCoverSetPosition = useCoverSetPosition;
45373
45507
  exports.useCoverSetTiltPosition = useCoverSetTiltPosition;
45374
45508
  exports.useCoverStop = useCoverStop;
45375
45509
  exports.useCustomFieldRenderer = useCustomFieldRenderer;
45510
+ exports.useDayNightGetOptions = useDayNightGetOptions;
45511
+ exports.useDayNightGetStatus = useDayNightGetStatus;
45512
+ exports.useDayNightSetSettings = useDayNightSetSettings;
45376
45513
  exports.useDebouncedString = useDebouncedString;
45377
45514
  exports.useDecoderCreateSession = useDecoderCreateSession;
45378
45515
  exports.useDecoderDestroySession = useDecoderDestroySession;
@@ -45563,6 +45700,9 @@ exports.useHumidifierSetOn = useHumidifierSetOn;
45563
45700
  exports.useHumidifierSetTargetHumidity = useHumidifierSetTargetHumidity;
45564
45701
  exports.useHumiditySensorGetStatus = useHumiditySensorGetStatus;
45565
45702
  exports.useImageGetStatus = useImageGetStatus;
45703
+ exports.useImageSettingsGetOptions = useImageSettingsGetOptions;
45704
+ exports.useImageSettingsGetStatus = useImageSettingsGetStatus;
45705
+ exports.useImageSettingsSetSettings = useImageSettingsSetSettings;
45566
45706
  exports.useIntegrationsCreate = useIntegrationsCreate;
45567
45707
  exports.useIntegrationsDelete = useIntegrationsDelete;
45568
45708
  exports.useIntegrationsGet = useIntegrationsGet;