@nice-code/action 0.3.3 → 0.4.1

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";
@@ -4874,29 +4874,34 @@ function ActionList({
4874
4874
  }
4875
4875
 
4876
4876
  // src/devtools/browser/components/PanelChrome.tsx
4877
+ import { useState as useState9 } from "react";
4877
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";
4878
4881
  var DOCKED_SIZE_MIN = 140;
4879
4882
  var POSITION_GRID = [
4880
- ["top-left", "dock-top", "top-right"],
4883
+ [null, "dock-top", null],
4881
4884
  ["dock-left", null, "dock-right"],
4882
- ["bottom-left", "dock-bottom", "bottom-right"]
4885
+ [null, "dock-bottom", null]
4883
4886
  ];
4884
4887
  function getDockSide(pos) {
4885
- if (pos === "dock-top")
4886
- return "top";
4887
- if (pos === "dock-bottom")
4888
- return "bottom";
4889
- if (pos === "dock-left")
4890
- return "left";
4891
- if (pos === "dock-right")
4892
- return "right";
4893
- 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
+ }
4894
4898
  }
4895
4899
  function PanelHeader({
4896
4900
  position,
4897
4901
  onPositionChange,
4898
4902
  onClose,
4899
- onClear
4903
+ onClear,
4904
+ openOthers
4900
4905
  }) {
4901
4906
  return /* @__PURE__ */ jsxDEV21("div", {
4902
4907
  style: {
@@ -4904,15 +4909,56 @@ function PanelHeader({
4904
4909
  alignItems: "center",
4905
4910
  justifyContent: "space-between",
4906
4911
  padding: "8px 12px",
4912
+ gap: "10px",
4907
4913
  background: DEVTOOL_SECTION_BACKGROUND,
4908
4914
  borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`,
4909
4915
  flexShrink: 0
4910
4916
  },
4911
4917
  children: [
4912
- /* @__PURE__ */ jsxDEV21("span", {
4913
- style: { color: DEVTOOL_COLOR_SEMANTIC_SYSTEM, fontWeight: "bold", fontSize: "11px" },
4914
- children: "⚡ nice-action devtools"
4915
- }, 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),
4916
4962
  /* @__PURE__ */ jsxDEV21("div", {
4917
4963
  style: { display: "flex", gap: "10px", alignItems: "center" },
4918
4964
  children: [
@@ -5022,6 +5068,195 @@ function ResizeHandle({
5022
5068
  }
5023
5069
  }, undefined, false, undefined, this);
5024
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
+ }
5025
5260
 
5026
5261
  // src/devtools/browser/NiceActionDevtools.tsx
5027
5262
  import { jsxDEV as jsxDEV22, Fragment as Fragment10 } from "react/jsx-dev-runtime";
@@ -5062,18 +5297,27 @@ if (typeof document !== "undefined" && !document.getElementById("__nice-action-d
5062
5297
  var PREFS_KEY = "__nice-action-devtools-prefs";
5063
5298
  var DOCKED_HEIGHT_DEFAULT = 320;
5064
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
+ }
5065
5305
  function readPrefs(defaultPosition, initialOpen) {
5066
5306
  const fallback = {
5067
5307
  position: defaultPosition,
5068
5308
  isOpen: initialOpen,
5069
5309
  dockedHeight: DOCKED_HEIGHT_DEFAULT,
5070
- dockedWidth: DOCKED_WIDTH_DEFAULT
5310
+ dockedWidth: DOCKED_WIDTH_DEFAULT,
5311
+ detailRatio: DETAIL_RATIO_DEFAULT
5071
5312
  };
5072
5313
  try {
5073
5314
  if (typeof localStorage === "undefined")
5074
5315
  return fallback;
5075
5316
  const stored = localStorage.getItem(PREFS_KEY);
5076
- 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;
5077
5321
  } catch (_e) {
5078
5322
  return fallback;
5079
5323
  }
@@ -5123,12 +5367,12 @@ function NiceActionDevtools(props) {
5123
5367
  }
5124
5368
  function NiceActionDevtools_Panel({
5125
5369
  core,
5126
- position: defaultPosition = "bottom-right",
5370
+ position: defaultPosition = "dock-bottom",
5127
5371
  initialOpen = false
5128
5372
  }) {
5129
- const [prefs, setPrefsRaw] = useState9(() => readPrefs(defaultPosition, initialOpen));
5130
- const [entries, setEntries] = useState9([]);
5131
- const [selectedCuid, setSelectedCuid] = useState9(null);
5373
+ const [prefs, setPrefsRaw] = useState10(() => readPrefs(defaultPosition, initialOpen));
5374
+ const [entries, setEntries] = useState10([]);
5375
+ const [selectedCuid, setSelectedCuid] = useState10(null);
5132
5376
  useEffect4(() => core.subscribe(setEntries), [core]);
5133
5377
  const groups = useMemo3(() => {
5134
5378
  const byCuid = new Map(entries.map((e) => [e.cuid, e]));
@@ -5165,44 +5409,37 @@ function NiceActionDevtools_Panel({
5165
5409
  return next;
5166
5410
  });
5167
5411
  };
5168
- const { position, isOpen, dockedHeight, dockedWidth } = prefs;
5412
+ const { position, isOpen, dockedHeight, dockedWidth, detailRatio } = prefs;
5169
5413
  const dockSide = getDockSide(position);
5170
5414
  const isHorizDock = dockSide === "top" || dockSide === "bottom";
5171
5415
  const dockedSize = isHorizDock ? dockedHeight : dockedWidth;
5172
5416
  const selectedEntry = selectedCuid != null ? entries.find((e) => e.cuid === selectedCuid) : null;
5173
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;
5174
5422
  useEffect4(() => {
5175
- const sides = ["top", "bottom", "left", "right"];
5176
- const clearAll = () => {
5177
- sides.forEach((s) => {
5178
- document.body.style.removeProperty(`margin-${s}`);
5179
- });
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();
5180
5437
  };
5181
- if (!isOpen || dockSide == null) {
5182
- clearAll();
5183
- return clearAll;
5184
- }
5185
- clearAll();
5186
- document.body.style.setProperty(`margin-${dockSide}`, `${dockedSize}px`);
5187
- return clearAll;
5188
- }, [dockSide, isOpen, dockedSize]);
5189
- const closedAnchor = (() => {
5190
- switch (position) {
5191
- case "dock-bottom":
5192
- return { bottom: "16px", right: "16px" };
5193
- case "dock-top":
5194
- return { top: "16px", right: "16px" };
5195
- case "dock-left":
5196
- return { top: "16px", left: "16px" };
5197
- case "dock-right":
5198
- return { top: "16px", right: "16px" };
5199
- default:
5200
- return {
5201
- ...position.includes("right") ? { right: "16px" } : { left: "16px" },
5202
- ...position.includes("bottom") ? { bottom: "16px" } : { top: "16px" }
5203
- };
5204
- }
5205
- })();
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);
5206
5443
  const baseStyle = {
5207
5444
  position: "fixed",
5208
5445
  zIndex: 2147483647,
@@ -5210,36 +5447,14 @@ function NiceActionDevtools_Panel({
5210
5447
  fontSize: "12px"
5211
5448
  };
5212
5449
  if (!isOpen) {
5213
- return /* @__PURE__ */ jsxDEV22("button", {
5214
- onClick: () => setPrefs({ isOpen: true }),
5215
- style: {
5216
- ...baseStyle,
5217
- ...closedAnchor,
5218
- background: DEVTOOL_SECTION_BACKGROUND,
5219
- color: DEVTOOL_COLOR_TEXT_SECONDARY,
5220
- border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
5221
- borderRadius: "6px",
5222
- padding: "5px 10px",
5223
- cursor: "pointer",
5224
- lineHeight: "1.5"
5225
- },
5226
- children: [
5227
- "⚡ actions",
5228
- runningCount > 0 && /* @__PURE__ */ jsxDEV22("span", {
5229
- style: {
5230
- marginLeft: "6px",
5231
- color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
5232
- animation: "__nice-action-pulse 1.2s ease-in-out infinite"
5233
- },
5234
- children: [
5235
- runningCount,
5236
- " running"
5237
- ]
5238
- }, undefined, true, undefined, this)
5239
- ]
5240
- }, 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;
5241
5456
  }
5242
- const panelStyle = dockSide != null ? {
5457
+ const panelStyle = {
5243
5458
  ...baseStyle,
5244
5459
  background: DEVTOOL_LIST_BASE_BACKGROUND,
5245
5460
  border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
@@ -5248,44 +5463,7 @@ function NiceActionDevtools_Panel({
5248
5463
  flexDirection: "column",
5249
5464
  boxShadow: "0 -4px 24px rgba(0,0,0,0.4)",
5250
5465
  overflow: "hidden",
5251
- ...dockSide === "bottom" ? {
5252
- bottom: 0,
5253
- left: 0,
5254
- right: 0,
5255
- height: `${dockedSize}px`,
5256
- borderRadius: "8px 8px 0 0"
5257
- } : dockSide === "top" ? {
5258
- top: 0,
5259
- left: 0,
5260
- right: 0,
5261
- height: `${dockedSize}px`,
5262
- borderRadius: "0 0 8px 8px"
5263
- } : dockSide === "left" ? {
5264
- top: 0,
5265
- left: 0,
5266
- bottom: 0,
5267
- width: `${dockedSize}px`,
5268
- borderRadius: "0 8px 8px 0"
5269
- } : {
5270
- top: 0,
5271
- right: 0,
5272
- bottom: 0,
5273
- width: `${dockedSize}px`,
5274
- borderRadius: "8px 0 0 8px"
5275
- }
5276
- } : {
5277
- ...baseStyle,
5278
- ...closedAnchor,
5279
- width: "460px",
5280
- maxHeight: "560px",
5281
- background: DEVTOOL_LIST_BASE_BACKGROUND,
5282
- border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
5283
- borderRadius: "10px",
5284
- color: DEVTOOL_COLOR_TEXT_SECONDARY,
5285
- display: "flex",
5286
- flexDirection: "column",
5287
- boxShadow: "0 25px 50px rgba(0,0,0,0.5)",
5288
- 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" }
5289
5467
  };
5290
5468
  const selectedEntryParent = selectedEntry?.parentCuid != null ? entries.find((e) => e.cuid === selectedEntry.parentCuid) ?? null : null;
5291
5469
  const selectedEntryChildren = selectedEntry != null ? [...entries].filter((e) => e.parentCuid === selectedEntry.cuid).sort((a, b) => a.startTime - b.startTime) : [];
@@ -5302,7 +5480,7 @@ function NiceActionDevtools_Panel({
5302
5480
  id: "__nice-action-devtools-panel",
5303
5481
  style: panelStyle,
5304
5482
  children: [
5305
- dockSide != null && /* @__PURE__ */ jsxDEV22(ResizeHandle, {
5483
+ /* @__PURE__ */ jsxDEV22(ResizeHandle, {
5306
5484
  dockSide,
5307
5485
  dockedSize,
5308
5486
  onChange: (size) => setPrefs(isHorizDock ? { dockedHeight: size } : { dockedWidth: size })
@@ -5314,95 +5492,64 @@ function NiceActionDevtools_Panel({
5314
5492
  onClear: entries.length > 0 ? () => {
5315
5493
  core.clear();
5316
5494
  setSelectedCuid(null);
5317
- } : undefined
5495
+ } : undefined,
5496
+ openOthers: view.otherClosed
5318
5497
  }, undefined, false, undefined, this),
5319
- isHorizDock ? /* @__PURE__ */ jsxDEV22("div", {
5320
- 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
+ },
5321
5506
  children: [
5322
- /* @__PURE__ */ jsxDEV22("div", {
5323
- style: { position: "relative", width: "340px", flexShrink: 0 },
5324
- children: [
5325
- /* @__PURE__ */ jsxDEV22(ActionList, {
5326
- ...virtualListProps,
5327
- style: { width: "100%", height: "100%", overflowY: "auto" }
5328
- }, undefined, false, undefined, this),
5329
- /* @__PURE__ */ jsxDEV22("div", {
5330
- style: {
5331
- position: "absolute",
5332
- top: 0,
5333
- right: 0,
5334
- bottom: 0,
5335
- width: "36px",
5336
- pointerEvents: "none",
5337
- background: `linear-gradient(to right, transparent, ${DEVTOOL_LIST_BASE_BACKGROUND} 88%)`
5338
- }
5339
- }, undefined, false, undefined, this)
5340
- ]
5341
- }, undefined, true, undefined, this),
5342
5507
  /* @__PURE__ */ jsxDEV22("div", {
5343
5508
  style: {
5344
- flex: 1,
5345
- display: "flex",
5346
- flexDirection: "column",
5347
- overflow: "hidden",
5348
- borderLeft: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5349
- 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
5350
5514
  },
5351
- children: selectedEntry != null ? /* @__PURE__ */ jsxDEV22(ActionDetailPanel, {
5352
- entry: selectedEntry,
5353
- parent: selectedEntryParent,
5354
- childEntries: selectedEntryChildren,
5355
- onSelectEntry: (cuid) => setSelectedCuid(cuid)
5356
- }, selectedEntry.cuid, false, undefined, this) : /* @__PURE__ */ jsxDEV22("div", {
5357
- style: {
5358
- padding: "24px",
5359
- textAlign: "center",
5360
- color: DEVTOOL_COLOR_TEXT_MUTED,
5361
- fontSize: "11px"
5362
- },
5363
- children: "Select an action to inspect"
5515
+ children: /* @__PURE__ */ jsxDEV22(ActionList, {
5516
+ ...virtualListProps,
5517
+ style: { width: "100%", height: "100%", overflowY: "auto" }
5364
5518
  }, undefined, false, undefined, this)
5365
- }, undefined, false, undefined, this)
5366
- ]
5367
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV22(Fragment10, {
5368
- children: [
5369
- /* @__PURE__ */ jsxDEV22("div", {
5370
- style: { position: "relative", flex: 1, minHeight: "80px" },
5519
+ }, undefined, false, undefined, this),
5520
+ selectedEntry != null && /* @__PURE__ */ jsxDEV22(Fragment10, {
5371
5521
  children: [
5372
- /* @__PURE__ */ jsxDEV22(ActionList, {
5373
- ...virtualListProps,
5374
- style: { overflowY: "auto", height: "100%" }
5522
+ /* @__PURE__ */ jsxDEV22(SplitHandle, {
5523
+ horizontal: isHorizDock,
5524
+ onRatioChange: (ratio) => setPrefs({ detailRatio: ratio })
5375
5525
  }, undefined, false, undefined, this),
5376
- selectedEntry != null && /* @__PURE__ */ jsxDEV22("div", {
5526
+ /* @__PURE__ */ jsxDEV22("div", {
5377
5527
  style: {
5378
- position: "absolute",
5379
- bottom: 0,
5380
- left: 0,
5381
- right: 0,
5382
- height: "36px",
5383
- pointerEvents: "none",
5384
- background: `linear-gradient(to bottom, transparent, ${DEVTOOL_LIST_BASE_BACKGROUND} 88%)`
5385
- }
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)
5386
5550
  }, undefined, false, undefined, this)
5387
5551
  ]
5388
- }, undefined, true, undefined, this),
5389
- selectedEntry != null && /* @__PURE__ */ jsxDEV22("div", {
5390
- style: {
5391
- flexShrink: 0,
5392
- maxHeight: "55%",
5393
- display: "flex",
5394
- flexDirection: "column",
5395
- overflow: "hidden",
5396
- borderTop: `1px solid ${DEVTOOL_PANEL_DIVIDER_BORDER}`,
5397
- boxShadow: "inset 0 18px 36px -14px rgba(0,0,0,0.8)"
5398
- },
5399
- children: /* @__PURE__ */ jsxDEV22(ActionDetailPanel, {
5400
- entry: selectedEntry,
5401
- parent: selectedEntryParent,
5402
- childEntries: selectedEntryChildren,
5403
- onSelectEntry: (cuid) => setSelectedCuid(cuid)
5404
- }, selectedEntry.cuid, false, undefined, this)
5405
- }, undefined, false, undefined, this)
5552
+ }, undefined, true, undefined, this)
5406
5553
  ]
5407
5554
  }, undefined, true, undefined, this)
5408
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.3",
3
+ "version": "0.4.1",
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.3",
48
- "@nice-code/error": "0.3.3",
49
- "@nice-code/util": "0.3.3",
47
+ "@nice-code/common-errors": "0.4.1",
48
+ "@nice-code/error": "0.4.1",
49
+ "@nice-code/util": "0.4.1",
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
  }