@ai-matrx/canvas 0.5.3 → 0.6.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.
- package/CHANGELOG.md +27 -0
- package/README.md +6 -0
- package/dist/index.cjs +45 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +45 -9
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +247 -59
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +251 -62
- package/dist/react.js.map +1 -1
- package/dist/styles.css +66 -27
- package/dist/{toggleKind-BD78LnMB.d.cts → toggleKind-DhDmWExm.d.cts} +27 -1
- package/dist/{toggleKind-BD78LnMB.d.ts → toggleKind-DhDmWExm.d.ts} +27 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
var CANVAS_MAIN_WINDOW = "main";
|
|
17
17
|
var CANVAS_LAUNCHER_KIND = "canvas.launcher";
|
|
18
18
|
var CANVAS_HISTORY_LIMIT = 50;
|
|
19
|
+
var CANVAS_KEEP_ALIVE_DEPTH = 5;
|
|
19
20
|
var CANVAS_MIN_WIDTH = 360;
|
|
20
21
|
var CANVAS_DEFAULT_WIDTH = 640;
|
|
21
22
|
var CANVAS_MIN_SPLIT_FRACTION = 0.12;
|
|
@@ -374,8 +375,10 @@ function sendHome(state, itemId, home, activate) {
|
|
|
374
375
|
next = insertInto(next, homeFrame.focusedPaneId, itemId, activate);
|
|
375
376
|
return activate ? withFrame(next, target, { focusedPaneId: homeFrame.focusedPaneId }) : next;
|
|
376
377
|
}
|
|
377
|
-
function newItem(kind, key, title, data, now,
|
|
378
|
+
function newItem(kind, key, title, data, now, steps) {
|
|
378
379
|
const id = canvasItemId(kind, key);
|
|
380
|
+
const history = steps?.history ?? [];
|
|
381
|
+
const kept = steps?.kept ?? [];
|
|
379
382
|
return {
|
|
380
383
|
id,
|
|
381
384
|
kind,
|
|
@@ -384,12 +387,14 @@ function newItem(kind, key, title, data, now, history) {
|
|
|
384
387
|
data,
|
|
385
388
|
openedAt: now,
|
|
386
389
|
updatedAt: now,
|
|
387
|
-
instance
|
|
388
|
-
|
|
390
|
+
// A reused instance is the SAME live body coming back (Back, or returning to a kept step).
|
|
391
|
+
instance: steps?.instance ?? `${now.toString(36)}:${id}`,
|
|
392
|
+
...history.length > 0 ? { history: history.slice(-CANVAS_HISTORY_LIMIT) } : {},
|
|
393
|
+
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {}
|
|
389
394
|
};
|
|
390
395
|
}
|
|
391
396
|
function historyEntry(item) {
|
|
392
|
-
return { kind: item.kind, key: item.key, title: item.title, data: item.data };
|
|
397
|
+
return { kind: item.kind, key: item.key, title: item.title, data: item.data, ...item.instance ? { instance: item.instance } : {} };
|
|
393
398
|
}
|
|
394
399
|
function replaceInPlace(state, current, next) {
|
|
395
400
|
const pane = paneOf(state, current.id);
|
|
@@ -418,17 +423,25 @@ function sanitizeHistory(raw) {
|
|
|
418
423
|
if (!entry || typeof entry !== "object") continue;
|
|
419
424
|
const e = entry;
|
|
420
425
|
if (typeof e.kind !== "string" || typeof e.key !== "string") continue;
|
|
421
|
-
out.push({
|
|
426
|
+
out.push({
|
|
427
|
+
kind: e.kind,
|
|
428
|
+
key: e.key,
|
|
429
|
+
title: typeof e.title === "string" ? e.title : null,
|
|
430
|
+
data: e.data ?? null,
|
|
431
|
+
...typeof e.instance === "string" && e.instance ? { instance: e.instance } : {}
|
|
432
|
+
});
|
|
422
433
|
}
|
|
423
434
|
return out.slice(-CANVAS_HISTORY_LIMIT);
|
|
424
435
|
}
|
|
425
436
|
function sanitizeItem(item) {
|
|
426
437
|
const history = sanitizeHistory(item.history);
|
|
427
|
-
const
|
|
438
|
+
const kept = sanitizeHistory(item.kept).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
439
|
+
const { history: _history, kept: _kept, instance, ...rest } = item;
|
|
428
440
|
return {
|
|
429
441
|
...rest,
|
|
430
442
|
...typeof instance === "string" && instance ? { instance } : {},
|
|
431
|
-
...history.length > 0 ? { history } : {}
|
|
443
|
+
...history.length > 0 ? { history } : {},
|
|
444
|
+
...kept.length > 0 ? { kept } : {}
|
|
432
445
|
};
|
|
433
446
|
}
|
|
434
447
|
function clampWidth(width) {
|
|
@@ -615,7 +628,22 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
615
628
|
return current.kind === CANVAS_LAUNCHER_KIND ? canvasReducer(focused, canvasActions.closeItem(itemId)) : focused;
|
|
616
629
|
}
|
|
617
630
|
const history = [...current.history ?? [], historyEntry(current)];
|
|
618
|
-
|
|
631
|
+
const keptAll = current.kept ?? [];
|
|
632
|
+
const keptIndex = keptAll.findIndex((entry) => canvasItemId(entry.kind, entry.key) === targetId);
|
|
633
|
+
const revived = keptIndex >= 0 ? keptAll[keptIndex] : void 0;
|
|
634
|
+
const kept = keptAll.filter((_, i) => i !== keptIndex);
|
|
635
|
+
return replaceInPlace(
|
|
636
|
+
state,
|
|
637
|
+
current,
|
|
638
|
+
newItem(
|
|
639
|
+
to.kind,
|
|
640
|
+
to.key,
|
|
641
|
+
to.title !== void 0 ? to.title : revived?.title ?? null,
|
|
642
|
+
to.data !== void 0 ? to.data : revived?.data ?? null,
|
|
643
|
+
now,
|
|
644
|
+
{ history, kept, instance: revived?.instance }
|
|
645
|
+
)
|
|
646
|
+
);
|
|
619
647
|
}
|
|
620
648
|
case `${P}back`: {
|
|
621
649
|
const { itemId, now } = action.payload;
|
|
@@ -628,7 +656,12 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
628
656
|
if (state.items[targetId]) {
|
|
629
657
|
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
630
658
|
}
|
|
631
|
-
|
|
659
|
+
const kept = [...(current.kept ?? []).filter((k) => canvasItemId(k.kind, k.key) !== itemId), historyEntry(current)];
|
|
660
|
+
return replaceInPlace(
|
|
661
|
+
state,
|
|
662
|
+
current,
|
|
663
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, instance: entry.instance })
|
|
664
|
+
);
|
|
632
665
|
}
|
|
633
666
|
case `${P}closeItem`: {
|
|
634
667
|
const { itemId } = action.payload;
|
|
@@ -1363,6 +1396,7 @@ var NO_HISTORY = Object.freeze([]);
|
|
|
1363
1396
|
var selectCanvasCanGoBack = (s, itemId) => (s.items[itemId]?.history?.length ?? 0) > 0;
|
|
1364
1397
|
|
|
1365
1398
|
// src/core/presentation.ts
|
|
1399
|
+
var CANVAS_PHONE_MAX_WIDTH = 767;
|
|
1366
1400
|
var CANVAS_NARROW_WIDTH = 480;
|
|
1367
1401
|
var SQUARE_BAND = 1.2;
|
|
1368
1402
|
function canvasOrientation(width, height) {
|
|
@@ -1611,8 +1645,7 @@ function createLauncherEntryStore() {
|
|
|
1611
1645
|
}
|
|
1612
1646
|
};
|
|
1613
1647
|
}
|
|
1614
|
-
var
|
|
1615
|
-
var isPhoneViewport = () => typeof window !== "undefined" && window.innerWidth <= PHONE_MAX_WIDTH;
|
|
1648
|
+
var isPhoneViewport = () => typeof window !== "undefined" && window.innerWidth <= CANVAS_PHONE_MAX_WIDTH;
|
|
1616
1649
|
var isMacPlatform = () => typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(navigator.userAgentData?.platform ?? navigator.platform ?? "");
|
|
1617
1650
|
function escapeBelongsElsewhere(event) {
|
|
1618
1651
|
if (event.defaultPrevented) return true;
|
|
@@ -1812,7 +1845,7 @@ function useCanvasLauncherEntries() {
|
|
|
1812
1845
|
// src/react/CanvasColumn.tsx
|
|
1813
1846
|
import {
|
|
1814
1847
|
useEffect as useEffect7,
|
|
1815
|
-
useLayoutEffect as
|
|
1848
|
+
useLayoutEffect as useLayoutEffect5,
|
|
1816
1849
|
useRef as useRef7,
|
|
1817
1850
|
useState as useState6,
|
|
1818
1851
|
useSyncExternalStore as useSyncExternalStore4
|
|
@@ -1826,7 +1859,7 @@ import {
|
|
|
1826
1859
|
lazy,
|
|
1827
1860
|
memo,
|
|
1828
1861
|
useEffect as useEffect5,
|
|
1829
|
-
useLayoutEffect as
|
|
1862
|
+
useLayoutEffect as useLayoutEffect3,
|
|
1830
1863
|
useMemo as useMemo2,
|
|
1831
1864
|
useRef as useRef5,
|
|
1832
1865
|
useState as useState4
|
|
@@ -1836,6 +1869,7 @@ import {
|
|
|
1836
1869
|
import {
|
|
1837
1870
|
forwardRef,
|
|
1838
1871
|
useEffect as useEffect3,
|
|
1872
|
+
useLayoutEffect as useLayoutEffect2,
|
|
1839
1873
|
useRef as useRef3,
|
|
1840
1874
|
useState as useState3
|
|
1841
1875
|
} from "react";
|
|
@@ -1936,9 +1970,26 @@ var LayoutGridIcon = (p) => /* @__PURE__ */ jsxs2(Glyph, { ...p, children: [
|
|
|
1936
1970
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1937
1971
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1938
1972
|
] });
|
|
1973
|
+
var ChevronLeftIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m15 18-6-6 6-6" }) });
|
|
1974
|
+
var ChevronRightIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m9 18 6-6-6-6" }) });
|
|
1939
1975
|
|
|
1940
1976
|
// src/react/chrome.tsx
|
|
1941
1977
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1978
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? useEffect3 : useLayoutEffect2;
|
|
1979
|
+
function useTruncated(title) {
|
|
1980
|
+
const ref = useRef3(null);
|
|
1981
|
+
const [truncated, setTruncated] = useState3(false);
|
|
1982
|
+
useIsoLayoutEffect(() => {
|
|
1983
|
+
const el = ref.current;
|
|
1984
|
+
if (!el) return void 0;
|
|
1985
|
+
const check = () => setTruncated(el.scrollWidth - el.clientWidth > 1);
|
|
1986
|
+
check();
|
|
1987
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(check);
|
|
1988
|
+
observer?.observe(el);
|
|
1989
|
+
return () => observer?.disconnect();
|
|
1990
|
+
}, [title]);
|
|
1991
|
+
return { ref, truncated };
|
|
1992
|
+
}
|
|
1942
1993
|
var cx = (...names) => names.filter(Boolean).join(" ");
|
|
1943
1994
|
function useTooltip(text) {
|
|
1944
1995
|
const internals = useOptionalCanvasInternals();
|
|
@@ -2078,6 +2129,7 @@ function CanvasPanelEmpty({ children, className }) {
|
|
|
2078
2129
|
}
|
|
2079
2130
|
var CanvasTab = forwardRef(function CanvasTab2({ title, icon, note, active, onSelect, onClose, className, onKeyDown, onClick, ...rest }, ref) {
|
|
2080
2131
|
const shown = title || "New tab";
|
|
2132
|
+
const name = useTruncated(shown);
|
|
2081
2133
|
return /* @__PURE__ */ jsxs3(
|
|
2082
2134
|
"div",
|
|
2083
2135
|
{
|
|
@@ -2087,6 +2139,7 @@ var CanvasTab = forwardRef(function CanvasTab2({ title, icon, note, active, onSe
|
|
|
2087
2139
|
tabIndex: active ? 0 : -1,
|
|
2088
2140
|
"data-slot": "browser-tab",
|
|
2089
2141
|
"data-active": active ? "" : void 0,
|
|
2142
|
+
"data-truncated": name.truncated ? "" : void 0,
|
|
2090
2143
|
title: shown,
|
|
2091
2144
|
className: cx("mxc-tab", className),
|
|
2092
2145
|
...rest,
|
|
@@ -2104,7 +2157,7 @@ var CanvasTab = forwardRef(function CanvasTab2({ title, icon, note, active, onSe
|
|
|
2104
2157
|
},
|
|
2105
2158
|
children: [
|
|
2106
2159
|
/* @__PURE__ */ jsx4("span", { className: "mxc-tab-icon", children: icon }),
|
|
2107
|
-
/* @__PURE__ */ jsx4("span", { className: "mxc-tab-title", children: shown }),
|
|
2160
|
+
/* @__PURE__ */ jsx4("span", { ref: name.ref, className: "mxc-tab-title", children: shown }),
|
|
2108
2161
|
note ? /* @__PURE__ */ jsx4("span", { className: "mxc-tab-note", "data-slot": "browser-tab-note", children: note }) : null,
|
|
2109
2162
|
onClose ? /* @__PURE__ */ jsx4(
|
|
2110
2163
|
"button",
|
|
@@ -2271,6 +2324,21 @@ function useChoices() {
|
|
|
2271
2324
|
}
|
|
2272
2325
|
return { contextual, global };
|
|
2273
2326
|
}
|
|
2327
|
+
var FOLLOW_UP_MS = 600;
|
|
2328
|
+
function swallowFollowUpClicks() {
|
|
2329
|
+
if (typeof window === "undefined") return;
|
|
2330
|
+
const types = ["pointerdown", "mousedown", "pointerup", "mouseup", "click", "dblclick"];
|
|
2331
|
+
const swallow = (event) => {
|
|
2332
|
+
if (event.detail >= 2 || event.type === "dblclick") {
|
|
2333
|
+
event.preventDefault();
|
|
2334
|
+
event.stopPropagation();
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
for (const type of types) window.addEventListener(type, swallow, true);
|
|
2338
|
+
setTimeout(() => {
|
|
2339
|
+
for (const type of types) window.removeEventListener(type, swallow, true);
|
|
2340
|
+
}, FOLLOW_UP_MS);
|
|
2341
|
+
}
|
|
2274
2342
|
function LauncherGrid({ onChoose }) {
|
|
2275
2343
|
const { contextual, global } = useChoices();
|
|
2276
2344
|
if (contextual.length === 0 && global.length === 0) return /* @__PURE__ */ jsx5(CanvasPanelEmpty, { children: "Nothing to open" });
|
|
@@ -2278,10 +2346,23 @@ function LauncherGrid({ onChoose }) {
|
|
|
2278
2346
|
label ? /* @__PURE__ */ jsx5("h3", { className: "mxc-launcher-heading", children: label }) : null,
|
|
2279
2347
|
/* @__PURE__ */ jsx5("div", { className: "mxc-launcher-grid", children: choices.map((choice) => {
|
|
2280
2348
|
const Icon = choice.icon;
|
|
2281
|
-
return /* @__PURE__ */ jsxs4(
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2349
|
+
return /* @__PURE__ */ jsxs4(
|
|
2350
|
+
"button",
|
|
2351
|
+
{
|
|
2352
|
+
type: "button",
|
|
2353
|
+
className: "mxc-launcher-item",
|
|
2354
|
+
onClick: (event) => {
|
|
2355
|
+
if (event.detail >= 2) return;
|
|
2356
|
+
swallowFollowUpClicks();
|
|
2357
|
+
onChoose(choice.input);
|
|
2358
|
+
},
|
|
2359
|
+
children: [
|
|
2360
|
+
/* @__PURE__ */ jsx5(Icon, { className: "mxc-launcher-icon" }),
|
|
2361
|
+
/* @__PURE__ */ jsx5("span", { children: choice.label })
|
|
2362
|
+
]
|
|
2363
|
+
},
|
|
2364
|
+
choice.id
|
|
2365
|
+
);
|
|
2285
2366
|
}) })
|
|
2286
2367
|
] });
|
|
2287
2368
|
return /* @__PURE__ */ jsxs4("div", { className: "mxc-launcher", children: [
|
|
@@ -2394,6 +2475,10 @@ var CanvasSlotsContext = createContext3(null);
|
|
|
2394
2475
|
function useCanvasSlots() {
|
|
2395
2476
|
return useContext3(CanvasSlotsContext);
|
|
2396
2477
|
}
|
|
2478
|
+
var CanvasSinglePaneContext = createContext3(false);
|
|
2479
|
+
function useCanvasSinglePane() {
|
|
2480
|
+
return useContext3(CanvasSinglePaneContext);
|
|
2481
|
+
}
|
|
2397
2482
|
function useSlotSize(slots, paneId) {
|
|
2398
2483
|
return useSyncExternalStore3(
|
|
2399
2484
|
slots.subscribe,
|
|
@@ -2404,7 +2489,7 @@ function useSlotSize(slots, paneId) {
|
|
|
2404
2489
|
|
|
2405
2490
|
// src/react/bodies.tsx
|
|
2406
2491
|
import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2407
|
-
var
|
|
2492
|
+
var useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect5 : useLayoutEffect3;
|
|
2408
2493
|
var LAZY = /* @__PURE__ */ Symbol.for("ai-matrx.canvas.lazy");
|
|
2409
2494
|
function kindComponent(kind) {
|
|
2410
2495
|
if (kind.component) return kind.component;
|
|
@@ -2425,19 +2510,36 @@ function usePanePresentation(slots, paneId, isFullscreen, paneCount) {
|
|
|
2425
2510
|
var hostKey = (item) => item.instance ?? item.id;
|
|
2426
2511
|
var SEP2 = "";
|
|
2427
2512
|
var ROW = "";
|
|
2513
|
+
function bornAt(instance, fallback) {
|
|
2514
|
+
const stamp = instance ? Number.parseInt(instance.slice(0, instance.indexOf(":")), 36) : Number.NaN;
|
|
2515
|
+
return Number.isFinite(stamp) ? stamp : fallback;
|
|
2516
|
+
}
|
|
2517
|
+
function keptCanvasSteps(item) {
|
|
2518
|
+
const back = (item.history ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2519
|
+
const forward = (item.kept ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2520
|
+
return [...back, ...forward].filter((entry) => !!entry.instance);
|
|
2521
|
+
}
|
|
2428
2522
|
function layerSignature(state, windowId, onlyPane) {
|
|
2429
2523
|
const frame2 = canvasWindowFrame(state, windowId);
|
|
2430
2524
|
if (!frame2) return "";
|
|
2431
2525
|
const rows = [];
|
|
2526
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2527
|
+
const add = (hostId, born, fields) => {
|
|
2528
|
+
if (seen.has(hostId)) return;
|
|
2529
|
+
seen.add(hostId);
|
|
2530
|
+
rows.push({ born, hostId, row: [hostId, ...fields].join(SEP2) });
|
|
2531
|
+
};
|
|
2432
2532
|
const paneIds = listPaneIds(frame2.layout);
|
|
2433
2533
|
for (const paneId of onlyPane ? [onlyPane] : paneIds) {
|
|
2434
2534
|
const pane = state.panes[paneId];
|
|
2435
2535
|
for (const itemId of pane?.itemIds ?? []) {
|
|
2436
2536
|
const item = state.items[itemId];
|
|
2437
|
-
if (item)
|
|
2537
|
+
if (!item) continue;
|
|
2538
|
+
add(hostKey(item), bornAt(item.instance, item.openedAt), [itemId, paneId, pane?.activeItemId === itemId ? "1" : "0", "0"]);
|
|
2539
|
+
for (const step of keptCanvasSteps(item)) add(step.instance, bornAt(step.instance, item.openedAt), [itemId, paneId, "0", "1"]);
|
|
2438
2540
|
}
|
|
2439
2541
|
}
|
|
2440
|
-
rows.sort((a, b) => a.
|
|
2542
|
+
rows.sort((a, b) => a.born - b.born || a.hostId.localeCompare(b.hostId));
|
|
2441
2543
|
const head = [frame2.isOpen ? "1" : "0", frame2.isFullscreen ? "1" : "0", frame2.focusedPaneId, String(paneIds.length)].join(SEP2);
|
|
2442
2544
|
return [head, ...rows.map((r) => r.row)].join(ROW);
|
|
2443
2545
|
}
|
|
@@ -2451,8 +2553,8 @@ function parseSignature(signature) {
|
|
|
2451
2553
|
focusedPaneId,
|
|
2452
2554
|
paneCount: Number(count) || 1,
|
|
2453
2555
|
placed: rows.map((row) => {
|
|
2454
|
-
const [hostId = "", itemId = "", paneId = "", active] = row.split(SEP2);
|
|
2455
|
-
return { hostId, itemId, paneId, active: active === "1" };
|
|
2556
|
+
const [hostId = "", itemId = "", paneId = "", active, kept] = row.split(SEP2);
|
|
2557
|
+
return { hostId, itemId, paneId, active: active === "1", kept: kept === "1" };
|
|
2456
2558
|
})
|
|
2457
2559
|
};
|
|
2458
2560
|
}
|
|
@@ -2499,21 +2601,23 @@ function CanvasBodyLayer() {
|
|
|
2499
2601
|
const { windowId, layer } = useCanvasInternals();
|
|
2500
2602
|
const slots = useCanvasSlots();
|
|
2501
2603
|
const signature = useCanvasState((s) => layerSignature(s, windowId));
|
|
2604
|
+
const single = useCanvasSinglePane();
|
|
2502
2605
|
const root = useRef5(null);
|
|
2503
2606
|
const [placement] = useState4(
|
|
2504
2607
|
() => slots ? createPlacement(() => root.current, slots, () => layer.native.remeasure()) : null
|
|
2505
2608
|
);
|
|
2506
|
-
|
|
2507
|
-
|
|
2609
|
+
useIsoLayoutEffect2(() => placement?.place());
|
|
2610
|
+
useIsoLayoutEffect2(() => slots && placement ? slots.subscribe(placement.place) : void 0, [slots, placement]);
|
|
2508
2611
|
const shape = parseSignature(signature);
|
|
2509
2612
|
if (!slots || !placement || !shape) return null;
|
|
2510
|
-
return /* @__PURE__ */ jsx6("div", { ref: root, className: "mxc-bodies", "data-mxc-bodies": windowId, children: shape.placed.map(({ hostId, itemId, paneId, active }) => /* @__PURE__ */ jsx6(
|
|
2613
|
+
return /* @__PURE__ */ jsx6("div", { ref: root, className: "mxc-bodies", "data-mxc-bodies": windowId, children: shape.placed.map(({ hostId, itemId, paneId, active, kept }) => /* @__PURE__ */ jsx6(
|
|
2511
2614
|
BodyHost,
|
|
2512
2615
|
{
|
|
2513
2616
|
hostId,
|
|
2514
2617
|
itemId,
|
|
2618
|
+
kept,
|
|
2515
2619
|
paneId,
|
|
2516
|
-
visible: shape.isOpen && active,
|
|
2620
|
+
visible: shape.isOpen && active && (!single || paneId === shape.focusedPaneId),
|
|
2517
2621
|
focused: active && shape.focusedPaneId === paneId,
|
|
2518
2622
|
isFullscreen: shape.isFullscreen,
|
|
2519
2623
|
paneCount: shape.paneCount,
|
|
@@ -2527,11 +2631,12 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2527
2631
|
const { windowId } = useCanvasInternals();
|
|
2528
2632
|
const shape = parseSignature(useCanvasState((s) => layerSignature(s, windowId, paneId)));
|
|
2529
2633
|
if (!shape) return null;
|
|
2530
|
-
return /* @__PURE__ */ jsx6(Fragment, { children: shape.placed.map(({ hostId, itemId, active }) => /* @__PURE__ */ jsx6(
|
|
2634
|
+
return /* @__PURE__ */ jsx6(Fragment, { children: shape.placed.map(({ hostId, itemId, active, kept }) => /* @__PURE__ */ jsx6(
|
|
2531
2635
|
BodyHost,
|
|
2532
2636
|
{
|
|
2533
2637
|
hostId,
|
|
2534
2638
|
itemId,
|
|
2639
|
+
kept,
|
|
2535
2640
|
paneId,
|
|
2536
2641
|
visible: active,
|
|
2537
2642
|
focused: active && shape.focusedPaneId === paneId,
|
|
@@ -2543,9 +2648,22 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2543
2648
|
hostId
|
|
2544
2649
|
)) });
|
|
2545
2650
|
}
|
|
2651
|
+
function stepAsItem(step, openedAt) {
|
|
2652
|
+
return {
|
|
2653
|
+
id: canvasItemId(step.kind, step.key),
|
|
2654
|
+
kind: step.kind,
|
|
2655
|
+
key: step.key,
|
|
2656
|
+
title: step.title,
|
|
2657
|
+
data: step.data,
|
|
2658
|
+
openedAt,
|
|
2659
|
+
updatedAt: openedAt,
|
|
2660
|
+
...step.instance ? { instance: step.instance } : {}
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2546
2663
|
var BodyHost = memo(function BodyHost2({
|
|
2547
2664
|
hostId,
|
|
2548
2665
|
itemId,
|
|
2666
|
+
kept,
|
|
2549
2667
|
paneId,
|
|
2550
2668
|
visible,
|
|
2551
2669
|
focused,
|
|
@@ -2556,7 +2674,14 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2556
2674
|
}) {
|
|
2557
2675
|
const canvas = useCanvas();
|
|
2558
2676
|
const { windowId, ports } = useCanvasInternals();
|
|
2559
|
-
const
|
|
2677
|
+
const live = useCanvasState((s) => kept ? void 0 : s.items[itemId]);
|
|
2678
|
+
const step = useCanvasState((s) => {
|
|
2679
|
+
const ownerItem = kept ? s.items[itemId] : void 0;
|
|
2680
|
+
return ownerItem ? keptCanvasSteps(ownerItem).find((e) => e.instance === hostId) : void 0;
|
|
2681
|
+
});
|
|
2682
|
+
const owner = useCanvasState((s) => kept ? s.items[itemId]?.openedAt : void 0);
|
|
2683
|
+
const keptItem = useMemo2(() => step ? stepAsItem(step, owner ?? 0) : void 0, [step, owner]);
|
|
2684
|
+
const item = kept ? keptItem : live;
|
|
2560
2685
|
const kind = useItemKind(item?.kind ?? "");
|
|
2561
2686
|
const element = useRef5(null);
|
|
2562
2687
|
const [everShown, setEverShown] = useState4(visible);
|
|
@@ -2564,7 +2689,7 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2564
2689
|
const mounted = kind?.unmountWhenHidden ? visible : everShown;
|
|
2565
2690
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
2566
2691
|
const drop = usePaneDrop(paneId, () => slots.section(paneId));
|
|
2567
|
-
|
|
2692
|
+
useIsoLayoutEffect2(() => {
|
|
2568
2693
|
const el = element.current;
|
|
2569
2694
|
if (!placement || !el) return void 0;
|
|
2570
2695
|
return placement.register(hostId, { element: el, paneId });
|
|
@@ -2580,6 +2705,7 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2580
2705
|
"data-visible": visible ? "true" : "false",
|
|
2581
2706
|
"data-kind": item.kind,
|
|
2582
2707
|
"data-item": item.id,
|
|
2708
|
+
"data-kept": kept ? "" : void 0,
|
|
2583
2709
|
"data-pane": paneId,
|
|
2584
2710
|
inert: !visible,
|
|
2585
2711
|
"aria-hidden": visible ? void 0 : true,
|
|
@@ -2661,8 +2787,8 @@ function NativeBody({ itemId, windowId, visible, children }) {
|
|
|
2661
2787
|
const rect = { ...toCanvasRect(box), devicePixelRatio: window.devicePixelRatio || 1 };
|
|
2662
2788
|
layer.native.report(itemId, rect, { visible: !layer.isBlocked(), windowId });
|
|
2663
2789
|
};
|
|
2664
|
-
|
|
2665
|
-
|
|
2790
|
+
useIsoLayoutEffect2(() => measure.current());
|
|
2791
|
+
useIsoLayoutEffect2(() => {
|
|
2666
2792
|
const el = ref.current;
|
|
2667
2793
|
const run = () => measure.current();
|
|
2668
2794
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(run);
|
|
@@ -2715,10 +2841,10 @@ var ItemBoundary = class extends Component {
|
|
|
2715
2841
|
};
|
|
2716
2842
|
|
|
2717
2843
|
// src/react/CanvasPane.tsx
|
|
2718
|
-
import { Component as Component2, useEffect as useEffect6, useLayoutEffect as
|
|
2844
|
+
import { Component as Component2, useEffect as useEffect6, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useState as useState5 } from "react";
|
|
2719
2845
|
import * as Menu from "@radix-ui/react-dropdown-menu";
|
|
2720
2846
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2721
|
-
var
|
|
2847
|
+
var useIsoLayoutEffect3 = typeof window === "undefined" ? useEffect6 : useLayoutEffect4;
|
|
2722
2848
|
function itemTitle(item, kind) {
|
|
2723
2849
|
if (item.title) return item.title;
|
|
2724
2850
|
if (item.kind === CANVAS_LAUNCHER_KIND) return "New tab";
|
|
@@ -2732,6 +2858,25 @@ function itemTitle(item, kind) {
|
|
|
2732
2858
|
return kind?.label ?? item.kind;
|
|
2733
2859
|
}
|
|
2734
2860
|
var isMac = () => typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(navigator.platform ?? "");
|
|
2861
|
+
function windowTabsSignature(state, windowId) {
|
|
2862
|
+
const frame2 = canvasWindowFrame(state, windowId);
|
|
2863
|
+
if (!frame2) return "";
|
|
2864
|
+
const rows = [];
|
|
2865
|
+
for (const paneId of listPaneIds(frame2.layout)) {
|
|
2866
|
+
for (const itemId of state.panes[paneId]?.itemIds ?? []) rows.push(`${itemId}${paneId}`);
|
|
2867
|
+
}
|
|
2868
|
+
return rows.join("");
|
|
2869
|
+
}
|
|
2870
|
+
function parseWindowTabs(signature) {
|
|
2871
|
+
if (!signature) return [];
|
|
2872
|
+
const perPane = /* @__PURE__ */ new Map();
|
|
2873
|
+
return signature.split("").map((row) => {
|
|
2874
|
+
const [itemId = "", paneId = ""] = row.split("");
|
|
2875
|
+
const index = perPane.get(paneId) ?? 0;
|
|
2876
|
+
perPane.set(paneId, index + 1);
|
|
2877
|
+
return { itemId, paneId, index };
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2735
2880
|
function CanvasPaneView({ paneId }) {
|
|
2736
2881
|
const canvas = useCanvas();
|
|
2737
2882
|
const { windowId, tabStrip } = useCanvasInternals();
|
|
@@ -2750,11 +2895,13 @@ function CanvasPaneView({ paneId }) {
|
|
|
2750
2895
|
const role = useCanvasState((s) => windowId === CANVAS_MAIN_WINDOW ? "main" : s.windows[windowId]?.role ?? "window");
|
|
2751
2896
|
const canGoBack = useCanvasState((s) => active ? selectCanvasCanGoBack(s, active.id) : false);
|
|
2752
2897
|
const kind = useItemKind(active?.kind ?? "");
|
|
2898
|
+
const singlePane = useCanvasSinglePane();
|
|
2899
|
+
const windowTabs = useCanvasState((s) => singlePane ? windowTabsSignature(s, windowId) : "");
|
|
2753
2900
|
const sectionRef = useRef6(null);
|
|
2754
2901
|
const bodyRef = useRef6(null);
|
|
2755
2902
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
2756
2903
|
const drop = usePaneDrop(paneId, () => sectionRef.current);
|
|
2757
|
-
|
|
2904
|
+
useIsoLayoutEffect3(() => {
|
|
2758
2905
|
const body = bodyRef.current;
|
|
2759
2906
|
const section = sectionRef.current;
|
|
2760
2907
|
if (body) slots.set(paneId, body);
|
|
@@ -2769,7 +2916,8 @@ function CanvasPaneView({ paneId }) {
|
|
|
2769
2916
|
const Leading = kind?.HeaderLeading;
|
|
2770
2917
|
const Action = kind?.HeaderAction;
|
|
2771
2918
|
const Toolbar = kind?.Toolbar;
|
|
2772
|
-
const
|
|
2919
|
+
const tabs = singlePane ? parseWindowTabs(windowTabs) : pane.itemIds.map((itemId, index) => ({ itemId, paneId, index }));
|
|
2920
|
+
const single = tabStrip === "auto" && tabs.length === 1 && active;
|
|
2773
2921
|
const closeLabel = role === "pop-out" && paneCount === 1 ? "Close window" : paneCount > 1 ? "Close pane" : "Close canvas";
|
|
2774
2922
|
return /* @__PURE__ */ jsxs6(
|
|
2775
2923
|
CanvasPanel,
|
|
@@ -2782,7 +2930,7 @@ function CanvasPaneView({ paneId }) {
|
|
|
2782
2930
|
"data-mxc-pane": paneId,
|
|
2783
2931
|
header: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
2784
2932
|
canGoBack && active ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-back", label: "Back", onClick: () => void canvas.back(active.id), children: /* @__PURE__ */ jsx7(ArrowLeftIcon, {}) }) : null,
|
|
2785
|
-
single && active ? /* @__PURE__ */ jsx7(SingleTitle, { item: active, kind }) :
|
|
2933
|
+
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" }),
|
|
2786
2934
|
/* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-new-tab", label: "New tab", onClick: () => void canvas.newTab(paneId), children: /* @__PURE__ */ jsx7(PlusIcon, {}) })
|
|
2787
2935
|
] }),
|
|
2788
2936
|
leading: Leading && kindProps ? /* @__PURE__ */ jsx7(Leading, { ...kindProps }) : null,
|
|
@@ -2831,38 +2979,71 @@ function SingleTitle({ item, kind }) {
|
|
|
2831
2979
|
/* @__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, {}) })
|
|
2832
2980
|
] });
|
|
2833
2981
|
}
|
|
2834
|
-
function TabStrip({
|
|
2982
|
+
function TabStrip({ tabs, activeItemId }) {
|
|
2835
2983
|
const strip = useRef6(null);
|
|
2836
|
-
|
|
2984
|
+
const [overflow, setOverflow] = useState5({ start: false, end: false });
|
|
2985
|
+
const measure = useRef6(() => void 0);
|
|
2986
|
+
measure.current = () => {
|
|
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(() => {
|
|
2837
2995
|
const el = strip.current;
|
|
2838
2996
|
if (!el) return void 0;
|
|
2839
2997
|
const reveal = () => {
|
|
2840
2998
|
const tab = el.querySelector('[role="tab"][aria-selected="true"]');
|
|
2841
|
-
if (
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2999
|
+
if (tab) {
|
|
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();
|
|
2846
3006
|
};
|
|
2847
3007
|
reveal();
|
|
2848
3008
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(reveal);
|
|
2849
3009
|
observer?.observe(el);
|
|
2850
|
-
const
|
|
2851
|
-
if (activeTab) observer?.observe(activeTab);
|
|
3010
|
+
for (const tab of el.querySelectorAll('[role="tab"]')) observer?.observe(tab);
|
|
2852
3011
|
return () => observer?.disconnect();
|
|
2853
|
-
}, [activeItemId,
|
|
2854
|
-
|
|
3012
|
+
}, [activeItemId, tabKey]);
|
|
3013
|
+
const page = (direction) => {
|
|
3014
|
+
const el = strip.current;
|
|
3015
|
+
if (!el) return;
|
|
3016
|
+
const by = Math.max(48, Math.round(el.clientWidth * 0.8)) * direction;
|
|
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;
|
|
3021
|
+
return /* @__PURE__ */ jsxs6(
|
|
2855
3022
|
"div",
|
|
2856
3023
|
{
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
"
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
3024
|
+
className: "mxc-tabstrip",
|
|
3025
|
+
"data-overflow": overflowing ? "" : void 0,
|
|
3026
|
+
"data-overflow-start": overflow.start ? "" : void 0,
|
|
3027
|
+
"data-overflow-end": overflow.end ? "" : void 0,
|
|
3028
|
+
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
|
+
/* @__PURE__ */ jsx7(
|
|
3031
|
+
"div",
|
|
3032
|
+
{
|
|
3033
|
+
ref: strip,
|
|
3034
|
+
className: "mxc-tabs",
|
|
3035
|
+
role: "tablist",
|
|
3036
|
+
"aria-label": "Canvas tabs",
|
|
3037
|
+
onScroll: () => measure.current(),
|
|
3038
|
+
onWheel: (event) => {
|
|
3039
|
+
const el = strip.current;
|
|
3040
|
+
if (el && Math.abs(event.deltaY) > Math.abs(event.deltaX)) el.scrollLeft += event.deltaY;
|
|
3041
|
+
},
|
|
3042
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsx7(ItemTab, { itemId: tab.itemId, paneId: tab.paneId, index: tab.index, active: tab.itemId === activeItemId }, tab.itemId))
|
|
3043
|
+
}
|
|
3044
|
+
),
|
|
3045
|
+
overflowing ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs right", available: overflow.end, onClick: () => page(1), children: /* @__PURE__ */ jsx7(ChevronRightIcon, {}) }) : null
|
|
3046
|
+
]
|
|
2866
3047
|
}
|
|
2867
3048
|
);
|
|
2868
3049
|
}
|
|
@@ -2986,7 +3167,7 @@ function safeMenu(build) {
|
|
|
2986
3167
|
|
|
2987
3168
|
// src/react/CanvasColumn.tsx
|
|
2988
3169
|
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2989
|
-
var
|
|
3170
|
+
var useIsoLayoutEffect4 = typeof window === "undefined" ? useEffect7 : useLayoutEffect5;
|
|
2990
3171
|
function subscribeViewport(listener) {
|
|
2991
3172
|
window.addEventListener("resize", listener);
|
|
2992
3173
|
return () => window.removeEventListener("resize", listener);
|
|
@@ -3015,7 +3196,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3015
3196
|
const isFullscreen = useCanvasState((s) => selectCanvasIsFullscreen(s, windowId));
|
|
3016
3197
|
const width = useCanvasState((s) => selectCanvasWidth(s, windowId));
|
|
3017
3198
|
const layout = useCanvasState((s) => selectCanvasLayout(s, windowId));
|
|
3199
|
+
const focusedPaneId = useCanvasState((s) => selectCanvasFocusedPaneId(s, windowId));
|
|
3018
3200
|
const region = useRegionWidth();
|
|
3201
|
+
const viewport = useViewportWidth();
|
|
3202
|
+
const singlePane = placement === "docked" && viewport > 0 && viewport <= CANVAS_PHONE_MAX_WIDTH;
|
|
3019
3203
|
const [dragWidth, setDragWidth] = useState6(null);
|
|
3020
3204
|
const [slots] = useState6(createCanvasSlots);
|
|
3021
3205
|
const ref = useRef7(null);
|
|
@@ -3080,10 +3264,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3080
3264
|
}
|
|
3081
3265
|
}
|
|
3082
3266
|
) : null,
|
|
3083
|
-
/* @__PURE__ */
|
|
3084
|
-
/* @__PURE__ */ jsx8("div", { className: "mxc-layout", children: /* @__PURE__ */ jsx8(LayoutNodeView, { node: layout }) }),
|
|
3267
|
+
/* @__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: presentedPane(layout, focusedPaneId) }) : /* @__PURE__ */ jsx8(LayoutNodeView, { node: layout }) }),
|
|
3085
3269
|
/* @__PURE__ */ jsx8(CanvasBodyLayer, {})
|
|
3086
|
-
] })
|
|
3270
|
+
] }) })
|
|
3087
3271
|
] })
|
|
3088
3272
|
}
|
|
3089
3273
|
);
|
|
@@ -3131,13 +3315,17 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3131
3315
|
}
|
|
3132
3316
|
);
|
|
3133
3317
|
}
|
|
3318
|
+
function presentedPane(layout, focused) {
|
|
3319
|
+
const ids = listPaneIds(layout);
|
|
3320
|
+
return ids.find((id) => id === focused) ?? ids[0];
|
|
3321
|
+
}
|
|
3134
3322
|
function LayoutNodeView({ node }) {
|
|
3135
3323
|
if (node.type === "pane") return /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: node.paneId });
|
|
3136
3324
|
return /* @__PURE__ */ jsx8(SplitView, { node });
|
|
3137
3325
|
}
|
|
3138
3326
|
function useMeasuredWidth(ref, enabled) {
|
|
3139
3327
|
const [width, setWidth] = useState6(0);
|
|
3140
|
-
|
|
3328
|
+
useIsoLayoutEffect4(() => {
|
|
3141
3329
|
const el = ref.current;
|
|
3142
3330
|
if (!enabled || !el) return;
|
|
3143
3331
|
setWidth(el.getBoundingClientRect().width);
|
|
@@ -3233,6 +3421,7 @@ function CanvasToggle({
|
|
|
3233
3421
|
ariaLabel: isOpen ? "Hide canvas" : "Show canvas",
|
|
3234
3422
|
tooltip: `${isOpen ? "Hide canvas" : "Show canvas"}${hint ? ` (${hint})` : ""}`,
|
|
3235
3423
|
icon: /* @__PURE__ */ jsx8(PanelRightIcon, {}),
|
|
3424
|
+
pressed: isOpen,
|
|
3236
3425
|
onClick: () => controller.toggle()
|
|
3237
3426
|
}
|
|
3238
3427
|
),
|