@ai-matrx/canvas 0.6.1 → 0.7.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.
- package/CHANGELOG.md +18 -0
- package/README.md +17 -2
- package/dist/index.cjs +76 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +76 -7
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +427 -202
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +77 -44
- package/dist/react.d.ts +77 -44
- package/dist/react.js +381 -156
- package/dist/react.js.map +1 -1
- package/dist/styles.css +71 -38
- package/dist/{toggleKind-DhDmWExm.d.cts → toggleKind-CAI7pndj.d.cts} +21 -2
- package/dist/{toggleKind-DhDmWExm.d.ts → toggleKind-CAI7pndj.d.ts} +21 -2
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -5,13 +5,52 @@
|
|
|
5
5
|
import {
|
|
6
6
|
createContext,
|
|
7
7
|
useContext,
|
|
8
|
-
useEffect as
|
|
8
|
+
useEffect as useEffect3,
|
|
9
9
|
useMemo,
|
|
10
|
-
useRef as
|
|
10
|
+
useRef as useRef3,
|
|
11
11
|
useState as useState2,
|
|
12
12
|
useSyncExternalStore as useSyncExternalStore2
|
|
13
13
|
} from "react";
|
|
14
14
|
|
|
15
|
+
// src/react/menuItems.ts
|
|
16
|
+
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
17
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
18
|
+
function createCanvasItemMenus() {
|
|
19
|
+
const sources = /* @__PURE__ */ new Map();
|
|
20
|
+
return {
|
|
21
|
+
register(itemId, read) {
|
|
22
|
+
let set = sources.get(itemId);
|
|
23
|
+
if (!set) sources.set(itemId, set = /* @__PURE__ */ new Set());
|
|
24
|
+
set.add(read);
|
|
25
|
+
return () => {
|
|
26
|
+
const current = sources.get(itemId);
|
|
27
|
+
current?.delete(read);
|
|
28
|
+
if (current && current.size === 0) sources.delete(itemId);
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
read(itemId) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const read of sources.get(itemId) ?? []) {
|
|
34
|
+
try {
|
|
35
|
+
out.push(...read());
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("[@ai-matrx/canvas] a body's menu entries threw", error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function useCanvasMenuItems(itemId, items) {
|
|
45
|
+
const { itemMenus } = useCanvasInternals();
|
|
46
|
+
const latest = useRef(items);
|
|
47
|
+
latest.current = items;
|
|
48
|
+
useIsoLayoutEffect(() => {
|
|
49
|
+
if (!itemId) return void 0;
|
|
50
|
+
return itemMenus.register(itemId, () => latest.current);
|
|
51
|
+
}, [itemMenus, itemId]);
|
|
52
|
+
}
|
|
53
|
+
|
|
15
54
|
// src/core/types.ts
|
|
16
55
|
var CANVAS_MAIN_WINDOW = "main";
|
|
17
56
|
var CANVAS_LAUNCHER_KIND = "canvas.launcher";
|
|
@@ -179,6 +218,8 @@ var canvasActions = {
|
|
|
179
218
|
navigate: (itemId, to) => ({ type: `${P}navigate`, payload: { itemId, to, now: Date.now() } }),
|
|
180
219
|
/** Returns a tab to what it showed before its last navigate. */
|
|
181
220
|
back: (itemId) => ({ type: `${P}back`, payload: { itemId, now: Date.now() } }),
|
|
221
|
+
/** Re-does the last Back (the step Back left), with that step's live body. */
|
|
222
|
+
forward: (itemId) => ({ type: `${P}forward`, payload: { itemId, now: Date.now() } }),
|
|
182
223
|
closeItem: (itemId) => ({ type: `${P}closeItem`, payload: { itemId } }),
|
|
183
224
|
closeOthers: (itemId) => ({ type: `${P}closeOthers`, payload: { itemId } }),
|
|
184
225
|
activate: (itemId) => ({ type: `${P}activate`, payload: { itemId } }),
|
|
@@ -379,6 +420,7 @@ function newItem(kind, key, title, data, now, steps) {
|
|
|
379
420
|
const id = canvasItemId(kind, key);
|
|
380
421
|
const history = steps?.history ?? [];
|
|
381
422
|
const kept = steps?.kept ?? [];
|
|
423
|
+
const forward = steps?.forward ?? [];
|
|
382
424
|
return {
|
|
383
425
|
id,
|
|
384
426
|
kind,
|
|
@@ -390,7 +432,8 @@ function newItem(kind, key, title, data, now, steps) {
|
|
|
390
432
|
// A reused instance is the SAME live body coming back (Back, or returning to a kept step).
|
|
391
433
|
instance: steps?.instance ?? `${now.toString(36)}:${id}`,
|
|
392
434
|
...history.length > 0 ? { history: history.slice(-CANVAS_HISTORY_LIMIT) } : {},
|
|
393
|
-
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {}
|
|
435
|
+
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {},
|
|
436
|
+
...forward.length > 0 ? { forward: forward.slice(-CANVAS_HISTORY_LIMIT) } : {}
|
|
394
437
|
};
|
|
395
438
|
}
|
|
396
439
|
function historyEntry(item) {
|
|
@@ -436,12 +479,14 @@ function sanitizeHistory(raw) {
|
|
|
436
479
|
function sanitizeItem(item) {
|
|
437
480
|
const history = sanitizeHistory(item.history);
|
|
438
481
|
const kept = sanitizeHistory(item.kept).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
439
|
-
const
|
|
482
|
+
const forward = sanitizeHistory(item.forward);
|
|
483
|
+
const { history: _history, kept: _kept, forward: _forward, instance, ...rest } = item;
|
|
440
484
|
return {
|
|
441
485
|
...rest,
|
|
442
486
|
...typeof instance === "string" && instance ? { instance } : {},
|
|
443
487
|
...history.length > 0 ? { history } : {},
|
|
444
|
-
...kept.length > 0 ? { kept } : {}
|
|
488
|
+
...kept.length > 0 ? { kept } : {},
|
|
489
|
+
...forward.length > 0 ? { forward } : {}
|
|
445
490
|
};
|
|
446
491
|
}
|
|
447
492
|
function clampWidth(width) {
|
|
@@ -632,6 +677,9 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
632
677
|
const keptIndex = keptAll.findIndex((entry) => canvasItemId(entry.kind, entry.key) === targetId);
|
|
633
678
|
const revived = keptIndex >= 0 ? keptAll[keptIndex] : void 0;
|
|
634
679
|
const kept = keptAll.filter((_, i) => i !== keptIndex);
|
|
680
|
+
const forwardAll = current.forward ?? [];
|
|
681
|
+
const nextForward = forwardAll[forwardAll.length - 1];
|
|
682
|
+
const forward = nextForward && canvasItemId(nextForward.kind, nextForward.key) === targetId ? forwardAll.slice(0, -1) : [];
|
|
635
683
|
return replaceInPlace(
|
|
636
684
|
state,
|
|
637
685
|
current,
|
|
@@ -641,7 +689,7 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
641
689
|
to.title !== void 0 ? to.title : revived?.title ?? null,
|
|
642
690
|
to.data !== void 0 ? to.data : revived?.data ?? null,
|
|
643
691
|
now,
|
|
644
|
-
{ history, kept, instance: revived?.instance }
|
|
692
|
+
{ history, kept, forward, instance: revived?.instance }
|
|
645
693
|
)
|
|
646
694
|
);
|
|
647
695
|
}
|
|
@@ -657,10 +705,36 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
657
705
|
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
658
706
|
}
|
|
659
707
|
const kept = [...(current.kept ?? []).filter((k) => canvasItemId(k.kind, k.key) !== itemId), historyEntry(current)];
|
|
708
|
+
const forward = [...current.forward ?? [], historyEntry(current)];
|
|
660
709
|
return replaceInPlace(
|
|
661
710
|
state,
|
|
662
711
|
current,
|
|
663
|
-
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, instance: entry.instance })
|
|
712
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, forward, instance: entry.instance })
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
case `${P}forward`: {
|
|
716
|
+
const { itemId, now } = action.payload;
|
|
717
|
+
const current = state.items[itemId];
|
|
718
|
+
const forwardAll = current?.forward ?? [];
|
|
719
|
+
const entry = forwardAll[forwardAll.length - 1];
|
|
720
|
+
if (!current || !entry || !paneOf(state, itemId)) return state;
|
|
721
|
+
const targetId = canvasItemId(entry.kind, entry.key);
|
|
722
|
+
if (targetId === itemId) return state;
|
|
723
|
+
if (state.items[targetId]) {
|
|
724
|
+
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
725
|
+
}
|
|
726
|
+
const keptAll = current.kept ?? [];
|
|
727
|
+
const revived = keptAll.find((k) => canvasItemId(k.kind, k.key) === targetId);
|
|
728
|
+
const kept = keptAll.filter((k) => canvasItemId(k.kind, k.key) !== targetId);
|
|
729
|
+
return replaceInPlace(
|
|
730
|
+
state,
|
|
731
|
+
current,
|
|
732
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, {
|
|
733
|
+
history: [...current.history ?? [], historyEntry(current)],
|
|
734
|
+
kept,
|
|
735
|
+
forward: forwardAll.slice(0, -1),
|
|
736
|
+
instance: revived?.instance ?? entry.instance
|
|
737
|
+
})
|
|
664
738
|
);
|
|
665
739
|
}
|
|
666
740
|
case `${P}closeItem`: {
|
|
@@ -912,6 +986,7 @@ function toPersistableSnapshot(state, isRestorable) {
|
|
|
912
986
|
}
|
|
913
987
|
|
|
914
988
|
// src/core/rules.ts
|
|
989
|
+
var CANVAS_PANE_MIN_WIDTH = 280;
|
|
915
990
|
var CANVAS_WEB_LAYOUT_RULES = {
|
|
916
991
|
defaultWidth: CANVAS_DEFAULT_WIDTH,
|
|
917
992
|
minWidth: CANVAS_MIN_WIDTH,
|
|
@@ -919,7 +994,9 @@ var CANVAS_WEB_LAYOUT_RULES = {
|
|
|
919
994
|
// Split down 70/30 like the desktop: what opens below (notifications, a feed) is the smaller pane.
|
|
920
995
|
stackSplit: 0.7,
|
|
921
996
|
sideSplit: 0.5,
|
|
922
|
-
|
|
997
|
+
// A pane narrower than this cannot show a header (Back, a readable tab, "+", the menu, close) and a
|
|
998
|
+
// body side by side with another: a split right that would go under it stacks instead.
|
|
999
|
+
paneMinWidth: CANVAS_PANE_MIN_WIDTH
|
|
923
1000
|
};
|
|
924
1001
|
var clampFraction = (value, fallback) => Number.isFinite(value) ? Math.min(0.88, Math.max(0.12, value)) : fallback;
|
|
925
1002
|
function resolveCanvasLayoutRules(rules) {
|
|
@@ -958,6 +1035,9 @@ var CANVAS_SHORTCUTS = [
|
|
|
958
1035
|
{ id: "canvas.toggleFullscreen", accelerator: "CmdOrCtrl+Shift+Enter", when: "canvas-focus", label: "Expand canvas", web: true },
|
|
959
1036
|
{ id: "canvas.splitRight", accelerator: "CmdOrCtrl+Alt+\\", when: "canvas-focus", label: "Split right", web: true },
|
|
960
1037
|
{ id: "canvas.splitDown", accelerator: "CmdOrCtrl+Alt+Shift+\\", when: "canvas-focus", label: "Split down", web: true },
|
|
1038
|
+
// Browsers own ⌘[ / ⌘] for their own history: data for a desktop host (and the header's Back / Forward).
|
|
1039
|
+
{ id: "canvas.back", accelerator: "CmdOrCtrl+[", when: "canvas-focus", label: "Back", web: false },
|
|
1040
|
+
{ id: "canvas.forward", accelerator: "CmdOrCtrl+]", when: "canvas-focus", label: "Forward", web: false },
|
|
961
1041
|
{ id: "canvas.closeTab", accelerator: "CmdOrCtrl+W", when: "canvas-focus", label: "Close tab", web: false },
|
|
962
1042
|
{ id: "canvas.nextTab", accelerator: "Ctrl+Tab", when: "canvas-focus", label: "Next tab", web: false },
|
|
963
1043
|
{ id: "canvas.previousTab", accelerator: "Ctrl+Shift+Tab", when: "canvas-focus", label: "Previous tab", web: false }
|
|
@@ -1007,11 +1087,17 @@ function runCanvasCommand(controller, id, windowId = controller.windowId) {
|
|
|
1007
1087
|
if (!frame2?.isOpen || !pane) return false;
|
|
1008
1088
|
controller.splitPane(pane.id, id === "canvas.splitRight" ? "horizontal" : "vertical", pane.itemIds.length > 1 ? pane.activeItemId ?? void 0 : void 0);
|
|
1009
1089
|
return true;
|
|
1090
|
+
case "canvas.back":
|
|
1091
|
+
if (!frame2?.isOpen || !pane?.activeItemId) return false;
|
|
1092
|
+
return controller.back(pane.activeItemId) !== null;
|
|
1093
|
+
case "canvas.forward":
|
|
1094
|
+
if (!frame2?.isOpen || !pane?.activeItemId) return false;
|
|
1095
|
+
return controller.forward(pane.activeItemId) !== null;
|
|
1010
1096
|
default:
|
|
1011
1097
|
return false;
|
|
1012
1098
|
}
|
|
1013
1099
|
}
|
|
1014
|
-
var CODE_FOR = { "\\": "Backslash", Tab: "Tab", Escape: "Escape", Enter: "Enter" };
|
|
1100
|
+
var CODE_FOR = { "\\": "Backslash", "[": "BracketLeft", "]": "BracketRight", Tab: "Tab", Escape: "Escape", Enter: "Enter" };
|
|
1015
1101
|
function matchesCanvasAccelerator(input, accelerator, isMac3) {
|
|
1016
1102
|
const parts = accelerator.split("+");
|
|
1017
1103
|
const key = parts.pop() ?? "";
|
|
@@ -1128,6 +1214,13 @@ function view(shared, windowId) {
|
|
|
1128
1214
|
dispatch(canvasActions.back(itemId));
|
|
1129
1215
|
return canvasItemId(entry.kind, entry.key);
|
|
1130
1216
|
},
|
|
1217
|
+
forward(itemId) {
|
|
1218
|
+
const forward = store.getState().items[itemId]?.forward ?? [];
|
|
1219
|
+
const entry = forward[forward.length - 1];
|
|
1220
|
+
if (!entry) return null;
|
|
1221
|
+
dispatch(canvasActions.forward(itemId));
|
|
1222
|
+
return canvasItemId(entry.kind, entry.key);
|
|
1223
|
+
},
|
|
1131
1224
|
newTab(paneId) {
|
|
1132
1225
|
const state = store.getState();
|
|
1133
1226
|
const frame2 = canvasWindowFrame(state, windowId);
|
|
@@ -1394,6 +1487,16 @@ function selectCanvasWindowItemCount(s, windowId) {
|
|
|
1394
1487
|
var NO_WINDOWS = Object.freeze({});
|
|
1395
1488
|
var NO_HISTORY = Object.freeze([]);
|
|
1396
1489
|
var selectCanvasCanGoBack = (s, itemId) => (s.items[itemId]?.history?.length ?? 0) > 0;
|
|
1490
|
+
var selectCanvasCanGoForward = (s, itemId) => (s.items[itemId]?.forward?.length ?? 0) > 0;
|
|
1491
|
+
function selectCanvasPresentedPaneId(s, windowId) {
|
|
1492
|
+
const f = frame(s, windowId);
|
|
1493
|
+
if (!f) return null;
|
|
1494
|
+
const ids = listPaneIds(f.layout);
|
|
1495
|
+
const holds = (id) => (s.panes[id]?.itemIds.length ?? 0) > 0;
|
|
1496
|
+
const focused = ids.find((id) => id === f.focusedPaneId);
|
|
1497
|
+
if (focused && holds(focused)) return focused;
|
|
1498
|
+
return ids.find(holds) ?? focused ?? ids[0] ?? null;
|
|
1499
|
+
}
|
|
1397
1500
|
|
|
1398
1501
|
// src/core/presentation.ts
|
|
1399
1502
|
var CANVAS_PHONE_MAX_WIDTH = 767;
|
|
@@ -1437,7 +1540,9 @@ function toggleKind(controller, input) {
|
|
|
1437
1540
|
const before = selectCanvasKindVisibility(controller.getState(), input.kind, key);
|
|
1438
1541
|
if (before === "visible") {
|
|
1439
1542
|
if ((input.whenVisible ?? "close") === "close") {
|
|
1543
|
+
const windowId = canvasWindowOfItem(controller.getState(), itemId);
|
|
1440
1544
|
controller.close(itemId);
|
|
1545
|
+
if (windowId && selectCanvasWindowItemCount(controller.getState(), windowId) === 0) controller.hide(windowId);
|
|
1441
1546
|
} else {
|
|
1442
1547
|
controller.hide(canvasWindowOfItem(controller.getState(), itemId) ?? void 0);
|
|
1443
1548
|
}
|
|
@@ -1459,7 +1564,7 @@ function toggleKind(controller, input) {
|
|
|
1459
1564
|
}
|
|
1460
1565
|
|
|
1461
1566
|
// src/react/layer.tsx
|
|
1462
|
-
import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
1567
|
+
import { useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState, useSyncExternalStore } from "react";
|
|
1463
1568
|
import { createPortal } from "react-dom";
|
|
1464
1569
|
import { jsx } from "react/jsx-runtime";
|
|
1465
1570
|
function createCanvasLayer(getPorts) {
|
|
@@ -1528,7 +1633,7 @@ function createCanvasLayer(getPorts) {
|
|
|
1528
1633
|
function CanvasDomOverlay({ layer }) {
|
|
1529
1634
|
const elements = useSyncExternalStore(layer.dom.subscribe, layer.dom.list, layer.dom.list);
|
|
1530
1635
|
const [mounted, setMounted] = useState(false);
|
|
1531
|
-
|
|
1636
|
+
useEffect2(() => setMounted(true), []);
|
|
1532
1637
|
if (!mounted || elements.length === 0 || typeof document === "undefined") return null;
|
|
1533
1638
|
return createPortal(
|
|
1534
1639
|
/* @__PURE__ */ jsx("div", { className: "mxc-overlay", "aria-hidden": "true", children: elements.map(([id, element]) => /* @__PURE__ */ jsx(FloatingView, { element }, id)) }),
|
|
@@ -1544,11 +1649,11 @@ function FloatingView({ element }) {
|
|
|
1544
1649
|
return null;
|
|
1545
1650
|
}
|
|
1546
1651
|
function Tooltip({ element }) {
|
|
1547
|
-
const ref =
|
|
1652
|
+
const ref = useRef2(null);
|
|
1548
1653
|
const { anchor, side, text } = element;
|
|
1549
1654
|
const top = side === "bottom" ? anchor.y + anchor.height + 6 : anchor.y - 6;
|
|
1550
1655
|
const centre = anchor.x + anchor.width / 2;
|
|
1551
|
-
|
|
1656
|
+
useLayoutEffect2(() => {
|
|
1552
1657
|
const el = ref.current;
|
|
1553
1658
|
if (!el) return;
|
|
1554
1659
|
const width = el.offsetWidth;
|
|
@@ -1683,9 +1788,9 @@ function CanvasProvider({
|
|
|
1683
1788
|
() => ({ itemActions, popOut, dockBack, showMenu, overlay, onNativeBodyFrame }),
|
|
1684
1789
|
[itemActions, popOut, dockBack, showMenu, overlay, onNativeBodyFrame]
|
|
1685
1790
|
);
|
|
1686
|
-
const portsRef =
|
|
1791
|
+
const portsRef = useRef3(hostPorts);
|
|
1687
1792
|
portsRef.current = hostPorts;
|
|
1688
|
-
const [{ controller, layer, launcherEntries }] = useState2(() => ({
|
|
1793
|
+
const [{ controller, layer, launcherEntries, itemMenus }] = useState2(() => ({
|
|
1689
1794
|
controller: createCanvasController({
|
|
1690
1795
|
store: store ?? createCanvasStore(),
|
|
1691
1796
|
persistence: persistence === void 0 ? createLocalStorageCanvasPersistence() : persistence,
|
|
@@ -1699,11 +1804,12 @@ function CanvasProvider({
|
|
|
1699
1804
|
preferredTarget: (kind) => getCanvasKind(kind)?.preferredTarget
|
|
1700
1805
|
}),
|
|
1701
1806
|
layer: createCanvasLayer(() => portsRef.current),
|
|
1702
|
-
launcherEntries: createLauncherEntryStore()
|
|
1807
|
+
launcherEntries: createLauncherEntryStore(),
|
|
1808
|
+
itemMenus: createCanvasItemMenus()
|
|
1703
1809
|
}));
|
|
1704
1810
|
const windowId = controller.windowId;
|
|
1705
|
-
|
|
1706
|
-
|
|
1811
|
+
useEffect3(() => controller.start(), [controller]);
|
|
1812
|
+
useEffect3(() => {
|
|
1707
1813
|
if (!hotkeys) return;
|
|
1708
1814
|
const isMac3 = isMacPlatform();
|
|
1709
1815
|
const applies = (when, event) => {
|
|
@@ -1725,8 +1831,8 @@ function CanvasProvider({
|
|
|
1725
1831
|
return () => window.removeEventListener("keydown", onKey);
|
|
1726
1832
|
}, [controller, hotkeys, windowId]);
|
|
1727
1833
|
const value = useMemo(
|
|
1728
|
-
() => ({ controller, windowId, ports: hostPorts, layer, regionWidth, tabStrip, launcherEntries }),
|
|
1729
|
-
[controller, windowId, hostPorts, layer, regionWidth, tabStrip, launcherEntries]
|
|
1834
|
+
() => ({ controller, windowId, ports: hostPorts, layer, regionWidth, tabStrip, launcherEntries, itemMenus }),
|
|
1835
|
+
[controller, windowId, hostPorts, layer, regionWidth, tabStrip, launcherEntries, itemMenus]
|
|
1730
1836
|
);
|
|
1731
1837
|
return /* @__PURE__ */ jsxs(CanvasContext.Provider, { value, children: [
|
|
1732
1838
|
children,
|
|
@@ -1771,7 +1877,7 @@ function useRegisterCanvasPresence() {
|
|
|
1771
1877
|
const context = useContext(CanvasContext);
|
|
1772
1878
|
const controller = context?.controller;
|
|
1773
1879
|
const windowId = context?.windowId;
|
|
1774
|
-
|
|
1880
|
+
useEffect3(() => controller?.registerPresentation(windowId), [controller, windowId]);
|
|
1775
1881
|
}
|
|
1776
1882
|
var noPresence = () => () => void 0;
|
|
1777
1883
|
function useCanvasIsPresented() {
|
|
@@ -1822,7 +1928,7 @@ function useCanvasLauncherEntry(entry) {
|
|
|
1822
1928
|
const store = useContext(CanvasContext)?.launcherEntries;
|
|
1823
1929
|
const fingerprint = JSON.stringify([entry.kind, entry.key, entry.title ?? null, entry.data ?? null]);
|
|
1824
1930
|
const icon = entry.icon;
|
|
1825
|
-
|
|
1931
|
+
useEffect3(() => {
|
|
1826
1932
|
if (!store) return void 0;
|
|
1827
1933
|
const [kind, key, title, data] = JSON.parse(fingerprint);
|
|
1828
1934
|
return store.add({ kind, key, title, data, ...icon ? { icon } : {} });
|
|
@@ -1844,9 +1950,9 @@ function useCanvasLauncherEntries() {
|
|
|
1844
1950
|
|
|
1845
1951
|
// src/react/CanvasColumn.tsx
|
|
1846
1952
|
import {
|
|
1847
|
-
useEffect as
|
|
1848
|
-
useLayoutEffect as
|
|
1849
|
-
useRef as
|
|
1953
|
+
useEffect as useEffect8,
|
|
1954
|
+
useLayoutEffect as useLayoutEffect6,
|
|
1955
|
+
useRef as useRef8,
|
|
1850
1956
|
useState as useState6,
|
|
1851
1957
|
useSyncExternalStore as useSyncExternalStore4
|
|
1852
1958
|
} from "react";
|
|
@@ -1858,19 +1964,19 @@ import {
|
|
|
1858
1964
|
Suspense,
|
|
1859
1965
|
lazy,
|
|
1860
1966
|
memo,
|
|
1861
|
-
useEffect as
|
|
1862
|
-
useLayoutEffect as
|
|
1967
|
+
useEffect as useEffect6,
|
|
1968
|
+
useLayoutEffect as useLayoutEffect4,
|
|
1863
1969
|
useMemo as useMemo2,
|
|
1864
|
-
useRef as
|
|
1970
|
+
useRef as useRef6,
|
|
1865
1971
|
useState as useState4
|
|
1866
1972
|
} from "react";
|
|
1867
1973
|
|
|
1868
1974
|
// src/react/chrome.tsx
|
|
1869
1975
|
import {
|
|
1870
1976
|
forwardRef,
|
|
1871
|
-
useEffect as
|
|
1872
|
-
useLayoutEffect as
|
|
1873
|
-
useRef as
|
|
1977
|
+
useEffect as useEffect4,
|
|
1978
|
+
useLayoutEffect as useLayoutEffect3,
|
|
1979
|
+
useRef as useRef4,
|
|
1874
1980
|
useState as useState3
|
|
1875
1981
|
} from "react";
|
|
1876
1982
|
|
|
@@ -1970,16 +2076,15 @@ var LayoutGridIcon = (p) => /* @__PURE__ */ jsxs2(Glyph, { ...p, children: [
|
|
|
1970
2076
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1971
2077
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1972
2078
|
] });
|
|
1973
|
-
var
|
|
1974
|
-
var ChevronRightIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m9 18 6-6-6-6" }) });
|
|
2079
|
+
var ChevronDownIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m6 9 6 6 6-6" }) });
|
|
1975
2080
|
|
|
1976
2081
|
// src/react/chrome.tsx
|
|
1977
2082
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1978
|
-
var
|
|
2083
|
+
var useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect4 : useLayoutEffect3;
|
|
1979
2084
|
function useTruncated(title) {
|
|
1980
|
-
const ref =
|
|
2085
|
+
const ref = useRef4(null);
|
|
1981
2086
|
const [truncated, setTruncated] = useState3(false);
|
|
1982
|
-
|
|
2087
|
+
useIsoLayoutEffect2(() => {
|
|
1983
2088
|
const el = ref.current;
|
|
1984
2089
|
if (!el) return void 0;
|
|
1985
2090
|
const check = () => setTruncated(el.scrollWidth - el.clientWidth > 1);
|
|
@@ -1993,15 +2098,15 @@ function useTruncated(title) {
|
|
|
1993
2098
|
var cx = (...names) => names.filter(Boolean).join(" ");
|
|
1994
2099
|
function useTooltip(text) {
|
|
1995
2100
|
const internals = useOptionalCanvasInternals();
|
|
1996
|
-
const timer =
|
|
1997
|
-
const hide =
|
|
2101
|
+
const timer = useRef4(null);
|
|
2102
|
+
const hide = useRef4(null);
|
|
1998
2103
|
const close = () => {
|
|
1999
2104
|
if (timer.current) clearTimeout(timer.current);
|
|
2000
2105
|
timer.current = null;
|
|
2001
2106
|
hide.current?.();
|
|
2002
2107
|
hide.current = null;
|
|
2003
2108
|
};
|
|
2004
|
-
|
|
2109
|
+
useEffect4(() => close, []);
|
|
2005
2110
|
if (!text) return { close, handlers: {} };
|
|
2006
2111
|
if (!internals) return { close, handlers: { title: text } };
|
|
2007
2112
|
const open = (element, delay) => {
|
|
@@ -2198,7 +2303,7 @@ function BrowserTabs({ tabs, activeId, onSelect, onClose, onNew, newLabel = "New
|
|
|
2198
2303
|
function BrowserBar({ url, canGoBack, canGoForward, onBack, onForward, onReload, onNavigate, trailing, className }) {
|
|
2199
2304
|
const [draft, setDraft] = useState3(url);
|
|
2200
2305
|
const [editing, setEditing] = useState3(false);
|
|
2201
|
-
|
|
2306
|
+
useEffect4(() => {
|
|
2202
2307
|
if (!editing) setDraft(url);
|
|
2203
2308
|
}, [url, editing]);
|
|
2204
2309
|
return /* @__PURE__ */ jsxs3("div", { "data-slot": "browser-bar", className: cx("mxc-browser-bar", className), children: [
|
|
@@ -2240,15 +2345,15 @@ function BrowserBar({ url, canGoBack, canGoForward, onBack, onForward, onReload,
|
|
|
2240
2345
|
}
|
|
2241
2346
|
|
|
2242
2347
|
// src/react/dnd.ts
|
|
2243
|
-
import { useEffect as
|
|
2348
|
+
import { useEffect as useEffect5, useRef as useRef5 } from "react";
|
|
2244
2349
|
var CANVAS_DRAG_MIME = "application/x-matrx-canvas-item";
|
|
2245
2350
|
function useTabDrag(itemId, title) {
|
|
2246
2351
|
const { layer } = useCanvasInternals();
|
|
2247
|
-
const active =
|
|
2352
|
+
const active = useRef5([]);
|
|
2248
2353
|
const end = () => {
|
|
2249
2354
|
for (const dispose of active.current.splice(0)) dispose();
|
|
2250
2355
|
};
|
|
2251
|
-
|
|
2356
|
+
useEffect5(() => end, []);
|
|
2252
2357
|
return {
|
|
2253
2358
|
draggable: true,
|
|
2254
2359
|
onDragStart: (event) => {
|
|
@@ -2263,8 +2368,8 @@ function useTabDrag(itemId, title) {
|
|
|
2263
2368
|
function usePaneDrop(paneId, getOutline) {
|
|
2264
2369
|
const canvas = useCanvas();
|
|
2265
2370
|
const { layer } = useCanvasInternals();
|
|
2266
|
-
const drop =
|
|
2267
|
-
|
|
2371
|
+
const drop = useRef5(null);
|
|
2372
|
+
useEffect5(() => () => drop.current?.(), []);
|
|
2268
2373
|
const hide = () => {
|
|
2269
2374
|
drop.current?.();
|
|
2270
2375
|
drop.current = null;
|
|
@@ -2395,6 +2500,9 @@ function useItemKind(kindId) {
|
|
|
2395
2500
|
const registered = useCanvasKind(kindId);
|
|
2396
2501
|
return kindId === CANVAS_LAUNCHER_KIND ? CANVAS_LAUNCHER_KIND_DEFINITION : registered;
|
|
2397
2502
|
}
|
|
2503
|
+
function resolveItemKind(kindId, lookup) {
|
|
2504
|
+
return kindId === CANVAS_LAUNCHER_KIND ? CANVAS_LAUNCHER_KIND_DEFINITION : lookup(kindId);
|
|
2505
|
+
}
|
|
2398
2506
|
|
|
2399
2507
|
// src/react/presentation.tsx
|
|
2400
2508
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
@@ -2489,7 +2597,7 @@ function useSlotSize(slots, paneId) {
|
|
|
2489
2597
|
|
|
2490
2598
|
// src/react/bodies.tsx
|
|
2491
2599
|
import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2492
|
-
var
|
|
2600
|
+
var useIsoLayoutEffect3 = typeof window === "undefined" ? useEffect6 : useLayoutEffect4;
|
|
2493
2601
|
var LAZY = /* @__PURE__ */ Symbol.for("ai-matrx.canvas.lazy");
|
|
2494
2602
|
function kindComponent(kind) {
|
|
2495
2603
|
if (kind.component) return kind.component;
|
|
@@ -2540,17 +2648,24 @@ function layerSignature(state, windowId, onlyPane) {
|
|
|
2540
2648
|
}
|
|
2541
2649
|
}
|
|
2542
2650
|
rows.sort((a, b) => a.born - b.born || a.hostId.localeCompare(b.hostId));
|
|
2543
|
-
const head = [
|
|
2651
|
+
const head = [
|
|
2652
|
+
frame2.isOpen ? "1" : "0",
|
|
2653
|
+
frame2.isFullscreen ? "1" : "0",
|
|
2654
|
+
frame2.focusedPaneId,
|
|
2655
|
+
String(paneIds.length),
|
|
2656
|
+
selectCanvasPresentedPaneId(state, windowId) ?? ""
|
|
2657
|
+
].join(SEP2);
|
|
2544
2658
|
return [head, ...rows.map((r) => r.row)].join(ROW);
|
|
2545
2659
|
}
|
|
2546
2660
|
function parseSignature(signature) {
|
|
2547
2661
|
if (!signature) return null;
|
|
2548
2662
|
const [head = "", ...rows] = signature.split(ROW);
|
|
2549
|
-
const [open, full, focusedPaneId = "", count = "1"] = head.split(SEP2);
|
|
2663
|
+
const [open, full, focusedPaneId = "", count = "1", presentedPaneId = ""] = head.split(SEP2);
|
|
2550
2664
|
return {
|
|
2551
2665
|
isOpen: open === "1",
|
|
2552
2666
|
isFullscreen: full === "1",
|
|
2553
2667
|
focusedPaneId,
|
|
2668
|
+
presentedPaneId,
|
|
2554
2669
|
paneCount: Number(count) || 1,
|
|
2555
2670
|
placed: rows.map((row) => {
|
|
2556
2671
|
const [hostId = "", itemId = "", paneId = "", active, kept] = row.split(SEP2);
|
|
@@ -2602,12 +2717,12 @@ function CanvasBodyLayer() {
|
|
|
2602
2717
|
const slots = useCanvasSlots();
|
|
2603
2718
|
const signature = useCanvasState((s) => layerSignature(s, windowId));
|
|
2604
2719
|
const single = useCanvasSinglePane();
|
|
2605
|
-
const root =
|
|
2720
|
+
const root = useRef6(null);
|
|
2606
2721
|
const [placement] = useState4(
|
|
2607
2722
|
() => slots ? createPlacement(() => root.current, slots, () => layer.native.remeasure()) : null
|
|
2608
2723
|
);
|
|
2609
|
-
|
|
2610
|
-
|
|
2724
|
+
useIsoLayoutEffect3(() => placement?.place());
|
|
2725
|
+
useIsoLayoutEffect3(() => slots && placement ? slots.subscribe(placement.place) : void 0, [slots, placement]);
|
|
2611
2726
|
const shape = parseSignature(signature);
|
|
2612
2727
|
if (!slots || !placement || !shape) return null;
|
|
2613
2728
|
return /* @__PURE__ */ jsx6("div", { ref: root, className: "mxc-bodies", "data-mxc-bodies": windowId, children: shape.placed.map(({ hostId, itemId, paneId, active, kept }) => /* @__PURE__ */ jsx6(
|
|
@@ -2617,7 +2732,7 @@ function CanvasBodyLayer() {
|
|
|
2617
2732
|
itemId,
|
|
2618
2733
|
kept,
|
|
2619
2734
|
paneId,
|
|
2620
|
-
visible: shape.isOpen && active && (!single || paneId === shape.
|
|
2735
|
+
visible: shape.isOpen && active && (!single || paneId === shape.presentedPaneId),
|
|
2621
2736
|
focused: active && shape.focusedPaneId === paneId,
|
|
2622
2737
|
isFullscreen: shape.isFullscreen,
|
|
2623
2738
|
paneCount: shape.paneCount,
|
|
@@ -2683,13 +2798,13 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2683
2798
|
const keptItem = useMemo2(() => step ? stepAsItem(step, owner ?? 0) : void 0, [step, owner]);
|
|
2684
2799
|
const item = kept ? keptItem : live;
|
|
2685
2800
|
const kind = useItemKind(item?.kind ?? "");
|
|
2686
|
-
const element =
|
|
2801
|
+
const element = useRef6(null);
|
|
2687
2802
|
const [everShown, setEverShown] = useState4(visible);
|
|
2688
2803
|
if (visible && !everShown) setEverShown(true);
|
|
2689
2804
|
const mounted = kind?.unmountWhenHidden ? visible : everShown;
|
|
2690
2805
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
2691
2806
|
const drop = usePaneDrop(paneId, () => slots.section(paneId));
|
|
2692
|
-
|
|
2807
|
+
useIsoLayoutEffect3(() => {
|
|
2693
2808
|
const el = element.current;
|
|
2694
2809
|
if (!placement || !el) return void 0;
|
|
2695
2810
|
return placement.register(hostId, { element: el, paneId });
|
|
@@ -2775,8 +2890,8 @@ function KindRender({ kind, props }) {
|
|
|
2775
2890
|
}
|
|
2776
2891
|
function NativeBody({ itemId, windowId, visible, children }) {
|
|
2777
2892
|
const { layer } = useCanvasInternals();
|
|
2778
|
-
const ref =
|
|
2779
|
-
const measure =
|
|
2893
|
+
const ref = useRef6(null);
|
|
2894
|
+
const measure = useRef6(() => void 0);
|
|
2780
2895
|
measure.current = () => {
|
|
2781
2896
|
const el = ref.current;
|
|
2782
2897
|
const box = visible && el?.isConnected ? el.getBoundingClientRect() : null;
|
|
@@ -2787,8 +2902,8 @@ function NativeBody({ itemId, windowId, visible, children }) {
|
|
|
2787
2902
|
const rect = { ...toCanvasRect(box), devicePixelRatio: window.devicePixelRatio || 1 };
|
|
2788
2903
|
layer.native.report(itemId, rect, { visible: !layer.isBlocked(), windowId });
|
|
2789
2904
|
};
|
|
2790
|
-
|
|
2791
|
-
|
|
2905
|
+
useIsoLayoutEffect3(() => measure.current());
|
|
2906
|
+
useIsoLayoutEffect3(() => {
|
|
2792
2907
|
const el = ref.current;
|
|
2793
2908
|
const run = () => measure.current();
|
|
2794
2909
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(run);
|
|
@@ -2840,11 +2955,28 @@ var ItemBoundary = class extends Component {
|
|
|
2840
2955
|
}
|
|
2841
2956
|
};
|
|
2842
2957
|
|
|
2958
|
+
// src/react/resizeGesture.ts
|
|
2959
|
+
function beginCanvasResize(mode) {
|
|
2960
|
+
if (typeof document === "undefined") return () => void 0;
|
|
2961
|
+
const root = document.documentElement;
|
|
2962
|
+
root.dataset.mxcResizing = mode;
|
|
2963
|
+
document.getSelection?.()?.removeAllRanges();
|
|
2964
|
+
const refuse = (event) => event.preventDefault();
|
|
2965
|
+
document.addEventListener("selectstart", refuse, true);
|
|
2966
|
+
let ended = false;
|
|
2967
|
+
return () => {
|
|
2968
|
+
if (ended) return;
|
|
2969
|
+
ended = true;
|
|
2970
|
+
delete root.dataset.mxcResizing;
|
|
2971
|
+
document.removeEventListener("selectstart", refuse, true);
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2843
2975
|
// src/react/CanvasPane.tsx
|
|
2844
|
-
import { Component as Component2, useEffect as
|
|
2976
|
+
import { Component as Component2, useEffect as useEffect7, useLayoutEffect as useLayoutEffect5, useRef as useRef7, useState as useState5 } from "react";
|
|
2845
2977
|
import * as Menu from "@radix-ui/react-dropdown-menu";
|
|
2846
2978
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2847
|
-
var
|
|
2979
|
+
var useIsoLayoutEffect4 = typeof window === "undefined" ? useEffect7 : useLayoutEffect5;
|
|
2848
2980
|
function itemTitle(item, kind) {
|
|
2849
2981
|
if (item.title) return item.title;
|
|
2850
2982
|
if (item.kind === CANVAS_LAUNCHER_KIND) return "New tab";
|
|
@@ -2894,14 +3026,15 @@ function CanvasPaneView({ paneId }) {
|
|
|
2894
3026
|
const paneCount = useCanvasState((s) => selectCanvasPaneCount(s, windowId));
|
|
2895
3027
|
const role = useCanvasState((s) => windowId === CANVAS_MAIN_WINDOW ? "main" : s.windows[windowId]?.role ?? "window");
|
|
2896
3028
|
const canGoBack = useCanvasState((s) => active ? selectCanvasCanGoBack(s, active.id) : false);
|
|
3029
|
+
const canGoForward = useCanvasState((s) => active ? selectCanvasCanGoForward(s, active.id) : false);
|
|
2897
3030
|
const kind = useItemKind(active?.kind ?? "");
|
|
2898
3031
|
const singlePane = useCanvasSinglePane();
|
|
2899
3032
|
const windowTabs = useCanvasState((s) => singlePane ? windowTabsSignature(s, windowId) : "");
|
|
2900
|
-
const sectionRef =
|
|
2901
|
-
const bodyRef =
|
|
3033
|
+
const sectionRef = useRef7(null);
|
|
3034
|
+
const bodyRef = useRef7(null);
|
|
2902
3035
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
2903
3036
|
const drop = usePaneDrop(paneId, () => sectionRef.current);
|
|
2904
|
-
|
|
3037
|
+
useIsoLayoutEffect4(() => {
|
|
2905
3038
|
const body = bodyRef.current;
|
|
2906
3039
|
const section = sectionRef.current;
|
|
2907
3040
|
if (body) slots.set(paneId, body);
|
|
@@ -2929,7 +3062,17 @@ function CanvasPaneView({ paneId }) {
|
|
|
2929
3062
|
focused,
|
|
2930
3063
|
"data-mxc-pane": paneId,
|
|
2931
3064
|
header: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
2932
|
-
canGoBack && active ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-back", label: "Back", onClick: () => void canvas.back(active.id), children: /* @__PURE__ */ jsx7(ArrowLeftIcon, {}) }) : null,
|
|
3065
|
+
canGoBack && active ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-back", label: "Back", shortcut: canvasShortcutHint("canvas.back", isMac()), onClick: () => void canvas.back(active.id), children: /* @__PURE__ */ jsx7(ArrowLeftIcon, {}) }) : null,
|
|
3066
|
+
canGoForward && active ? /* @__PURE__ */ jsx7(
|
|
3067
|
+
CanvasHeaderButton,
|
|
3068
|
+
{
|
|
3069
|
+
className: "mxc-forward",
|
|
3070
|
+
label: "Forward",
|
|
3071
|
+
shortcut: canvasShortcutHint("canvas.forward", isMac()),
|
|
3072
|
+
onClick: () => void canvas.forward(active.id),
|
|
3073
|
+
children: /* @__PURE__ */ jsx7(ArrowRightIcon, {})
|
|
3074
|
+
}
|
|
3075
|
+
) : null,
|
|
2933
3076
|
single && active ? /* @__PURE__ */ jsx7(SingleTitle, { item: active, kind }) : tabs.length > 0 ? /* @__PURE__ */ jsx7(TabStrip, { tabs, activeItemId: pane.activeItemId }) : /* @__PURE__ */ jsx7("span", { className: "mxc-panel-title", children: "New tab" }),
|
|
2934
3077
|
/* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-new-tab", label: "New tab", onClick: () => void canvas.newTab(paneId), children: /* @__PURE__ */ jsx7(PlusIcon, {}) })
|
|
2935
3078
|
] }),
|
|
@@ -2938,7 +3081,19 @@ function CanvasPaneView({ paneId }) {
|
|
|
2938
3081
|
// Its own gapless row: a kind's tap buttons space themselves (the tap-target placement rule).
|
|
2939
3082
|
/* @__PURE__ */ jsx7("span", { className: "mxc-kind-actions", children: /* @__PURE__ */ jsx7(Action, { ...kindProps }) })
|
|
2940
3083
|
) : null,
|
|
2941
|
-
renderMenu: () => /* @__PURE__ */ jsx7(
|
|
3084
|
+
renderMenu: () => /* @__PURE__ */ jsx7(
|
|
3085
|
+
PaneMenu,
|
|
3086
|
+
{
|
|
3087
|
+
paneId,
|
|
3088
|
+
windowId,
|
|
3089
|
+
activeItem: active,
|
|
3090
|
+
kind,
|
|
3091
|
+
kindProps,
|
|
3092
|
+
tabCount: pane.itemIds.length,
|
|
3093
|
+
role,
|
|
3094
|
+
canSplit: !singlePane
|
|
3095
|
+
}
|
|
3096
|
+
),
|
|
2942
3097
|
expanded: isFullscreen,
|
|
2943
3098
|
onToggleExpand: () => canvas.setFullscreen(!isFullscreen),
|
|
2944
3099
|
expandShortcut: canvasShortcutHint("canvas.toggleFullscreen", isMac()),
|
|
@@ -2972,77 +3127,135 @@ function SingleTitle({ item, kind }) {
|
|
|
2972
3127
|
const canvas = useCanvas();
|
|
2973
3128
|
const title = itemTitle(item, kind);
|
|
2974
3129
|
const drag = useTabDrag(item.id, title);
|
|
3130
|
+
const name = useTruncated(title);
|
|
2975
3131
|
const Icon = kind?.icon ?? AlertIcon;
|
|
2976
|
-
return /* @__PURE__ */ jsxs6("span", { className: "mxc-panel-title mxc-single-title", "data-draggable": "", title, ...drag, children: [
|
|
3132
|
+
return /* @__PURE__ */ jsxs6("span", { className: "mxc-panel-title mxc-single-title", "data-draggable": "", "data-truncated": name.truncated ? "" : void 0, title, ...drag, children: [
|
|
2977
3133
|
/* @__PURE__ */ jsx7(Icon, { className: "mxc-title-icon" }),
|
|
2978
|
-
/* @__PURE__ */ jsx7("span", { className: "mxc-tab-title", children: title }),
|
|
3134
|
+
/* @__PURE__ */ jsx7("span", { ref: name.ref, className: "mxc-tab-title", children: title }),
|
|
2979
3135
|
/* @__PURE__ */ jsx7("button", { type: "button", className: "mxc-tab-close", "data-slot": "browser-tab-close", "aria-label": `Close ${title}`, onClick: () => canvas.close(item.id), children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
|
|
2980
3136
|
] });
|
|
2981
3137
|
}
|
|
3138
|
+
var CANVAS_TAB_MIN_WIDTH = 78;
|
|
3139
|
+
var CANVAS_ACTIVE_TAB_MIN_WIDTH = 100;
|
|
3140
|
+
var TAB_LIST_BUTTON_ROOM = 30;
|
|
3141
|
+
var TAB_GAP = 2;
|
|
3142
|
+
function fitCanvasTabs(count, activeIndex, width) {
|
|
3143
|
+
if (count <= 0) return { start: 0, end: 0, overflow: false, compact: false };
|
|
3144
|
+
const need = (n) => CANVAS_ACTIVE_TAB_MIN_WIDTH + (n - 1) * (CANVAS_TAB_MIN_WIDTH + TAB_GAP);
|
|
3145
|
+
if (width <= 0 || need(count) <= width) return { start: 0, end: count, overflow: false, compact: false };
|
|
3146
|
+
if (count === 1) return { start: 0, end: 1, overflow: false, compact: true };
|
|
3147
|
+
let fit = 1;
|
|
3148
|
+
while (fit + 1 < count && need(fit + 1) + TAB_LIST_BUTTON_ROOM <= width) fit += 1;
|
|
3149
|
+
const active = Math.min(Math.max(activeIndex, 0), count - 1);
|
|
3150
|
+
const start = active < fit ? 0 : active - fit + 1;
|
|
3151
|
+
return { start, end: start + fit, overflow: true, compact: need(fit) + TAB_LIST_BUTTON_ROOM > width };
|
|
3152
|
+
}
|
|
2982
3153
|
function TabStrip({ tabs, activeItemId }) {
|
|
2983
|
-
const
|
|
2984
|
-
const
|
|
2985
|
-
const
|
|
2986
|
-
|
|
2987
|
-
const el = strip.current;
|
|
2988
|
-
if (!el) return;
|
|
2989
|
-
const start = el.scrollLeft > 1;
|
|
2990
|
-
const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
2991
|
-
setOverflow((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
2992
|
-
};
|
|
2993
|
-
const tabKey = tabs.map((tab) => tab.itemId).join("|");
|
|
2994
|
-
useIsoLayoutEffect3(() => {
|
|
3154
|
+
const canvas = useCanvas();
|
|
3155
|
+
const strip = useRef7(null);
|
|
3156
|
+
const [width, setWidth] = useState5(0);
|
|
3157
|
+
useIsoLayoutEffect4(() => {
|
|
2995
3158
|
const el = strip.current;
|
|
2996
3159
|
if (!el) return void 0;
|
|
2997
|
-
const
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
const left = tab.offsetLeft;
|
|
3001
|
-
const right = left + tab.offsetWidth;
|
|
3002
|
-
if (left < el.scrollLeft) el.scrollLeft = left;
|
|
3003
|
-
else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth;
|
|
3004
|
-
}
|
|
3005
|
-
measure.current();
|
|
3006
|
-
};
|
|
3007
|
-
reveal();
|
|
3008
|
-
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(reveal);
|
|
3160
|
+
const read = () => setWidth(Math.floor(el.getBoundingClientRect().width));
|
|
3161
|
+
read();
|
|
3162
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(read);
|
|
3009
3163
|
observer?.observe(el);
|
|
3010
|
-
for (const tab of el.querySelectorAll('[role="tab"]')) observer?.observe(tab);
|
|
3011
3164
|
return () => observer?.disconnect();
|
|
3012
|
-
}, [
|
|
3013
|
-
const
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
if (typeof el.scrollBy === "function") el.scrollBy({ left: by, behavior: "smooth" });
|
|
3018
|
-
else el.scrollLeft += by;
|
|
3019
|
-
};
|
|
3020
|
-
const overflowing = overflow.start || overflow.end;
|
|
3165
|
+
}, []);
|
|
3166
|
+
const activeIndex = Math.max(0, tabs.findIndex((tab) => tab.itemId === activeItemId));
|
|
3167
|
+
const fit = fitCanvasTabs(tabs.length, activeIndex, width);
|
|
3168
|
+
const shown = tabs.slice(fit.start, fit.end);
|
|
3169
|
+
const hidden = tabs.length - shown.length;
|
|
3021
3170
|
return /* @__PURE__ */ jsxs6(
|
|
3022
3171
|
"div",
|
|
3023
3172
|
{
|
|
3173
|
+
ref: strip,
|
|
3024
3174
|
className: "mxc-tabstrip",
|
|
3025
|
-
"data-overflow":
|
|
3026
|
-
"data-
|
|
3027
|
-
"
|
|
3175
|
+
"data-overflow": fit.overflow ? "" : void 0,
|
|
3176
|
+
"data-compact": fit.compact ? "" : void 0,
|
|
3177
|
+
style: { ["--mxc-tab-min"]: `${CANVAS_TAB_MIN_WIDTH}px`, ["--mxc-tab-active-min"]: `${CANVAS_ACTIVE_TAB_MIN_WIDTH}px` },
|
|
3028
3178
|
children: [
|
|
3029
|
-
overflowing ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs left", available: overflow.start, onClick: () => page(-1), children: /* @__PURE__ */ jsx7(ChevronLeftIcon, {}) }) : null,
|
|
3030
3179
|
/* @__PURE__ */ jsx7(
|
|
3031
3180
|
"div",
|
|
3032
3181
|
{
|
|
3033
|
-
ref: strip,
|
|
3034
3182
|
className: "mxc-tabs",
|
|
3035
3183
|
role: "tablist",
|
|
3036
3184
|
"aria-label": "Canvas tabs",
|
|
3037
|
-
onScroll: () => measure.current(),
|
|
3038
3185
|
onWheel: (event) => {
|
|
3039
|
-
|
|
3040
|
-
|
|
3186
|
+
if (!fit.overflow || Math.abs(event.deltaY) < Math.abs(event.deltaX) || tabs.length < 2) return;
|
|
3187
|
+
const next = tabs[(activeIndex + (event.deltaY > 0 ? 1 : -1) + tabs.length) % tabs.length];
|
|
3188
|
+
if (next) canvas.activate(next.itemId);
|
|
3041
3189
|
},
|
|
3042
|
-
children:
|
|
3190
|
+
children: shown.map((tab) => /* @__PURE__ */ jsx7(ItemTab, { itemId: tab.itemId, paneId: tab.paneId, index: tab.index, active: tab.itemId === activeItemId }, tab.itemId))
|
|
3043
3191
|
}
|
|
3044
3192
|
),
|
|
3045
|
-
|
|
3193
|
+
fit.overflow ? /* @__PURE__ */ jsx7(TabListMenu, { tabs, activeItemId, hidden }) : null
|
|
3194
|
+
]
|
|
3195
|
+
}
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
function TabListMenu({ tabs, activeItemId, hidden }) {
|
|
3199
|
+
const canvas = useCanvas();
|
|
3200
|
+
const { ports, layer } = useCanvasInternals();
|
|
3201
|
+
const blocking = useRef7(null);
|
|
3202
|
+
useEffect7(() => () => blocking.current?.(), []);
|
|
3203
|
+
const label = `All tabs (${tabs.length})`;
|
|
3204
|
+
const trigger = (props) => /* @__PURE__ */ jsxs6(CanvasHeaderButton, { className: "mxc-tab-list", label, "data-hidden-count": hidden, ...props, children: [
|
|
3205
|
+
/* @__PURE__ */ jsx7(ChevronDownIcon, {}),
|
|
3206
|
+
/* @__PURE__ */ jsx7("span", { className: "mxc-tab-list-count", "aria-hidden": "true", children: hidden })
|
|
3207
|
+
] });
|
|
3208
|
+
const showMenu = ports.showMenu;
|
|
3209
|
+
if (showMenu) {
|
|
3210
|
+
return trigger({
|
|
3211
|
+
onClick: (event) => {
|
|
3212
|
+
const state = canvas.getState();
|
|
3213
|
+
const entries = tabs.map((tab) => {
|
|
3214
|
+
const item = state.items[tab.itemId];
|
|
3215
|
+
const title = item ? itemTitle(item, resolveItemKind(item.kind, getCanvasKind)) : tab.itemId;
|
|
3216
|
+
return { type: "item", id: tab.itemId, label: tab.itemId === activeItemId ? `${title} (current)` : title };
|
|
3217
|
+
});
|
|
3218
|
+
void showMenu(entries, toCanvasRect(event.currentTarget.getBoundingClientRect())).then(
|
|
3219
|
+
(chosen) => {
|
|
3220
|
+
if (chosen) canvas.activate(chosen);
|
|
3221
|
+
},
|
|
3222
|
+
(error) => console.error("[@ai-matrx/canvas] the host menu failed", error)
|
|
3223
|
+
);
|
|
3224
|
+
}
|
|
3225
|
+
});
|
|
3226
|
+
}
|
|
3227
|
+
return /* @__PURE__ */ jsxs6(
|
|
3228
|
+
Menu.Root,
|
|
3229
|
+
{
|
|
3230
|
+
onOpenChange: (open) => {
|
|
3231
|
+
blocking.current?.();
|
|
3232
|
+
blocking.current = open ? layer.beginBlocking() : null;
|
|
3233
|
+
},
|
|
3234
|
+
children: [
|
|
3235
|
+
/* @__PURE__ */ jsx7(Menu.Trigger, { asChild: true, children: trigger({}) }),
|
|
3236
|
+
/* @__PURE__ */ jsx7(Menu.Portal, { children: /* @__PURE__ */ jsx7(Menu.Content, { align: "end", sideOffset: 4, className: "mxc-menu mxc-tab-list-menu", "aria-label": "All tabs", children: tabs.map((tab) => /* @__PURE__ */ jsx7(TabListEntry, { itemId: tab.itemId, active: tab.itemId === activeItemId }, tab.itemId)) }) })
|
|
3237
|
+
]
|
|
3238
|
+
}
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
function TabListEntry({ itemId, active }) {
|
|
3242
|
+
const canvas = useCanvas();
|
|
3243
|
+
const item = useCanvasState((s) => s.items[itemId]);
|
|
3244
|
+
const kind = useItemKind(item?.kind ?? "");
|
|
3245
|
+
if (!item) return null;
|
|
3246
|
+
const Icon = kind?.icon ?? AlertIcon;
|
|
3247
|
+
const title = itemTitle(item, kind);
|
|
3248
|
+
return /* @__PURE__ */ jsxs6(
|
|
3249
|
+
Menu.Item,
|
|
3250
|
+
{
|
|
3251
|
+
className: "mxc-menu-item mxc-tab-list-item",
|
|
3252
|
+
"data-active": active ? "" : void 0,
|
|
3253
|
+
"aria-current": active ? "true" : void 0,
|
|
3254
|
+
title,
|
|
3255
|
+
onSelect: () => canvas.activate(itemId),
|
|
3256
|
+
children: [
|
|
3257
|
+
/* @__PURE__ */ jsx7(Icon, {}),
|
|
3258
|
+
/* @__PURE__ */ jsx7("span", { className: "mxc-tab-list-title", children: title })
|
|
3046
3259
|
]
|
|
3047
3260
|
}
|
|
3048
3261
|
);
|
|
@@ -3086,42 +3299,48 @@ function PaneMenu({
|
|
|
3086
3299
|
kind,
|
|
3087
3300
|
kindProps,
|
|
3088
3301
|
tabCount,
|
|
3089
|
-
role
|
|
3302
|
+
role,
|
|
3303
|
+
canSplit
|
|
3090
3304
|
}) {
|
|
3091
3305
|
const canvas = useCanvas();
|
|
3092
|
-
const { ports, layer } = useCanvasInternals();
|
|
3306
|
+
const { ports, layer, itemMenus } = useCanvasInternals();
|
|
3093
3307
|
const moveId = tabCount > 1 ? activeItem?.id : void 0;
|
|
3094
|
-
const blocking =
|
|
3095
|
-
|
|
3096
|
-
const
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
[
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3308
|
+
const blocking = useRef7(null);
|
|
3309
|
+
useEffect7(() => () => blocking.current?.(), []);
|
|
3310
|
+
const buildSections = () => {
|
|
3311
|
+
const kindItems = kind?.menuItems && kindProps ? safeMenu(() => kind.menuItems?.(kindProps)) : [];
|
|
3312
|
+
const hostItems = activeItem && ports.itemActions ? safeMenu(() => ports.itemActions?.(activeItem, kind)) : [];
|
|
3313
|
+
const bodyItems = activeItem ? itemMenus.read(activeItem.id) : [];
|
|
3314
|
+
return [
|
|
3315
|
+
[...kindItems, ...bodyItems, ...hostItems].map((entry) => ({ ...entry, id: `item:${entry.id}` })),
|
|
3316
|
+
[
|
|
3317
|
+
...canSplit ? [
|
|
3318
|
+
{ id: "split-right", label: "Split right", icon: /* @__PURE__ */ jsx7(SplitRightIcon, {}), onSelect: () => canvas.splitPane(paneId, "horizontal", moveId) },
|
|
3319
|
+
{ id: "split-down", label: "Split down", icon: /* @__PURE__ */ jsx7(SplitDownIcon, {}), onSelect: () => canvas.splitPane(paneId, "vertical", moveId) }
|
|
3320
|
+
] : [],
|
|
3321
|
+
...activeItem && role === "pop-out" ? [{ id: "dock-back", label: "Dock back", icon: /* @__PURE__ */ jsx7(DockIcon, {}), onSelect: () => void canvas.dockBack(activeItem.id) }] : activeItem && ports.popOut ? [{ id: "pop-out", label: "Pop out", icon: /* @__PURE__ */ jsx7(PopOutIcon, {}), onSelect: () => void canvas.popOut(activeItem.id) }] : []
|
|
3322
|
+
],
|
|
3323
|
+
activeItem ? [
|
|
3324
|
+
{ id: "close-tab", label: "Close tab", onSelect: () => canvas.close(activeItem.id) },
|
|
3325
|
+
...tabCount > 1 ? [{ id: "close-others", label: "Close other tabs", onSelect: () => canvas.closeOthers(activeItem.id) }] : []
|
|
3326
|
+
] : []
|
|
3327
|
+
].filter((section) => section.length > 0);
|
|
3328
|
+
};
|
|
3110
3329
|
const showMenu = ports.showMenu;
|
|
3111
3330
|
if (showMenu) {
|
|
3112
|
-
const entries = [];
|
|
3113
|
-
const handlers = /* @__PURE__ */ new Map();
|
|
3114
|
-
sections.forEach((section, i) => {
|
|
3115
|
-
if (i > 0) entries.push({ type: "separator" });
|
|
3116
|
-
for (const action of section) {
|
|
3117
|
-
entries.push({ type: "item", id: action.id, label: action.label, ...action.destructive ? { destructive: true } : {} });
|
|
3118
|
-
handlers.set(action.id, action.onSelect);
|
|
3119
|
-
}
|
|
3120
|
-
});
|
|
3121
3331
|
return /* @__PURE__ */ jsx7(
|
|
3122
3332
|
CanvasMenuTrigger,
|
|
3123
3333
|
{
|
|
3124
3334
|
onClick: (event) => {
|
|
3335
|
+
const entries = [];
|
|
3336
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
3337
|
+
buildSections().forEach((section, i) => {
|
|
3338
|
+
if (i > 0) entries.push({ type: "separator" });
|
|
3339
|
+
for (const action of section) {
|
|
3340
|
+
entries.push({ type: "item", id: action.id, label: action.label, ...action.destructive ? { destructive: true } : {} });
|
|
3341
|
+
handlers.set(action.id, action.onSelect);
|
|
3342
|
+
}
|
|
3343
|
+
});
|
|
3125
3344
|
const anchor = toCanvasRect(event.currentTarget.getBoundingClientRect());
|
|
3126
3345
|
void showMenu(entries, anchor).then(
|
|
3127
3346
|
(chosen) => {
|
|
@@ -3142,11 +3361,14 @@ function PaneMenu({
|
|
|
3142
3361
|
},
|
|
3143
3362
|
children: [
|
|
3144
3363
|
/* @__PURE__ */ jsx7(Menu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx7(CanvasMenuTrigger, {}) }),
|
|
3145
|
-
/* @__PURE__ */ jsx7(Menu.Portal, { children: /* @__PURE__ */ jsx7(Menu.Content, { align: "end", sideOffset: 4, className: "mxc-menu", children:
|
|
3364
|
+
/* @__PURE__ */ jsx7(Menu.Portal, { children: /* @__PURE__ */ jsx7(Menu.Content, { align: "end", sideOffset: 4, className: "mxc-menu", children: /* @__PURE__ */ jsx7(MenuSections, { build: buildSections }) }) })
|
|
3146
3365
|
]
|
|
3147
3366
|
}
|
|
3148
3367
|
);
|
|
3149
3368
|
}
|
|
3369
|
+
function MenuSections({ build }) {
|
|
3370
|
+
return /* @__PURE__ */ jsx7(Fragment2, { children: build().map((section, i) => /* @__PURE__ */ jsx7(MenuSection, { first: i === 0, actions: section }, i)) });
|
|
3371
|
+
}
|
|
3150
3372
|
function MenuSection({ first, actions }) {
|
|
3151
3373
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
3152
3374
|
first ? null : /* @__PURE__ */ jsx7(Menu.Separator, { className: "mxc-menu-separator" }),
|
|
@@ -3167,7 +3389,7 @@ function safeMenu(build) {
|
|
|
3167
3389
|
|
|
3168
3390
|
// src/react/CanvasColumn.tsx
|
|
3169
3391
|
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
3170
|
-
var
|
|
3392
|
+
var useIsoLayoutEffect5 = typeof window === "undefined" ? useEffect8 : useLayoutEffect6;
|
|
3171
3393
|
function subscribeViewport(listener) {
|
|
3172
3394
|
window.addEventListener("resize", listener);
|
|
3173
3395
|
return () => window.removeEventListener("resize", listener);
|
|
@@ -3196,15 +3418,15 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3196
3418
|
const isFullscreen = useCanvasState((s) => selectCanvasIsFullscreen(s, windowId));
|
|
3197
3419
|
const width = useCanvasState((s) => selectCanvasWidth(s, windowId));
|
|
3198
3420
|
const layout = useCanvasState((s) => selectCanvasLayout(s, windowId));
|
|
3199
|
-
const
|
|
3421
|
+
const presentedPaneId = useCanvasState((s) => selectCanvasPresentedPaneId(s, windowId));
|
|
3200
3422
|
const region = useRegionWidth();
|
|
3201
3423
|
const viewport = useViewportWidth();
|
|
3202
3424
|
const singlePane = placement === "docked" && viewport > 0 && viewport <= CANVAS_PHONE_MAX_WIDTH;
|
|
3203
3425
|
const [dragWidth, setDragWidth] = useState6(null);
|
|
3204
3426
|
const [slots] = useState6(createCanvasSlots);
|
|
3205
|
-
const ref =
|
|
3427
|
+
const ref = useRef8(null);
|
|
3206
3428
|
useRegisterCanvasPresence();
|
|
3207
|
-
|
|
3429
|
+
useEffect8(() => {
|
|
3208
3430
|
const el = ref.current;
|
|
3209
3431
|
if (!el) return void 0;
|
|
3210
3432
|
let frame2 = 0;
|
|
@@ -3265,7 +3487,7 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3265
3487
|
}
|
|
3266
3488
|
) : null,
|
|
3267
3489
|
/* @__PURE__ */ jsx8(CanvasSlotsContext.Provider, { value: slots, children: /* @__PURE__ */ jsxs7(CanvasSinglePaneContext.Provider, { value: singlePane, children: [
|
|
3268
|
-
/* @__PURE__ */ jsx8("div", { className: "mxc-layout", "data-single-pane": singlePane ? "" : void 0, children: singlePane ? /* @__PURE__ */ jsx8(CanvasPaneView, { paneId:
|
|
3490
|
+
/* @__PURE__ */ jsx8("div", { className: "mxc-layout", "data-single-pane": singlePane ? "" : void 0, children: singlePane ? /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: presentedPaneId ?? listPaneIds(layout)[0] }) : /* @__PURE__ */ jsx8(LayoutNodeView, { node: layout }) }),
|
|
3269
3491
|
/* @__PURE__ */ jsx8(CanvasBodyLayer, {})
|
|
3270
3492
|
] }) })
|
|
3271
3493
|
] })
|
|
@@ -3274,13 +3496,14 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3274
3496
|
}
|
|
3275
3497
|
function WidthHandle({ width, region, onDrag }) {
|
|
3276
3498
|
const canvas = useCanvas();
|
|
3277
|
-
const start =
|
|
3499
|
+
const start = useRef8(null);
|
|
3500
|
+
useEffect8(() => () => start.current?.end(), []);
|
|
3278
3501
|
const clamp = (next) => clampDraggedCanvasWidth(next, region, canvas.rules);
|
|
3279
3502
|
const onPointerDown = (event) => {
|
|
3280
3503
|
event.preventDefault();
|
|
3281
3504
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
3282
|
-
start.current
|
|
3283
|
-
|
|
3505
|
+
start.current?.end();
|
|
3506
|
+
start.current = { x: event.clientX, width, end: beginCanvasResize("") };
|
|
3284
3507
|
};
|
|
3285
3508
|
const onPointerMove = (event) => {
|
|
3286
3509
|
if (!start.current) return;
|
|
@@ -3289,8 +3512,8 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3289
3512
|
const finish = (event) => {
|
|
3290
3513
|
if (!start.current) return;
|
|
3291
3514
|
const next = clamp(start.current.width + (start.current.x - event.clientX));
|
|
3515
|
+
start.current.end();
|
|
3292
3516
|
start.current = null;
|
|
3293
|
-
delete document.documentElement.dataset.mxcResizing;
|
|
3294
3517
|
onDrag(null);
|
|
3295
3518
|
canvas.setWidth(next);
|
|
3296
3519
|
};
|
|
@@ -3307,6 +3530,7 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3307
3530
|
onPointerMove,
|
|
3308
3531
|
onPointerUp: finish,
|
|
3309
3532
|
onPointerCancel: finish,
|
|
3533
|
+
onLostPointerCapture: finish,
|
|
3310
3534
|
onDoubleClick: () => canvas.setWidth(clamp(Math.round(region / 2))),
|
|
3311
3535
|
onKeyDown: (event) => {
|
|
3312
3536
|
if (event.key === "ArrowLeft") canvas.setWidth(clamp(width + 32));
|
|
@@ -3315,17 +3539,13 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3315
3539
|
}
|
|
3316
3540
|
);
|
|
3317
3541
|
}
|
|
3318
|
-
function presentedPane(layout, focused) {
|
|
3319
|
-
const ids = listPaneIds(layout);
|
|
3320
|
-
return ids.find((id) => id === focused) ?? ids[0];
|
|
3321
|
-
}
|
|
3322
3542
|
function LayoutNodeView({ node }) {
|
|
3323
3543
|
if (node.type === "pane") return /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: node.paneId });
|
|
3324
3544
|
return /* @__PURE__ */ jsx8(SplitView, { node });
|
|
3325
3545
|
}
|
|
3326
3546
|
function useMeasuredWidth(ref, enabled) {
|
|
3327
3547
|
const [width, setWidth] = useState6(0);
|
|
3328
|
-
|
|
3548
|
+
useIsoLayoutEffect5(() => {
|
|
3329
3549
|
const el = ref.current;
|
|
3330
3550
|
if (!enabled || !el) return;
|
|
3331
3551
|
setWidth(el.getBoundingClientRect().width);
|
|
@@ -3338,7 +3558,7 @@ function useMeasuredWidth(ref, enabled) {
|
|
|
3338
3558
|
}
|
|
3339
3559
|
function SplitView({ node }) {
|
|
3340
3560
|
const canvas = useCanvas();
|
|
3341
|
-
const ref =
|
|
3561
|
+
const ref = useRef8(null);
|
|
3342
3562
|
const [liveSizes, setLiveSizes] = useState6(null);
|
|
3343
3563
|
const sizes = liveSizes ?? node.sizes;
|
|
3344
3564
|
const measuredWidth = useMeasuredWidth(ref, node.orientation === "horizontal" && canvas.rules.paneMinWidth > 0);
|
|
@@ -3356,7 +3576,7 @@ function SplitView({ node }) {
|
|
|
3356
3576
|
const base = [...node.sizes];
|
|
3357
3577
|
const pair = (base[index] ?? 0) + (base[index + 1] ?? 0);
|
|
3358
3578
|
const target = event.currentTarget;
|
|
3359
|
-
|
|
3579
|
+
const end = beginCanvasResize(horizontal ? "col" : "row");
|
|
3360
3580
|
const compute = (pos) => {
|
|
3361
3581
|
const delta = (pos - origin) / total;
|
|
3362
3582
|
const min = CANVAS_MIN_SPLIT_FRACTION;
|
|
@@ -3371,7 +3591,7 @@ function SplitView({ node }) {
|
|
|
3371
3591
|
target.removeEventListener("pointermove", move);
|
|
3372
3592
|
target.removeEventListener("pointerup", up);
|
|
3373
3593
|
target.removeEventListener("pointercancel", up);
|
|
3374
|
-
|
|
3594
|
+
end();
|
|
3375
3595
|
setLiveSizes(null);
|
|
3376
3596
|
canvas.resizeSplit(node.id, compute(horizontal ? e.clientX : e.clientY));
|
|
3377
3597
|
};
|
|
@@ -3475,6 +3695,8 @@ function CanvasFrame({ children, className }) {
|
|
|
3475
3695
|
export {
|
|
3476
3696
|
BrowserBar,
|
|
3477
3697
|
BrowserTabs,
|
|
3698
|
+
CANVAS_ACTIVE_TAB_MIN_WIDTH,
|
|
3699
|
+
CANVAS_TAB_MIN_WIDTH,
|
|
3478
3700
|
CanvasColumn,
|
|
3479
3701
|
CanvasFrame,
|
|
3480
3702
|
CanvasHeaderButton,
|
|
@@ -3487,8 +3709,10 @@ export {
|
|
|
3487
3709
|
CanvasTab,
|
|
3488
3710
|
CanvasToggle,
|
|
3489
3711
|
CanvasWindowScope,
|
|
3712
|
+
beginCanvasResize,
|
|
3490
3713
|
canvasSessionKey,
|
|
3491
3714
|
defineCanvasKind,
|
|
3715
|
+
fitCanvasTabs,
|
|
3492
3716
|
getCanvasKind,
|
|
3493
3717
|
itemTitle,
|
|
3494
3718
|
listCanvasKinds,
|
|
@@ -3503,6 +3727,7 @@ export {
|
|
|
3503
3727
|
useCanvasKinds,
|
|
3504
3728
|
useCanvasLauncherEntries,
|
|
3505
3729
|
useCanvasLauncherEntry,
|
|
3730
|
+
useCanvasMenuItems,
|
|
3506
3731
|
useCanvasNativeBodies,
|
|
3507
3732
|
useCanvasPresentation,
|
|
3508
3733
|
useCanvasState,
|