@ai-matrx/canvas 0.5.2 → 0.6.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 +23 -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 +222 -48
- 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 +222 -48
- package/dist/react.js.map +1 -1
- package/dist/styles.css +59 -30
- 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.cjs
CHANGED
|
@@ -78,6 +78,7 @@ var import_react2 = require("react");
|
|
|
78
78
|
var CANVAS_MAIN_WINDOW = "main";
|
|
79
79
|
var CANVAS_LAUNCHER_KIND = "canvas.launcher";
|
|
80
80
|
var CANVAS_HISTORY_LIMIT = 50;
|
|
81
|
+
var CANVAS_KEEP_ALIVE_DEPTH = 5;
|
|
81
82
|
var CANVAS_MIN_WIDTH = 360;
|
|
82
83
|
var CANVAS_DEFAULT_WIDTH = 640;
|
|
83
84
|
var CANVAS_MIN_SPLIT_FRACTION = 0.12;
|
|
@@ -436,8 +437,10 @@ function sendHome(state, itemId, home, activate) {
|
|
|
436
437
|
next = insertInto(next, homeFrame.focusedPaneId, itemId, activate);
|
|
437
438
|
return activate ? withFrame(next, target, { focusedPaneId: homeFrame.focusedPaneId }) : next;
|
|
438
439
|
}
|
|
439
|
-
function newItem(kind, key, title, data, now,
|
|
440
|
+
function newItem(kind, key, title, data, now, steps) {
|
|
440
441
|
const id = canvasItemId(kind, key);
|
|
442
|
+
const history = steps?.history ?? [];
|
|
443
|
+
const kept = steps?.kept ?? [];
|
|
441
444
|
return {
|
|
442
445
|
id,
|
|
443
446
|
kind,
|
|
@@ -446,12 +449,14 @@ function newItem(kind, key, title, data, now, history) {
|
|
|
446
449
|
data,
|
|
447
450
|
openedAt: now,
|
|
448
451
|
updatedAt: now,
|
|
449
|
-
instance
|
|
450
|
-
|
|
452
|
+
// A reused instance is the SAME live body coming back (Back, or returning to a kept step).
|
|
453
|
+
instance: steps?.instance ?? `${now.toString(36)}:${id}`,
|
|
454
|
+
...history.length > 0 ? { history: history.slice(-CANVAS_HISTORY_LIMIT) } : {},
|
|
455
|
+
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {}
|
|
451
456
|
};
|
|
452
457
|
}
|
|
453
458
|
function historyEntry(item) {
|
|
454
|
-
return { kind: item.kind, key: item.key, title: item.title, data: item.data };
|
|
459
|
+
return { kind: item.kind, key: item.key, title: item.title, data: item.data, ...item.instance ? { instance: item.instance } : {} };
|
|
455
460
|
}
|
|
456
461
|
function replaceInPlace(state, current, next) {
|
|
457
462
|
const pane = paneOf(state, current.id);
|
|
@@ -480,17 +485,25 @@ function sanitizeHistory(raw) {
|
|
|
480
485
|
if (!entry || typeof entry !== "object") continue;
|
|
481
486
|
const e = entry;
|
|
482
487
|
if (typeof e.kind !== "string" || typeof e.key !== "string") continue;
|
|
483
|
-
out.push({
|
|
488
|
+
out.push({
|
|
489
|
+
kind: e.kind,
|
|
490
|
+
key: e.key,
|
|
491
|
+
title: typeof e.title === "string" ? e.title : null,
|
|
492
|
+
data: e.data ?? null,
|
|
493
|
+
...typeof e.instance === "string" && e.instance ? { instance: e.instance } : {}
|
|
494
|
+
});
|
|
484
495
|
}
|
|
485
496
|
return out.slice(-CANVAS_HISTORY_LIMIT);
|
|
486
497
|
}
|
|
487
498
|
function sanitizeItem(item) {
|
|
488
499
|
const history = sanitizeHistory(item.history);
|
|
489
|
-
const
|
|
500
|
+
const kept = sanitizeHistory(item.kept).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
501
|
+
const { history: _history, kept: _kept, instance, ...rest } = item;
|
|
490
502
|
return {
|
|
491
503
|
...rest,
|
|
492
504
|
...typeof instance === "string" && instance ? { instance } : {},
|
|
493
|
-
...history.length > 0 ? { history } : {}
|
|
505
|
+
...history.length > 0 ? { history } : {},
|
|
506
|
+
...kept.length > 0 ? { kept } : {}
|
|
494
507
|
};
|
|
495
508
|
}
|
|
496
509
|
function clampWidth(width) {
|
|
@@ -677,7 +690,22 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
677
690
|
return current.kind === CANVAS_LAUNCHER_KIND ? canvasReducer(focused, canvasActions.closeItem(itemId)) : focused;
|
|
678
691
|
}
|
|
679
692
|
const history = [...current.history ?? [], historyEntry(current)];
|
|
680
|
-
|
|
693
|
+
const keptAll = current.kept ?? [];
|
|
694
|
+
const keptIndex = keptAll.findIndex((entry) => canvasItemId(entry.kind, entry.key) === targetId);
|
|
695
|
+
const revived = keptIndex >= 0 ? keptAll[keptIndex] : void 0;
|
|
696
|
+
const kept = keptAll.filter((_, i) => i !== keptIndex);
|
|
697
|
+
return replaceInPlace(
|
|
698
|
+
state,
|
|
699
|
+
current,
|
|
700
|
+
newItem(
|
|
701
|
+
to.kind,
|
|
702
|
+
to.key,
|
|
703
|
+
to.title !== void 0 ? to.title : revived?.title ?? null,
|
|
704
|
+
to.data !== void 0 ? to.data : revived?.data ?? null,
|
|
705
|
+
now,
|
|
706
|
+
{ history, kept, instance: revived?.instance }
|
|
707
|
+
)
|
|
708
|
+
);
|
|
681
709
|
}
|
|
682
710
|
case `${P}back`: {
|
|
683
711
|
const { itemId, now } = action.payload;
|
|
@@ -690,7 +718,12 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
690
718
|
if (state.items[targetId]) {
|
|
691
719
|
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
692
720
|
}
|
|
693
|
-
|
|
721
|
+
const kept = [...(current.kept ?? []).filter((k) => canvasItemId(k.kind, k.key) !== itemId), historyEntry(current)];
|
|
722
|
+
return replaceInPlace(
|
|
723
|
+
state,
|
|
724
|
+
current,
|
|
725
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, instance: entry.instance })
|
|
726
|
+
);
|
|
694
727
|
}
|
|
695
728
|
case `${P}closeItem`: {
|
|
696
729
|
const { itemId } = action.payload;
|
|
@@ -1425,6 +1458,7 @@ var NO_HISTORY = Object.freeze([]);
|
|
|
1425
1458
|
var selectCanvasCanGoBack = (s, itemId) => (s.items[itemId]?.history?.length ?? 0) > 0;
|
|
1426
1459
|
|
|
1427
1460
|
// src/core/presentation.ts
|
|
1461
|
+
var CANVAS_PHONE_MAX_WIDTH = 767;
|
|
1428
1462
|
var CANVAS_NARROW_WIDTH = 480;
|
|
1429
1463
|
var SQUARE_BAND = 1.2;
|
|
1430
1464
|
function canvasOrientation(width, height) {
|
|
@@ -1673,8 +1707,7 @@ function createLauncherEntryStore() {
|
|
|
1673
1707
|
}
|
|
1674
1708
|
};
|
|
1675
1709
|
}
|
|
1676
|
-
var
|
|
1677
|
-
var isPhoneViewport = () => typeof window !== "undefined" && window.innerWidth <= PHONE_MAX_WIDTH;
|
|
1710
|
+
var isPhoneViewport = () => typeof window !== "undefined" && window.innerWidth <= CANVAS_PHONE_MAX_WIDTH;
|
|
1678
1711
|
var isMacPlatform = () => typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(navigator.userAgentData?.platform ?? navigator.platform ?? "");
|
|
1679
1712
|
function escapeBelongsElsewhere(event) {
|
|
1680
1713
|
if (event.defaultPrevented) return true;
|
|
@@ -1977,6 +2010,8 @@ var LayoutGridIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Glyph,
|
|
|
1977
2010
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1978
2011
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1979
2012
|
] });
|
|
2013
|
+
var ChevronLeftIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Glyph, { ...p, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m15 18-6-6 6-6" }) });
|
|
2014
|
+
var ChevronRightIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Glyph, { ...p, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m9 18 6-6-6-6" }) });
|
|
1980
2015
|
|
|
1981
2016
|
// src/react/chrome.tsx
|
|
1982
2017
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -2312,6 +2347,21 @@ function useChoices() {
|
|
|
2312
2347
|
}
|
|
2313
2348
|
return { contextual, global };
|
|
2314
2349
|
}
|
|
2350
|
+
var FOLLOW_UP_MS = 600;
|
|
2351
|
+
function swallowFollowUpClicks() {
|
|
2352
|
+
if (typeof window === "undefined") return;
|
|
2353
|
+
const types = ["pointerdown", "mousedown", "pointerup", "mouseup", "click", "dblclick"];
|
|
2354
|
+
const swallow = (event) => {
|
|
2355
|
+
if (event.detail >= 2 || event.type === "dblclick") {
|
|
2356
|
+
event.preventDefault();
|
|
2357
|
+
event.stopPropagation();
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
2360
|
+
for (const type of types) window.addEventListener(type, swallow, true);
|
|
2361
|
+
setTimeout(() => {
|
|
2362
|
+
for (const type of types) window.removeEventListener(type, swallow, true);
|
|
2363
|
+
}, FOLLOW_UP_MS);
|
|
2364
|
+
}
|
|
2315
2365
|
function LauncherGrid({ onChoose }) {
|
|
2316
2366
|
const { contextual, global } = useChoices();
|
|
2317
2367
|
if (contextual.length === 0 && global.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CanvasPanelEmpty, { children: "Nothing to open" });
|
|
@@ -2319,10 +2369,23 @@ function LauncherGrid({ onChoose }) {
|
|
|
2319
2369
|
label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "mxc-launcher-heading", children: label }) : null,
|
|
2320
2370
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mxc-launcher-grid", children: choices.map((choice) => {
|
|
2321
2371
|
const Icon = choice.icon;
|
|
2322
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2373
|
+
"button",
|
|
2374
|
+
{
|
|
2375
|
+
type: "button",
|
|
2376
|
+
className: "mxc-launcher-item",
|
|
2377
|
+
onClick: (event) => {
|
|
2378
|
+
if (event.detail >= 2) return;
|
|
2379
|
+
swallowFollowUpClicks();
|
|
2380
|
+
onChoose(choice.input);
|
|
2381
|
+
},
|
|
2382
|
+
children: [
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { className: "mxc-launcher-icon" }),
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: choice.label })
|
|
2385
|
+
]
|
|
2386
|
+
},
|
|
2387
|
+
choice.id
|
|
2388
|
+
);
|
|
2326
2389
|
}) })
|
|
2327
2390
|
] });
|
|
2328
2391
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mxc-launcher", children: [
|
|
@@ -2435,6 +2498,10 @@ var CanvasSlotsContext = (0, import_react6.createContext)(null);
|
|
|
2435
2498
|
function useCanvasSlots() {
|
|
2436
2499
|
return (0, import_react6.useContext)(CanvasSlotsContext);
|
|
2437
2500
|
}
|
|
2501
|
+
var CanvasSinglePaneContext = (0, import_react6.createContext)(false);
|
|
2502
|
+
function useCanvasSinglePane() {
|
|
2503
|
+
return (0, import_react6.useContext)(CanvasSinglePaneContext);
|
|
2504
|
+
}
|
|
2438
2505
|
function useSlotSize(slots, paneId) {
|
|
2439
2506
|
return (0, import_react6.useSyncExternalStore)(
|
|
2440
2507
|
slots.subscribe,
|
|
@@ -2466,19 +2533,36 @@ function usePanePresentation(slots, paneId, isFullscreen, paneCount) {
|
|
|
2466
2533
|
var hostKey = (item) => item.instance ?? item.id;
|
|
2467
2534
|
var SEP2 = "";
|
|
2468
2535
|
var ROW = "";
|
|
2536
|
+
function bornAt(instance, fallback) {
|
|
2537
|
+
const stamp = instance ? Number.parseInt(instance.slice(0, instance.indexOf(":")), 36) : Number.NaN;
|
|
2538
|
+
return Number.isFinite(stamp) ? stamp : fallback;
|
|
2539
|
+
}
|
|
2540
|
+
function keptCanvasSteps(item) {
|
|
2541
|
+
const back = (item.history ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2542
|
+
const forward = (item.kept ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2543
|
+
return [...back, ...forward].filter((entry) => !!entry.instance);
|
|
2544
|
+
}
|
|
2469
2545
|
function layerSignature(state, windowId, onlyPane) {
|
|
2470
2546
|
const frame2 = canvasWindowFrame(state, windowId);
|
|
2471
2547
|
if (!frame2) return "";
|
|
2472
2548
|
const rows = [];
|
|
2549
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2550
|
+
const add = (hostId, born, fields) => {
|
|
2551
|
+
if (seen.has(hostId)) return;
|
|
2552
|
+
seen.add(hostId);
|
|
2553
|
+
rows.push({ born, hostId, row: [hostId, ...fields].join(SEP2) });
|
|
2554
|
+
};
|
|
2473
2555
|
const paneIds = listPaneIds(frame2.layout);
|
|
2474
2556
|
for (const paneId of onlyPane ? [onlyPane] : paneIds) {
|
|
2475
2557
|
const pane = state.panes[paneId];
|
|
2476
2558
|
for (const itemId of pane?.itemIds ?? []) {
|
|
2477
2559
|
const item = state.items[itemId];
|
|
2478
|
-
if (item)
|
|
2560
|
+
if (!item) continue;
|
|
2561
|
+
add(hostKey(item), bornAt(item.instance, item.openedAt), [itemId, paneId, pane?.activeItemId === itemId ? "1" : "0", "0"]);
|
|
2562
|
+
for (const step of keptCanvasSteps(item)) add(step.instance, bornAt(step.instance, item.openedAt), [itemId, paneId, "0", "1"]);
|
|
2479
2563
|
}
|
|
2480
2564
|
}
|
|
2481
|
-
rows.sort((a, b) => a.
|
|
2565
|
+
rows.sort((a, b) => a.born - b.born || a.hostId.localeCompare(b.hostId));
|
|
2482
2566
|
const head = [frame2.isOpen ? "1" : "0", frame2.isFullscreen ? "1" : "0", frame2.focusedPaneId, String(paneIds.length)].join(SEP2);
|
|
2483
2567
|
return [head, ...rows.map((r) => r.row)].join(ROW);
|
|
2484
2568
|
}
|
|
@@ -2492,8 +2576,8 @@ function parseSignature(signature) {
|
|
|
2492
2576
|
focusedPaneId,
|
|
2493
2577
|
paneCount: Number(count) || 1,
|
|
2494
2578
|
placed: rows.map((row) => {
|
|
2495
|
-
const [hostId = "", itemId = "", paneId = "", active] = row.split(SEP2);
|
|
2496
|
-
return { hostId, itemId, paneId, active: active === "1" };
|
|
2579
|
+
const [hostId = "", itemId = "", paneId = "", active, kept] = row.split(SEP2);
|
|
2580
|
+
return { hostId, itemId, paneId, active: active === "1", kept: kept === "1" };
|
|
2497
2581
|
})
|
|
2498
2582
|
};
|
|
2499
2583
|
}
|
|
@@ -2540,6 +2624,7 @@ function CanvasBodyLayer() {
|
|
|
2540
2624
|
const { windowId, layer } = useCanvasInternals();
|
|
2541
2625
|
const slots = useCanvasSlots();
|
|
2542
2626
|
const signature = useCanvasState((s) => layerSignature(s, windowId));
|
|
2627
|
+
const single = useCanvasSinglePane();
|
|
2543
2628
|
const root = (0, import_react7.useRef)(null);
|
|
2544
2629
|
const [placement] = (0, import_react7.useState)(
|
|
2545
2630
|
() => slots ? createPlacement(() => root.current, slots, () => layer.native.remeasure()) : null
|
|
@@ -2548,13 +2633,14 @@ function CanvasBodyLayer() {
|
|
|
2548
2633
|
useIsoLayoutEffect(() => slots && placement ? slots.subscribe(placement.place) : void 0, [slots, placement]);
|
|
2549
2634
|
const shape = parseSignature(signature);
|
|
2550
2635
|
if (!slots || !placement || !shape) return null;
|
|
2551
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: root, className: "mxc-bodies", "data-mxc-bodies": windowId, children: shape.placed.map(({ hostId, itemId, paneId, active }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2636
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: root, className: "mxc-bodies", "data-mxc-bodies": windowId, children: shape.placed.map(({ hostId, itemId, paneId, active, kept }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2552
2637
|
BodyHost,
|
|
2553
2638
|
{
|
|
2554
2639
|
hostId,
|
|
2555
2640
|
itemId,
|
|
2641
|
+
kept,
|
|
2556
2642
|
paneId,
|
|
2557
|
-
visible: shape.isOpen && active,
|
|
2643
|
+
visible: shape.isOpen && active && (!single || paneId === shape.focusedPaneId),
|
|
2558
2644
|
focused: active && shape.focusedPaneId === paneId,
|
|
2559
2645
|
isFullscreen: shape.isFullscreen,
|
|
2560
2646
|
paneCount: shape.paneCount,
|
|
@@ -2568,11 +2654,12 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2568
2654
|
const { windowId } = useCanvasInternals();
|
|
2569
2655
|
const shape = parseSignature(useCanvasState((s) => layerSignature(s, windowId, paneId)));
|
|
2570
2656
|
if (!shape) return null;
|
|
2571
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: shape.placed.map(({ hostId, itemId, active }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: shape.placed.map(({ hostId, itemId, active, kept }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2572
2658
|
BodyHost,
|
|
2573
2659
|
{
|
|
2574
2660
|
hostId,
|
|
2575
2661
|
itemId,
|
|
2662
|
+
kept,
|
|
2576
2663
|
paneId,
|
|
2577
2664
|
visible: active,
|
|
2578
2665
|
focused: active && shape.focusedPaneId === paneId,
|
|
@@ -2584,9 +2671,22 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2584
2671
|
hostId
|
|
2585
2672
|
)) });
|
|
2586
2673
|
}
|
|
2674
|
+
function stepAsItem(step, openedAt) {
|
|
2675
|
+
return {
|
|
2676
|
+
id: canvasItemId(step.kind, step.key),
|
|
2677
|
+
kind: step.kind,
|
|
2678
|
+
key: step.key,
|
|
2679
|
+
title: step.title,
|
|
2680
|
+
data: step.data,
|
|
2681
|
+
openedAt,
|
|
2682
|
+
updatedAt: openedAt,
|
|
2683
|
+
...step.instance ? { instance: step.instance } : {}
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2587
2686
|
var BodyHost = (0, import_react7.memo)(function BodyHost2({
|
|
2588
2687
|
hostId,
|
|
2589
2688
|
itemId,
|
|
2689
|
+
kept,
|
|
2590
2690
|
paneId,
|
|
2591
2691
|
visible,
|
|
2592
2692
|
focused,
|
|
@@ -2597,7 +2697,14 @@ var BodyHost = (0, import_react7.memo)(function BodyHost2({
|
|
|
2597
2697
|
}) {
|
|
2598
2698
|
const canvas = useCanvas();
|
|
2599
2699
|
const { windowId, ports } = useCanvasInternals();
|
|
2600
|
-
const
|
|
2700
|
+
const live = useCanvasState((s) => kept ? void 0 : s.items[itemId]);
|
|
2701
|
+
const step = useCanvasState((s) => {
|
|
2702
|
+
const ownerItem = kept ? s.items[itemId] : void 0;
|
|
2703
|
+
return ownerItem ? keptCanvasSteps(ownerItem).find((e) => e.instance === hostId) : void 0;
|
|
2704
|
+
});
|
|
2705
|
+
const owner = useCanvasState((s) => kept ? s.items[itemId]?.openedAt : void 0);
|
|
2706
|
+
const keptItem = (0, import_react7.useMemo)(() => step ? stepAsItem(step, owner ?? 0) : void 0, [step, owner]);
|
|
2707
|
+
const item = kept ? keptItem : live;
|
|
2601
2708
|
const kind = useItemKind(item?.kind ?? "");
|
|
2602
2709
|
const element = (0, import_react7.useRef)(null);
|
|
2603
2710
|
const [everShown, setEverShown] = (0, import_react7.useState)(visible);
|
|
@@ -2621,6 +2728,7 @@ var BodyHost = (0, import_react7.memo)(function BodyHost2({
|
|
|
2621
2728
|
"data-visible": visible ? "true" : "false",
|
|
2622
2729
|
"data-kind": item.kind,
|
|
2623
2730
|
"data-item": item.id,
|
|
2731
|
+
"data-kept": kept ? "" : void 0,
|
|
2624
2732
|
"data-pane": paneId,
|
|
2625
2733
|
inert: !visible,
|
|
2626
2734
|
"aria-hidden": visible ? void 0 : true,
|
|
@@ -2773,6 +2881,25 @@ function itemTitle(item, kind) {
|
|
|
2773
2881
|
return kind?.label ?? item.kind;
|
|
2774
2882
|
}
|
|
2775
2883
|
var isMac = () => typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(navigator.platform ?? "");
|
|
2884
|
+
function windowTabsSignature(state, windowId) {
|
|
2885
|
+
const frame2 = canvasWindowFrame(state, windowId);
|
|
2886
|
+
if (!frame2) return "";
|
|
2887
|
+
const rows = [];
|
|
2888
|
+
for (const paneId of listPaneIds(frame2.layout)) {
|
|
2889
|
+
for (const itemId of state.panes[paneId]?.itemIds ?? []) rows.push(`${itemId}${paneId}`);
|
|
2890
|
+
}
|
|
2891
|
+
return rows.join("");
|
|
2892
|
+
}
|
|
2893
|
+
function parseWindowTabs(signature) {
|
|
2894
|
+
if (!signature) return [];
|
|
2895
|
+
const perPane = /* @__PURE__ */ new Map();
|
|
2896
|
+
return signature.split("").map((row) => {
|
|
2897
|
+
const [itemId = "", paneId = ""] = row.split("");
|
|
2898
|
+
const index = perPane.get(paneId) ?? 0;
|
|
2899
|
+
perPane.set(paneId, index + 1);
|
|
2900
|
+
return { itemId, paneId, index };
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2776
2903
|
function CanvasPaneView({ paneId }) {
|
|
2777
2904
|
const canvas = useCanvas();
|
|
2778
2905
|
const { windowId, tabStrip } = useCanvasInternals();
|
|
@@ -2791,6 +2918,8 @@ function CanvasPaneView({ paneId }) {
|
|
|
2791
2918
|
const role = useCanvasState((s) => windowId === CANVAS_MAIN_WINDOW ? "main" : s.windows[windowId]?.role ?? "window");
|
|
2792
2919
|
const canGoBack = useCanvasState((s) => active ? selectCanvasCanGoBack(s, active.id) : false);
|
|
2793
2920
|
const kind = useItemKind(active?.kind ?? "");
|
|
2921
|
+
const singlePane = useCanvasSinglePane();
|
|
2922
|
+
const windowTabs = useCanvasState((s) => singlePane ? windowTabsSignature(s, windowId) : "");
|
|
2794
2923
|
const sectionRef = (0, import_react8.useRef)(null);
|
|
2795
2924
|
const bodyRef = (0, import_react8.useRef)(null);
|
|
2796
2925
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
@@ -2810,7 +2939,8 @@ function CanvasPaneView({ paneId }) {
|
|
|
2810
2939
|
const Leading = kind?.HeaderLeading;
|
|
2811
2940
|
const Action = kind?.HeaderAction;
|
|
2812
2941
|
const Toolbar = kind?.Toolbar;
|
|
2813
|
-
const
|
|
2942
|
+
const tabs = singlePane ? parseWindowTabs(windowTabs) : pane.itemIds.map((itemId, index) => ({ itemId, paneId, index }));
|
|
2943
|
+
const single = tabStrip === "auto" && tabs.length === 1 && active;
|
|
2814
2944
|
const closeLabel = role === "pop-out" && paneCount === 1 ? "Close window" : paneCount > 1 ? "Close pane" : "Close canvas";
|
|
2815
2945
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2816
2946
|
CanvasPanel,
|
|
@@ -2823,11 +2953,14 @@ function CanvasPaneView({ paneId }) {
|
|
|
2823
2953
|
"data-mxc-pane": paneId,
|
|
2824
2954
|
header: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2825
2955
|
canGoBack && active ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CanvasHeaderButton, { className: "mxc-back", label: "Back", onClick: () => void canvas.back(active.id), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ArrowLeftIcon, {}) }) : null,
|
|
2826
|
-
single && active ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SingleTitle, { item: active, kind }) :
|
|
2956
|
+
single && active ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SingleTitle, { item: active, kind }) : tabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabStrip, { tabs, activeItemId: pane.activeItemId }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mxc-panel-title", children: "New tab" }),
|
|
2827
2957
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CanvasHeaderButton, { className: "mxc-new-tab", label: "New tab", onClick: () => void canvas.newTab(paneId), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PlusIcon, {}) })
|
|
2828
2958
|
] }),
|
|
2829
2959
|
leading: Leading && kindProps ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Leading, { ...kindProps }) : null,
|
|
2830
|
-
actions: Action && kindProps ?
|
|
2960
|
+
actions: Action && kindProps ? (
|
|
2961
|
+
// Its own gapless row: a kind's tap buttons space themselves (the tap-target placement rule).
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mxc-kind-actions", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Action, { ...kindProps }) })
|
|
2963
|
+
) : null,
|
|
2831
2964
|
renderMenu: () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PaneMenu, { paneId, windowId, activeItem: active, kind, kindProps, tabCount: pane.itemIds.length, role }),
|
|
2832
2965
|
expanded: isFullscreen,
|
|
2833
2966
|
onToggleExpand: () => canvas.setFullscreen(!isFullscreen),
|
|
@@ -2869,38 +3002,71 @@ function SingleTitle({ item, kind }) {
|
|
|
2869
3002
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "mxc-tab-close", "data-slot": "browser-tab-close", "aria-label": `Close ${title}`, onClick: () => canvas.close(item.id), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CloseIcon, {}) })
|
|
2870
3003
|
] });
|
|
2871
3004
|
}
|
|
2872
|
-
function TabStrip({
|
|
3005
|
+
function TabStrip({ tabs, activeItemId }) {
|
|
2873
3006
|
const strip = (0, import_react8.useRef)(null);
|
|
3007
|
+
const [overflow, setOverflow] = (0, import_react8.useState)({ start: false, end: false });
|
|
3008
|
+
const measure = (0, import_react8.useRef)(() => void 0);
|
|
3009
|
+
measure.current = () => {
|
|
3010
|
+
const el = strip.current;
|
|
3011
|
+
if (!el) return;
|
|
3012
|
+
const start = el.scrollLeft > 1;
|
|
3013
|
+
const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
3014
|
+
setOverflow((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
3015
|
+
};
|
|
3016
|
+
const tabKey = tabs.map((tab) => tab.itemId).join("|");
|
|
2874
3017
|
useIsoLayoutEffect2(() => {
|
|
2875
3018
|
const el = strip.current;
|
|
2876
3019
|
if (!el) return void 0;
|
|
2877
3020
|
const reveal = () => {
|
|
2878
3021
|
const tab = el.querySelector('[role="tab"][aria-selected="true"]');
|
|
2879
|
-
if (
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
3022
|
+
if (tab) {
|
|
3023
|
+
const left = tab.offsetLeft;
|
|
3024
|
+
const right = left + tab.offsetWidth;
|
|
3025
|
+
if (left < el.scrollLeft) el.scrollLeft = left;
|
|
3026
|
+
else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth;
|
|
3027
|
+
}
|
|
3028
|
+
measure.current();
|
|
2884
3029
|
};
|
|
2885
3030
|
reveal();
|
|
2886
3031
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(reveal);
|
|
2887
3032
|
observer?.observe(el);
|
|
2888
|
-
const
|
|
2889
|
-
if (activeTab) observer?.observe(activeTab);
|
|
3033
|
+
for (const tab of el.querySelectorAll('[role="tab"]')) observer?.observe(tab);
|
|
2890
3034
|
return () => observer?.disconnect();
|
|
2891
|
-
}, [activeItemId,
|
|
2892
|
-
|
|
3035
|
+
}, [activeItemId, tabKey]);
|
|
3036
|
+
const page = (direction) => {
|
|
3037
|
+
const el = strip.current;
|
|
3038
|
+
if (!el) return;
|
|
3039
|
+
const by = Math.max(48, Math.round(el.clientWidth * 0.8)) * direction;
|
|
3040
|
+
if (typeof el.scrollBy === "function") el.scrollBy({ left: by, behavior: "smooth" });
|
|
3041
|
+
else el.scrollLeft += by;
|
|
3042
|
+
};
|
|
3043
|
+
const overflowing = overflow.start || overflow.end;
|
|
3044
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
2893
3045
|
"div",
|
|
2894
3046
|
{
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
"
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
3047
|
+
className: "mxc-tabstrip",
|
|
3048
|
+
"data-overflow": overflowing ? "" : void 0,
|
|
3049
|
+
"data-overflow-start": overflow.start ? "" : void 0,
|
|
3050
|
+
"data-overflow-end": overflow.end ? "" : void 0,
|
|
3051
|
+
children: [
|
|
3052
|
+
overflowing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs left", available: overflow.start, onClick: () => page(-1), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}) }) : null,
|
|
3053
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3054
|
+
"div",
|
|
3055
|
+
{
|
|
3056
|
+
ref: strip,
|
|
3057
|
+
className: "mxc-tabs",
|
|
3058
|
+
role: "tablist",
|
|
3059
|
+
"aria-label": "Canvas tabs",
|
|
3060
|
+
onScroll: () => measure.current(),
|
|
3061
|
+
onWheel: (event) => {
|
|
3062
|
+
const el = strip.current;
|
|
3063
|
+
if (el && Math.abs(event.deltaY) > Math.abs(event.deltaX)) el.scrollLeft += event.deltaY;
|
|
3064
|
+
},
|
|
3065
|
+
children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ItemTab, { itemId: tab.itemId, paneId: tab.paneId, index: tab.index, active: tab.itemId === activeItemId }, tab.itemId))
|
|
3066
|
+
}
|
|
3067
|
+
),
|
|
3068
|
+
overflowing ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs right", available: overflow.end, onClick: () => page(1), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronRightIcon, {}) }) : null
|
|
3069
|
+
]
|
|
2904
3070
|
}
|
|
2905
3071
|
);
|
|
2906
3072
|
}
|
|
@@ -3053,7 +3219,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3053
3219
|
const isFullscreen = useCanvasState((s) => selectCanvasIsFullscreen(s, windowId));
|
|
3054
3220
|
const width = useCanvasState((s) => selectCanvasWidth(s, windowId));
|
|
3055
3221
|
const layout = useCanvasState((s) => selectCanvasLayout(s, windowId));
|
|
3222
|
+
const focusedPaneId = useCanvasState((s) => selectCanvasFocusedPaneId(s, windowId));
|
|
3056
3223
|
const region = useRegionWidth();
|
|
3224
|
+
const viewport = useViewportWidth();
|
|
3225
|
+
const singlePane = placement === "docked" && viewport > 0 && viewport <= CANVAS_PHONE_MAX_WIDTH;
|
|
3057
3226
|
const [dragWidth, setDragWidth] = (0, import_react9.useState)(null);
|
|
3058
3227
|
const [slots] = (0, import_react9.useState)(createCanvasSlots);
|
|
3059
3228
|
const ref = (0, import_react9.useRef)(null);
|
|
@@ -3118,10 +3287,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3118
3287
|
}
|
|
3119
3288
|
}
|
|
3120
3289
|
) : null,
|
|
3121
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
3122
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mxc-layout", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LayoutNodeView, { node: layout }) }),
|
|
3290
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CanvasSlotsContext.Provider, { value: slots, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(CanvasSinglePaneContext.Provider, { value: singlePane, children: [
|
|
3291
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mxc-layout", "data-single-pane": singlePane ? "" : void 0, children: singlePane ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CanvasPaneView, { paneId: presentedPane(layout, focusedPaneId) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LayoutNodeView, { node: layout }) }),
|
|
3123
3292
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CanvasBodyLayer, {})
|
|
3124
|
-
] })
|
|
3293
|
+
] }) })
|
|
3125
3294
|
] })
|
|
3126
3295
|
}
|
|
3127
3296
|
);
|
|
@@ -3169,6 +3338,10 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3169
3338
|
}
|
|
3170
3339
|
);
|
|
3171
3340
|
}
|
|
3341
|
+
function presentedPane(layout, focused) {
|
|
3342
|
+
const ids = listPaneIds(layout);
|
|
3343
|
+
return ids.find((id) => id === focused) ?? ids[0];
|
|
3344
|
+
}
|
|
3172
3345
|
function LayoutNodeView({ node }) {
|
|
3173
3346
|
if (node.type === "pane") return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CanvasPaneView, { paneId: node.paneId });
|
|
3174
3347
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SplitView, { node });
|
|
@@ -3271,6 +3444,7 @@ function CanvasToggle({
|
|
|
3271
3444
|
ariaLabel: isOpen ? "Hide canvas" : "Show canvas",
|
|
3272
3445
|
tooltip: `${isOpen ? "Hide canvas" : "Show canvas"}${hint ? ` (${hint})` : ""}`,
|
|
3273
3446
|
icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PanelRightIcon, {}),
|
|
3447
|
+
pressed: isOpen,
|
|
3274
3448
|
onClick: () => controller.toggle()
|
|
3275
3449
|
}
|
|
3276
3450
|
),
|