@nice-code/action 0.3.2 → 0.4.0

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.
@@ -1957,7 +1957,7 @@ class ActionDevtoolsCore {
1957
1957
  }
1958
1958
  }
1959
1959
  // src/devtools/browser/NiceActionDevtools.tsx
1960
- import { useEffect as useEffect4, useMemo as useMemo3, useState as useState9 } from "react";
1960
+ import { useEffect as useEffect4, useId, useMemo as useMemo3, useReducer, useState as useState10 } from "react";
1961
1961
 
1962
1962
  // src/devtools/core/devtools_colors.ts
1963
1963
  var DEVTOOL_COLOR_SEMANTIC_ERROR = "#FF5C5C";
@@ -3090,7 +3090,13 @@ function FullErrorContent({
3090
3090
  function CompactErrorContent({ value, color }) {
3091
3091
  if (isNiceErrorJson(value)) {
3092
3092
  return /* @__PURE__ */ jsxDEV5("div", {
3093
- style: { display: "flex", flexDirection: "column", gap: "1em" },
3093
+ style: {
3094
+ display: "flex",
3095
+ flexDirection: "column",
3096
+ gap: "1em",
3097
+ width: "100%",
3098
+ minWidth: 0
3099
+ },
3094
3100
  children: [
3095
3101
  /* @__PURE__ */ jsxDEV5("div", {
3096
3102
  style: {
@@ -3100,19 +3106,39 @@ function CompactErrorContent({ value, color }) {
3100
3106
  fontWeight: 600,
3101
3107
  lineHeight: "1.4",
3102
3108
  wordBreak: "break-word",
3109
+ overflowWrap: "anywhere",
3103
3110
  textAlign: "left"
3104
3111
  },
3105
3112
  children: value.message
3106
3113
  }, undefined, false, undefined, this),
3107
3114
  /* @__PURE__ */ jsxDEV5("div", {
3108
- style: { display: "flex", flexWrap: "wrap", gap: "6px", alignItems: "center" },
3115
+ style: {
3116
+ display: "flex",
3117
+ flexWrap: "wrap",
3118
+ gap: "6px",
3119
+ alignItems: "center",
3120
+ minWidth: 0
3121
+ },
3109
3122
  children: [
3110
3123
  value.ids.map((id) => /* @__PURE__ */ jsxDEV5("span", {
3111
- style: { color, fontFamily: MONO3, fontSize: "10px", opacity: 0.8 },
3124
+ style: {
3125
+ color,
3126
+ fontFamily: MONO3,
3127
+ fontSize: "10px",
3128
+ opacity: 0.8,
3129
+ wordBreak: "break-word",
3130
+ overflowWrap: "anywhere"
3131
+ },
3112
3132
  children: id
3113
3133
  }, id, false, undefined, this)),
3114
3134
  /* @__PURE__ */ jsxDEV5("span", {
3115
- style: { color: DEVTOOL_COLOR_TEXT_MUTED, fontFamily: MONO3, fontSize: "10px" },
3135
+ style: {
3136
+ color: DEVTOOL_COLOR_TEXT_MUTED,
3137
+ fontFamily: MONO3,
3138
+ fontSize: "10px",
3139
+ wordBreak: "break-word",
3140
+ overflowWrap: "anywhere"
3141
+ },
3116
3142
  children: value.def.domain
3117
3143
  }, undefined, false, undefined, this)
3118
3144
  ]
@@ -3129,7 +3155,8 @@ function CompactErrorContent({ value, color }) {
3129
3155
  fontFamily: SANS2,
3130
3156
  fontSize: "12px",
3131
3157
  lineHeight: "1.4",
3132
- wordBreak: "break-word"
3158
+ wordBreak: "break-word",
3159
+ overflowWrap: "anywhere"
3133
3160
  },
3134
3161
  children: text
3135
3162
  }, undefined, false, undefined, this);
@@ -3254,7 +3281,11 @@ function Tooltip({
3254
3281
  pointerEvents: "none",
3255
3282
  maxWidth: resolvedMaxWidth != null ? `${resolvedMaxWidth}px` : undefined,
3256
3283
  maxHeight: `${maxHeight}px`,
3257
- overflowY: "auto"
3284
+ overflowY: "auto",
3285
+ overflowX: "hidden",
3286
+ whiteSpace: "normal",
3287
+ wordBreak: "break-word",
3288
+ overflowWrap: "anywhere"
3258
3289
  },
3259
3290
  children: [
3260
3291
  resolvedTitle != null && /* @__PURE__ */ jsxDEV6("div", {
@@ -4590,27 +4621,49 @@ function ActionEntryRow({
4590
4621
  /* @__PURE__ */ jsxDEV19("div", {
4591
4622
  style: { flex: 1 }
4592
4623
  }, undefined, false, undefined, this),
4593
- /* @__PURE__ */ jsxDEV19("span", {
4624
+ /* @__PURE__ */ jsxDEV19("div", {
4594
4625
  style: {
4595
- color: DEVTOOL_COLOR_SEMANTIC_METADATA,
4596
- fontSize: "10px",
4597
- letterSpacing: "0.02em",
4598
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
4599
- flexShrink: 0
4626
+ display: "flex",
4627
+ flexDirection: "column",
4628
+ alignItems: "end",
4629
+ gap: "0.35em",
4630
+ marginTop: "-0.2em",
4631
+ marginRight: "-0.2em"
4600
4632
  },
4601
- children: timestamp
4602
- }, undefined, false, undefined, this),
4603
- /* @__PURE__ */ jsxDEV19("span", {
4604
- style: { color: DEVTOOL_COLOR_SEMANTIC_WARNING, fontSize: "11px", flexShrink: 0 },
4605
- children: /* @__PURE__ */ jsxDEV19(DurationDisplay, {
4606
- entry
4607
- }, undefined, false, undefined, this)
4608
- }, undefined, false, undefined, this)
4633
+ children: [
4634
+ /* @__PURE__ */ jsxDEV19("span", {
4635
+ style: {
4636
+ display: "flex",
4637
+ color: DEVTOOL_COLOR_SEMANTIC_METADATA,
4638
+ fontSize: "10px",
4639
+ lineHeight: "1em",
4640
+ letterSpacing: "0.02em",
4641
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
4642
+ flexShrink: 0,
4643
+ opacity: 0.7
4644
+ },
4645
+ children: timestamp
4646
+ }, undefined, false, undefined, this),
4647
+ /* @__PURE__ */ jsxDEV19("span", {
4648
+ style: {
4649
+ display: "flex",
4650
+ color: DEVTOOL_COLOR_SEMANTIC_WARNING,
4651
+ lineHeight: "1em",
4652
+ fontSize: "11px",
4653
+ opacity: 0.9,
4654
+ flexShrink: 0
4655
+ },
4656
+ children: /* @__PURE__ */ jsxDEV19(DurationDisplay, {
4657
+ entry
4658
+ }, undefined, false, undefined, this)
4659
+ }, undefined, false, undefined, this)
4660
+ ]
4661
+ }, undefined, true, undefined, this)
4609
4662
  ]
4610
4663
  }, undefined, true, undefined, this)
4611
4664
  ]
4612
4665
  }, undefined, true, undefined, this),
4613
- hasBottomContent && /* @__PURE__ */ jsxDEV19("div", {
4666
+ hasBottomContent && (childEntries?.length ?? 0) > 0 && /* @__PURE__ */ jsxDEV19("div", {
4614
4667
  style: {
4615
4668
  display: "flex",
4616
4669
  flexWrap: "wrap",
@@ -4821,29 +4874,34 @@ function ActionList({
4821
4874
  }
4822
4875
 
4823
4876
  // src/devtools/browser/components/PanelChrome.tsx
4877
+ import { useState as useState9 } from "react";
4824
4878
  import { jsxDEV as jsxDEV21 } from "react/jsx-dev-runtime";
4879
+ var MONO_FONT = "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace";
4880
+ var SANS_FONT = "ui-sans-serif, system-ui, sans-serif";
4825
4881
  var DOCKED_SIZE_MIN = 140;
4826
4882
  var POSITION_GRID = [
4827
- ["top-left", "dock-top", "top-right"],
4883
+ [null, "dock-top", null],
4828
4884
  ["dock-left", null, "dock-right"],
4829
- ["bottom-left", "dock-bottom", "bottom-right"]
4885
+ [null, "dock-bottom", null]
4830
4886
  ];
4831
4887
  function getDockSide(pos) {
4832
- if (pos === "dock-top")
4833
- return "top";
4834
- if (pos === "dock-bottom")
4835
- return "bottom";
4836
- if (pos === "dock-left")
4837
- return "left";
4838
- if (pos === "dock-right")
4839
- return "right";
4840
- return null;
4888
+ switch (pos) {
4889
+ case "dock-top":
4890
+ return "top";
4891
+ case "dock-left":
4892
+ return "left";
4893
+ case "dock-right":
4894
+ return "right";
4895
+ default:
4896
+ return "bottom";
4897
+ }
4841
4898
  }
4842
4899
  function PanelHeader({
4843
4900
  position,
4844
4901
  onPositionChange,
4845
4902
  onClose,
4846
- onClear
4903
+ onClear,
4904
+ openOthers
4847
4905
  }) {
4848
4906
  return /* @__PURE__ */ jsxDEV21("div", {
4849
4907
  style: {
@@ -4851,15 +4909,56 @@ function PanelHeader({
4851
4909
  alignItems: "center",
4852
4910
  justifyContent: "space-between",
4853
4911
  padding: "8px 12px",
4912
+ gap: "10px",
4854
4913
  background: DEVTOOL_SECTION_BACKGROUND,
4855
4914
  borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`,
4856
4915
  flexShrink: 0
4857
4916
  },
4858
4917
  children: [
4859
- /* @__PURE__ */ jsxDEV21("span", {
4860
- style: { color: DEVTOOL_COLOR_SEMANTIC_SYSTEM, fontWeight: "bold", fontSize: "11px" },
4861
- children: "⚡ nice-action devtools"
4862
- }, undefined, false, undefined, this),
4918
+ /* @__PURE__ */ jsxDEV21("div", {
4919
+ style: { display: "flex", alignItems: "center", gap: "8px", minWidth: 0 },
4920
+ children: [
4921
+ /* @__PURE__ */ jsxDEV21("span", {
4922
+ style: {
4923
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
4924
+ fontWeight: "bold",
4925
+ fontSize: "11px",
4926
+ whiteSpace: "nowrap"
4927
+ },
4928
+ children: "⚡ action"
4929
+ }, undefined, false, undefined, this),
4930
+ openOthers?.map((item) => /* @__PURE__ */ jsxDEV21("button", {
4931
+ onClick: item.onOpen,
4932
+ title: `Open ${item.label} devtools`,
4933
+ style: {
4934
+ display: "flex",
4935
+ alignItems: "center",
4936
+ gap: "4px",
4937
+ background: DEVTOOL_LIST_BASE_BACKGROUND,
4938
+ border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
4939
+ borderRadius: "999px",
4940
+ color: DEVTOOL_COLOR_TEXT_MUTED,
4941
+ cursor: "pointer",
4942
+ fontSize: "10px",
4943
+ padding: "1px 7px 1px 6px",
4944
+ fontFamily: SANS_FONT,
4945
+ whiteSpace: "nowrap"
4946
+ },
4947
+ children: [
4948
+ /* @__PURE__ */ jsxDEV21("span", {
4949
+ children: item.icon
4950
+ }, undefined, false, undefined, this),
4951
+ /* @__PURE__ */ jsxDEV21("span", {
4952
+ children: item.label
4953
+ }, undefined, false, undefined, this),
4954
+ item.badge != null && /* @__PURE__ */ jsxDEV21("span", {
4955
+ style: { color: DEVTOOL_COLOR_SEMANTIC_SYSTEM },
4956
+ children: item.badge
4957
+ }, undefined, false, undefined, this)
4958
+ ]
4959
+ }, item.id, true, undefined, this))
4960
+ ]
4961
+ }, undefined, true, undefined, this),
4863
4962
  /* @__PURE__ */ jsxDEV21("div", {
4864
4963
  style: { display: "flex", gap: "10px", alignItems: "center" },
4865
4964
  children: [
@@ -4969,6 +5068,195 @@ function ResizeHandle({
4969
5068
  }
4970
5069
  }, undefined, false, undefined, this);
4971
5070
  }
5071
+ var SPLIT_RATIO_MIN = 0.15;
5072
+ var SPLIT_RATIO_MAX = 0.85;
5073
+ function SplitHandle({
5074
+ horizontal,
5075
+ onRatioChange
5076
+ }) {
5077
+ const [hovered, setHovered] = useState9(false);
5078
+ const onMouseDown = (e) => {
5079
+ e.preventDefault();
5080
+ const container = e.currentTarget.parentElement;
5081
+ if (container == null)
5082
+ return;
5083
+ const onMove = (me) => {
5084
+ const rect = container.getBoundingClientRect();
5085
+ const ratio = horizontal ? (rect.right - me.clientX) / rect.width : (rect.bottom - me.clientY) / rect.height;
5086
+ onRatioChange(Math.max(SPLIT_RATIO_MIN, Math.min(SPLIT_RATIO_MAX, ratio)));
5087
+ };
5088
+ const onUp = () => {
5089
+ window.removeEventListener("mousemove", onMove);
5090
+ window.removeEventListener("mouseup", onUp);
5091
+ };
5092
+ window.addEventListener("mousemove", onMove);
5093
+ window.addEventListener("mouseup", onUp);
5094
+ };
5095
+ return /* @__PURE__ */ jsxDEV21("div", {
5096
+ onMouseDown,
5097
+ onMouseEnter: () => setHovered(true),
5098
+ onMouseLeave: () => setHovered(false),
5099
+ style: {
5100
+ flex: "0 0 5px",
5101
+ alignSelf: "stretch",
5102
+ cursor: horizontal ? "ew-resize" : "ns-resize",
5103
+ background: hovered ? DEVTOOL_COLOR_SEMANTIC_SYSTEM : "transparent",
5104
+ opacity: hovered ? 0.6 : 1,
5105
+ zIndex: 5
5106
+ }
5107
+ }, undefined, false, undefined, this);
5108
+ }
5109
+ function DevtoolsLauncher({ items }) {
5110
+ return /* @__PURE__ */ jsxDEV21("div", {
5111
+ style: {
5112
+ position: "fixed",
5113
+ bottom: "16px",
5114
+ right: "16px",
5115
+ zIndex: 2147483647,
5116
+ display: "flex",
5117
+ fontFamily: MONO_FONT,
5118
+ fontSize: "12px"
5119
+ },
5120
+ children: /* @__PURE__ */ jsxDEV21("div", {
5121
+ style: {
5122
+ display: "flex",
5123
+ background: DEVTOOL_SECTION_BACKGROUND,
5124
+ border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
5125
+ borderRadius: "6px",
5126
+ overflow: "hidden",
5127
+ boxShadow: "0 8px 24px rgba(0,0,0,0.35)"
5128
+ },
5129
+ children: items.map((item, i) => /* @__PURE__ */ jsxDEV21("button", {
5130
+ onClick: item.onOpen,
5131
+ style: {
5132
+ display: "flex",
5133
+ alignItems: "center",
5134
+ gap: "5px",
5135
+ background: "transparent",
5136
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
5137
+ border: "none",
5138
+ borderLeft: i > 0 ? `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}` : "none",
5139
+ cursor: "pointer",
5140
+ padding: "6px 11px",
5141
+ fontFamily: MONO_FONT,
5142
+ fontSize: "12px",
5143
+ lineHeight: "1.5"
5144
+ },
5145
+ children: [
5146
+ /* @__PURE__ */ jsxDEV21("span", {
5147
+ children: item.icon
5148
+ }, undefined, false, undefined, this),
5149
+ /* @__PURE__ */ jsxDEV21("span", {
5150
+ children: item.label
5151
+ }, undefined, false, undefined, this),
5152
+ item.badge != null && /* @__PURE__ */ jsxDEV21("span", {
5153
+ style: { color: DEVTOOL_COLOR_SEMANTIC_SYSTEM },
5154
+ children: item.badge
5155
+ }, undefined, false, undefined, this)
5156
+ ]
5157
+ }, item.id, true, undefined, this))
5158
+ }, undefined, false, undefined, this)
5159
+ }, undefined, false, undefined, this);
5160
+ }
5161
+
5162
+ // src/devtools/browser/devtools_dock.ts
5163
+ var GLOBAL_KEY = "__NICE_DEVTOOLS_DOCK__";
5164
+ var VERSION = 2;
5165
+ function createCoordinator() {
5166
+ const panels = new Map;
5167
+ const listeners = new Set;
5168
+ function toRef(panel) {
5169
+ return {
5170
+ id: panel.id,
5171
+ label: panel.label,
5172
+ icon: panel.icon,
5173
+ badge: panel.badge,
5174
+ onOpen: panel.onOpen
5175
+ };
5176
+ }
5177
+ function applyBodyMargins() {
5178
+ if (typeof document === "undefined")
5179
+ return;
5180
+ const margins = { top: 0, bottom: 0, left: 0, right: 0 };
5181
+ for (const panel of panels.values()) {
5182
+ if (panel.open)
5183
+ margins[panel.side] += panel.size;
5184
+ }
5185
+ const sides = ["top", "bottom", "left", "right"];
5186
+ for (const side of sides) {
5187
+ if (margins[side] > 0) {
5188
+ document.body.style.setProperty(`margin-${side}`, `${margins[side]}px`);
5189
+ } else {
5190
+ document.body.style.removeProperty(`margin-${side}`);
5191
+ }
5192
+ }
5193
+ }
5194
+ function notify() {
5195
+ applyBodyMargins();
5196
+ for (const listener of listeners)
5197
+ listener();
5198
+ }
5199
+ return {
5200
+ version: VERSION,
5201
+ register(panel) {
5202
+ panels.set(panel.id, { ...panel });
5203
+ notify();
5204
+ return () => {
5205
+ panels.delete(panel.id);
5206
+ notify();
5207
+ };
5208
+ },
5209
+ update(id, next) {
5210
+ const existing = panels.get(id);
5211
+ if (existing == null)
5212
+ return;
5213
+ if (existing.side === next.side && existing.size === next.size && existing.open === next.open && existing.badge === next.badge) {
5214
+ return;
5215
+ }
5216
+ panels.set(id, { ...existing, ...next });
5217
+ notify();
5218
+ },
5219
+ getView(id) {
5220
+ const list = [...panels.values()];
5221
+ const anyOpen = list.some((p) => p.open);
5222
+ const firstId = list.length > 0 ? list[0].id : null;
5223
+ let dockOffset = 0;
5224
+ const self = panels.get(id);
5225
+ if (self != null && self.open) {
5226
+ for (const panel of list) {
5227
+ if (panel.id === id)
5228
+ break;
5229
+ if (panel.open && panel.side === self.side)
5230
+ dockOffset += panel.size;
5231
+ }
5232
+ }
5233
+ return {
5234
+ dockOffset,
5235
+ anyOpen,
5236
+ isPrimary: id === firstId,
5237
+ devtools: list.map(toRef),
5238
+ otherClosed: list.filter((p) => !p.open && p.id !== id).map(toRef)
5239
+ };
5240
+ },
5241
+ subscribe(listener) {
5242
+ listeners.add(listener);
5243
+ return () => {
5244
+ listeners.delete(listener);
5245
+ };
5246
+ }
5247
+ };
5248
+ }
5249
+ function getDevtoolsDockCoordinator() {
5250
+ if (typeof window === "undefined")
5251
+ return createCoordinator();
5252
+ const host = window;
5253
+ const existing = host[GLOBAL_KEY];
5254
+ if (existing != null && existing.version === VERSION)
5255
+ return existing;
5256
+ const created = createCoordinator();
5257
+ host[GLOBAL_KEY] = created;
5258
+ return created;
5259
+ }
4972
5260
 
4973
5261
  // src/devtools/browser/NiceActionDevtools.tsx
4974
5262
  import { jsxDEV as jsxDEV22, Fragment as Fragment10 } from "react/jsx-dev-runtime";
@@ -5009,18 +5297,27 @@ if (typeof document !== "undefined" && !document.getElementById("__nice-action-d
5009
5297
  var PREFS_KEY = "__nice-action-devtools-prefs";
5010
5298
  var DOCKED_HEIGHT_DEFAULT = 320;
5011
5299
  var DOCKED_WIDTH_DEFAULT = 420;
5300
+ var DETAIL_RATIO_DEFAULT = 0.5;
5301
+ var DOCK_POSITIONS = ["dock-bottom", "dock-top", "dock-left", "dock-right"];
5302
+ function isDockPosition(value) {
5303
+ return typeof value === "string" && DOCK_POSITIONS.includes(value);
5304
+ }
5012
5305
  function readPrefs(defaultPosition, initialOpen) {
5013
5306
  const fallback = {
5014
5307
  position: defaultPosition,
5015
5308
  isOpen: initialOpen,
5016
5309
  dockedHeight: DOCKED_HEIGHT_DEFAULT,
5017
- dockedWidth: DOCKED_WIDTH_DEFAULT
5310
+ dockedWidth: DOCKED_WIDTH_DEFAULT,
5311
+ detailRatio: DETAIL_RATIO_DEFAULT
5018
5312
  };
5019
5313
  try {
5020
5314
  if (typeof localStorage === "undefined")
5021
5315
  return fallback;
5022
5316
  const stored = localStorage.getItem(PREFS_KEY);
5023
- return stored != null ? { ...fallback, ...JSON.parse(stored) } : fallback;
5317
+ const merged = stored != null ? { ...fallback, ...JSON.parse(stored) } : fallback;
5318
+ if (!isDockPosition(merged.position))
5319
+ merged.position = defaultPosition;
5320
+ return merged;
5024
5321
  } catch (_e) {
5025
5322
  return fallback;
5026
5323
  }
@@ -5070,12 +5367,12 @@ function NiceActionDevtools(props) {
5070
5367
  }
5071
5368
  function NiceActionDevtools_Panel({
5072
5369
  core,
5073
- position: defaultPosition = "bottom-right",
5370
+ position: defaultPosition = "dock-bottom",
5074
5371
  initialOpen = false
5075
5372
  }) {
5076
- const [prefs, setPrefsRaw] = useState9(() => readPrefs(defaultPosition, initialOpen));
5077
- const [entries, setEntries] = useState9([]);
5078
- const [selectedCuid, setSelectedCuid] = useState9(null);
5373
+ const [prefs, setPrefsRaw] = useState10(() => readPrefs(defaultPosition, initialOpen));
5374
+ const [entries, setEntries] = useState10([]);
5375
+ const [selectedCuid, setSelectedCuid] = useState10(null);
5079
5376
  useEffect4(() => core.subscribe(setEntries), [core]);
5080
5377
  const groups = useMemo3(() => {
5081
5378
  const byCuid = new Map(entries.map((e) => [e.cuid, e]));
@@ -5112,44 +5409,37 @@ function NiceActionDevtools_Panel({
5112
5409
  return next;
5113
5410
  });
5114
5411
  };
5115
- const { position, isOpen, dockedHeight, dockedWidth } = prefs;
5412
+ const { position, isOpen, dockedHeight, dockedWidth, detailRatio } = prefs;
5116
5413
  const dockSide = getDockSide(position);
5117
5414
  const isHorizDock = dockSide === "top" || dockSide === "bottom";
5118
5415
  const dockedSize = isHorizDock ? dockedHeight : dockedWidth;
5119
5416
  const selectedEntry = selectedCuid != null ? entries.find((e) => e.cuid === selectedCuid) : null;
5120
5417
  const runningCount = entries.filter((e) => e.status === "running").length;
5418
+ const dock = useMemo3(() => getDevtoolsDockCoordinator(), []);
5419
+ const panelId = useId();
5420
+ const [, bumpView] = useReducer((n) => n + 1, 0);
5421
+ const badge = runningCount > 0 ? `${runningCount}●` : undefined;
5121
5422
  useEffect4(() => {
5122
- const sides = ["top", "bottom", "left", "right"];
5123
- const clearAll = () => {
5124
- sides.forEach((s) => {
5125
- document.body.style.removeProperty(`margin-${s}`);
5126
- });
5423
+ const unregister = dock.register({
5424
+ id: panelId,
5425
+ label: "actions",
5426
+ icon: "⚡",
5427
+ side: dockSide,
5428
+ size: dockedSize,
5429
+ open: isOpen,
5430
+ badge,
5431
+ onOpen: () => setPrefs({ isOpen: true })
5432
+ });
5433
+ const unsubscribe = dock.subscribe(bumpView);
5434
+ return () => {
5435
+ unregister();
5436
+ unsubscribe();
5127
5437
  };
5128
- if (!isOpen || dockSide == null) {
5129
- clearAll();
5130
- return clearAll;
5131
- }
5132
- clearAll();
5133
- document.body.style.setProperty(`margin-${dockSide}`, `${dockedSize}px`);
5134
- return clearAll;
5135
- }, [dockSide, isOpen, dockedSize]);
5136
- const closedAnchor = (() => {
5137
- switch (position) {
5138
- case "dock-bottom":
5139
- return { bottom: "16px", right: "16px" };
5140
- case "dock-top":
5141
- return { top: "16px", right: "16px" };
5142
- case "dock-left":
5143
- return { top: "16px", left: "16px" };
5144
- case "dock-right":
5145
- return { top: "16px", right: "16px" };
5146
- default:
5147
- return {
5148
- ...position.includes("right") ? { right: "16px" } : { left: "16px" },
5149
- ...position.includes("bottom") ? { bottom: "16px" } : { top: "16px" }
5150
- };
5151
- }
5152
- })();
5438
+ }, [dock, panelId]);
5439
+ useEffect4(() => {
5440
+ dock.update(panelId, { side: dockSide, size: dockedSize, open: isOpen, badge });
5441
+ }, [dock, panelId, dockSide, dockedSize, isOpen, badge]);
5442
+ const view = dock.getView(panelId);
5153
5443
  const baseStyle = {
5154
5444
  position: "fixed",
5155
5445
  zIndex: 2147483647,
@@ -5157,36 +5447,14 @@ function NiceActionDevtools_Panel({
5157
5447
  fontSize: "12px"
5158
5448
  };
5159
5449
  if (!isOpen) {
5160
- return /* @__PURE__ */ jsxDEV22("button", {
5161
- onClick: () => setPrefs({ isOpen: true }),
5162
- style: {
5163
- ...baseStyle,
5164
- ...closedAnchor,
5165
- background: DEVTOOL_SECTION_BACKGROUND,
5166
- color: DEVTOOL_COLOR_TEXT_SECONDARY,
5167
- border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
5168
- borderRadius: "6px",
5169
- padding: "5px 10px",
5170
- cursor: "pointer",
5171
- lineHeight: "1.5"
5172
- },
5173
- children: [
5174
- "⚡ actions",
5175
- runningCount > 0 && /* @__PURE__ */ jsxDEV22("span", {
5176
- style: {
5177
- marginLeft: "6px",
5178
- color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
5179
- animation: "__nice-action-pulse 1.2s ease-in-out infinite"
5180
- },
5181
- children: [
5182
- runningCount,
5183
- " running"
5184
- ]
5185
- }, undefined, true, undefined, this)
5186
- ]
5187
- }, undefined, true, undefined, this);
5450
+ if (view.isPrimary && !view.anyOpen) {
5451
+ return /* @__PURE__ */ jsxDEV22(DevtoolsLauncher, {
5452
+ items: view.devtools
5453
+ }, undefined, false, undefined, this);
5454
+ }
5455
+ return null;
5188
5456
  }
5189
- const panelStyle = dockSide != null ? {
5457
+ const panelStyle = {
5190
5458
  ...baseStyle,
5191
5459
  background: DEVTOOL_LIST_BASE_BACKGROUND,
5192
5460
  border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
@@ -5195,44 +5463,7 @@ function NiceActionDevtools_Panel({
5195
5463
  flexDirection: "column",
5196
5464
  boxShadow: "0 -4px 24px rgba(0,0,0,0.4)",
5197
5465
  overflow: "hidden",
5198
- ...dockSide === "bottom" ? {
5199
- bottom: 0,
5200
- left: 0,
5201
- right: 0,
5202
- height: `${dockedSize}px`,
5203
- borderRadius: "8px 8px 0 0"
5204
- } : dockSide === "top" ? {
5205
- top: 0,
5206
- left: 0,
5207
- right: 0,
5208
- height: `${dockedSize}px`,
5209
- borderRadius: "0 0 8px 8px"
5210
- } : dockSide === "left" ? {
5211
- top: 0,
5212
- left: 0,
5213
- bottom: 0,
5214
- width: `${dockedSize}px`,
5215
- borderRadius: "0 8px 8px 0"
5216
- } : {
5217
- top: 0,
5218
- right: 0,
5219
- bottom: 0,
5220
- width: `${dockedSize}px`,
5221
- borderRadius: "8px 0 0 8px"
5222
- }
5223
- } : {
5224
- ...baseStyle,
5225
- ...closedAnchor,
5226
- width: "460px",
5227
- maxHeight: "560px",
5228
- background: DEVTOOL_LIST_BASE_BACKGROUND,
5229
- border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
5230
- borderRadius: "10px",
5231
- color: DEVTOOL_COLOR_TEXT_SECONDARY,
5232
- display: "flex",
5233
- flexDirection: "column",
5234
- boxShadow: "0 25px 50px rgba(0,0,0,0.5)",
5235
- overflow: "hidden"
5466
+ ...dockSide === "bottom" ? { bottom: view.dockOffset, left: 0, right: 0, height: `${dockedSize}px`, borderRadius: "8px 8px 0 0" } : dockSide === "top" ? { top: view.dockOffset, left: 0, right: 0, height: `${dockedSize}px`, borderRadius: "0 0 8px 8px" } : dockSide === "left" ? { top: 0, left: view.dockOffset, bottom: 0, width: `${dockedSize}px`, borderRadius: "0 8px 8px 0" } : { top: 0, right: view.dockOffset, bottom: 0, width: `${dockedSize}px`, borderRadius: "8px 0 0 8px" }
5236
5467
  };
5237
5468
  const selectedEntryParent = selectedEntry?.parentCuid != null ? entries.find((e) => e.cuid === selectedEntry.parentCuid) ?? null : null;
5238
5469
  const selectedEntryChildren = selectedEntry != null ? [...entries].filter((e) => e.parentCuid === selectedEntry.cuid).sort((a, b) => a.startTime - b.startTime) : [];
@@ -5249,7 +5480,7 @@ function NiceActionDevtools_Panel({
5249
5480
  id: "__nice-action-devtools-panel",
5250
5481
  style: panelStyle,
5251
5482
  children: [
5252
- dockSide != null && /* @__PURE__ */ jsxDEV22(ResizeHandle, {
5483
+ /* @__PURE__ */ jsxDEV22(ResizeHandle, {
5253
5484
  dockSide,
5254
5485
  dockedSize,
5255
5486
  onChange: (size) => setPrefs(isHorizDock ? { dockedHeight: size } : { dockedWidth: size })
@@ -5261,95 +5492,64 @@ function NiceActionDevtools_Panel({
5261
5492
  onClear: entries.length > 0 ? () => {
5262
5493
  core.clear();
5263
5494
  setSelectedCuid(null);
5264
- } : undefined
5495
+ } : undefined,
5496
+ openOthers: view.otherClosed
5265
5497
  }, undefined, false, undefined, this),
5266
- isHorizDock ? /* @__PURE__ */ jsxDEV22("div", {
5267
- style: { flex: 1, display: "flex", overflow: "hidden" },
5498
+ /* @__PURE__ */ jsxDEV22("div", {
5499
+ style: {
5500
+ flex: 1,
5501
+ display: "flex",
5502
+ flexDirection: isHorizDock ? "row" : "column",
5503
+ overflow: "hidden",
5504
+ minHeight: 0
5505
+ },
5268
5506
  children: [
5269
- /* @__PURE__ */ jsxDEV22("div", {
5270
- style: { position: "relative", width: "340px", flexShrink: 0 },
5271
- children: [
5272
- /* @__PURE__ */ jsxDEV22(ActionList, {
5273
- ...virtualListProps,
5274
- style: { width: "100%", height: "100%", overflowY: "auto" }
5275
- }, undefined, false, undefined, this),
5276
- /* @__PURE__ */ jsxDEV22("div", {
5277
- style: {
5278
- position: "absolute",
5279
- top: 0,
5280
- right: 0,
5281
- bottom: 0,
5282
- width: "36px",
5283
- pointerEvents: "none",
5284
- background: `linear-gradient(to right, transparent, ${DEVTOOL_LIST_BASE_BACKGROUND} 88%)`
5285
- }
5286
- }, undefined, false, undefined, this)
5287
- ]
5288
- }, undefined, true, undefined, this),
5289
5507
  /* @__PURE__ */ jsxDEV22("div", {
5290
5508
  style: {
5291
- flex: 1,
5292
- display: "flex",
5293
- flexDirection: "column",
5294
- overflow: "hidden",
5295
- borderLeft: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5296
- boxShadow: "inset 18px 0 36px -14px rgba(0,0,0,0.8)"
5509
+ flexGrow: selectedEntry != null ? 1 - detailRatio : 1,
5510
+ flexShrink: 1,
5511
+ flexBasis: 0,
5512
+ minWidth: 0,
5513
+ minHeight: 0
5297
5514
  },
5298
- children: selectedEntry != null ? /* @__PURE__ */ jsxDEV22(ActionDetailPanel, {
5299
- entry: selectedEntry,
5300
- parent: selectedEntryParent,
5301
- childEntries: selectedEntryChildren,
5302
- onSelectEntry: (cuid) => setSelectedCuid(cuid)
5303
- }, selectedEntry.cuid, false, undefined, this) : /* @__PURE__ */ jsxDEV22("div", {
5304
- style: {
5305
- padding: "24px",
5306
- textAlign: "center",
5307
- color: DEVTOOL_COLOR_TEXT_MUTED,
5308
- fontSize: "11px"
5309
- },
5310
- children: "Select an action to inspect"
5515
+ children: /* @__PURE__ */ jsxDEV22(ActionList, {
5516
+ ...virtualListProps,
5517
+ style: { width: "100%", height: "100%", overflowY: "auto" }
5311
5518
  }, undefined, false, undefined, this)
5312
- }, undefined, false, undefined, this)
5313
- ]
5314
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV22(Fragment10, {
5315
- children: [
5316
- /* @__PURE__ */ jsxDEV22("div", {
5317
- style: { position: "relative", flex: 1, minHeight: "80px" },
5519
+ }, undefined, false, undefined, this),
5520
+ selectedEntry != null && /* @__PURE__ */ jsxDEV22(Fragment10, {
5318
5521
  children: [
5319
- /* @__PURE__ */ jsxDEV22(ActionList, {
5320
- ...virtualListProps,
5321
- style: { overflowY: "auto", height: "100%" }
5522
+ /* @__PURE__ */ jsxDEV22(SplitHandle, {
5523
+ horizontal: isHorizDock,
5524
+ onRatioChange: (ratio) => setPrefs({ detailRatio: ratio })
5322
5525
  }, undefined, false, undefined, this),
5323
- selectedEntry != null && /* @__PURE__ */ jsxDEV22("div", {
5526
+ /* @__PURE__ */ jsxDEV22("div", {
5324
5527
  style: {
5325
- position: "absolute",
5326
- bottom: 0,
5327
- left: 0,
5328
- right: 0,
5329
- height: "36px",
5330
- pointerEvents: "none",
5331
- background: `linear-gradient(to bottom, transparent, ${DEVTOOL_LIST_BASE_BACKGROUND} 88%)`
5332
- }
5528
+ flexGrow: detailRatio,
5529
+ flexShrink: 1,
5530
+ flexBasis: 0,
5531
+ minWidth: 0,
5532
+ minHeight: 0,
5533
+ display: "flex",
5534
+ flexDirection: "column",
5535
+ overflow: "hidden",
5536
+ ...isHorizDock ? {
5537
+ borderLeft: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5538
+ boxShadow: "inset 18px 0 36px -14px rgba(0,0,0,0.8)"
5539
+ } : {
5540
+ borderTop: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5541
+ boxShadow: "inset 0 18px 36px -14px rgba(0,0,0,0.8)"
5542
+ }
5543
+ },
5544
+ children: /* @__PURE__ */ jsxDEV22(ActionDetailPanel, {
5545
+ entry: selectedEntry,
5546
+ parent: selectedEntryParent,
5547
+ childEntries: selectedEntryChildren,
5548
+ onSelectEntry: (cuid) => setSelectedCuid(cuid)
5549
+ }, selectedEntry.cuid, false, undefined, this)
5333
5550
  }, undefined, false, undefined, this)
5334
5551
  ]
5335
- }, undefined, true, undefined, this),
5336
- selectedEntry != null && /* @__PURE__ */ jsxDEV22("div", {
5337
- style: {
5338
- flexShrink: 0,
5339
- maxHeight: "55%",
5340
- display: "flex",
5341
- flexDirection: "column",
5342
- overflow: "hidden",
5343
- borderTop: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5344
- boxShadow: "inset 0 18px 36px -14px rgba(0,0,0,0.8)"
5345
- },
5346
- children: /* @__PURE__ */ jsxDEV22(ActionDetailPanel, {
5347
- entry: selectedEntry,
5348
- parent: selectedEntryParent,
5349
- childEntries: selectedEntryChildren,
5350
- onSelectEntry: (cuid) => setSelectedCuid(cuid)
5351
- }, selectedEntry.cuid, false, undefined, this)
5352
- }, undefined, false, undefined, this)
5552
+ }, undefined, true, undefined, this)
5353
5553
  ]
5354
5554
  }, undefined, true, undefined, this)
5355
5555
  ]
@@ -1,14 +1,41 @@
1
1
  import type { TDevtoolsPosition } from "../../core/ActionDevtools.types";
2
+ export interface IDevtoolsLauncherItem {
3
+ id: string;
4
+ label: string;
5
+ icon: string;
6
+ badge?: string;
7
+ onOpen: () => void;
8
+ }
2
9
  export type TDockSide = "top" | "bottom" | "left" | "right";
3
- export declare function getDockSide(pos: TDevtoolsPosition): TDockSide | null;
4
- export declare function PanelHeader({ position, onPositionChange, onClose, onClear, }: {
10
+ export declare function getDockSide(pos: TDevtoolsPosition): TDockSide;
11
+ export declare function PanelHeader({ position, onPositionChange, onClose, onClear, openOthers, }: {
5
12
  position: TDevtoolsPosition;
6
13
  onPositionChange: (p: TDevtoolsPosition) => void;
7
14
  onClose: () => void;
8
15
  onClear?: () => void;
16
+ openOthers?: IDevtoolsLauncherItem[];
9
17
  }): import("react/jsx-runtime").JSX.Element;
10
18
  export declare function ResizeHandle({ dockSide, dockedSize, onChange, }: {
11
19
  dockSide: TDockSide;
12
20
  dockedSize: number;
13
21
  onChange: (size: number) => void;
14
22
  }): import("react/jsx-runtime").JSX.Element;
23
+ /**
24
+ * Draggable divider between the list and the detail pane. `horizontal` refers to
25
+ * the split axis: a row layout (dock top/bottom) splits horizontally and drags
26
+ * left/right; a column layout (dock left/right) splits vertically. The reported
27
+ * ratio is the fraction of the container the *detail* pane should occupy.
28
+ */
29
+ export declare function SplitHandle({ horizontal, onRatioChange, }: {
30
+ horizontal: boolean;
31
+ onRatioChange: (ratio: number) => void;
32
+ }): import("react/jsx-runtime").JSX.Element;
33
+ /**
34
+ * The combined, page-wide launcher shown while every devtool is collapsed — one
35
+ * grouped pill with a segment per registered devtool, so the buttons never
36
+ * overlap or hide behind each other. Rendered by the coordinator's "primary"
37
+ * devtool only.
38
+ */
39
+ export declare function DevtoolsLauncher({ items }: {
40
+ items: IDevtoolsLauncherItem[];
41
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,47 @@
1
+ export type TDockSide = "top" | "bottom" | "left" | "right";
2
+ /** A handle to one registered devtool, used to render launch controls. */
3
+ export interface IDockDevtoolRef {
4
+ id: string;
5
+ label: string;
6
+ icon: string;
7
+ badge?: string;
8
+ onOpen: () => void;
9
+ }
10
+ /** The live, syncable part of a devtool's registration. */
11
+ export interface IDockDevtoolSync {
12
+ side: TDockSide;
13
+ size: number;
14
+ open: boolean;
15
+ badge?: string;
16
+ }
17
+ export interface IDockDevtoolInput extends IDockDevtoolSync {
18
+ id: string;
19
+ label: string;
20
+ icon: string;
21
+ onOpen: () => void;
22
+ }
23
+ export interface IDockView {
24
+ /** Offset (px) from the docked edge — stacks open panels on the same side. */
25
+ dockOffset: number;
26
+ /** Is any devtool on the page currently open? */
27
+ anyOpen: boolean;
28
+ /** First-registered devtool — the one that renders the combined launcher. */
29
+ isPrimary: boolean;
30
+ /** Every registered devtool, for the combined launcher. */
31
+ devtools: IDockDevtoolRef[];
32
+ /** Closed devtools other than this one, for an open panel's header. */
33
+ otherClosed: IDockDevtoolRef[];
34
+ }
35
+ export interface IDevtoolsDockCoordinator {
36
+ version: number;
37
+ register(panel: IDockDevtoolInput): () => void;
38
+ update(id: string, next: IDockDevtoolSync): void;
39
+ getView(id: string): IDockView;
40
+ subscribe(listener: () => void): () => void;
41
+ }
42
+ /**
43
+ * Returns the page-wide dock coordinator, installing it on `window` the first
44
+ * time it is requested. On the server (no `window`) a throwaway instance is
45
+ * returned so callers can use it unconditionally.
46
+ */
47
+ export declare function getDevtoolsDockCoordinator(): IDevtoolsDockCoordinator;
@@ -1,7 +1,7 @@
1
1
  import type { TActionProgress } from "../../ActionDefinition/Action/Payload/ActionPayload.types";
2
2
  import type { IRuntimeCoordinate } from "../../ActionRuntime/RuntimeCoordinate";
3
3
  export type TDevtoolsActionStatus = "running" | "success" | "action-error" | "failed" | "aborted";
4
- export type TDevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left" | "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
4
+ export type TDevtoolsPosition = "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
5
5
  export interface IDevtoolsRouteItem {
6
6
  runtime: IRuntimeCoordinate;
7
7
  handlerType: "local" | "external";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -44,9 +44,9 @@
44
44
  "build-types": "tsc --project tsconfig.build.json"
45
45
  },
46
46
  "dependencies": {
47
- "@nice-code/common-errors": "0.3.2",
48
- "@nice-code/error": "0.3.2",
49
- "@nice-code/util": "0.3.2",
47
+ "@nice-code/common-errors": "0.4.0",
48
+ "@nice-code/error": "0.4.0",
49
+ "@nice-code/util": "0.4.0",
50
50
  "@standard-schema/spec": "^1.1.0",
51
51
  "@tanstack/react-virtual": "^3.13.26",
52
52
  "http-status-codes": "^2.3.0",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@tanstack/react-query": "^5.100.3",
65
- "react": ">=18",
65
+ "react": ">=19",
66
66
  "valibot": "^1.3.1"
67
67
  }
68
68
  }