@ai-matrx/canvas 0.5.3 → 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 +15 -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 +218 -47
- 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 +218 -47
- package/dist/react.js.map +1 -1
- package/dist/styles.css +51 -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.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, ReactElement } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { P as CanvasRect, d as CanvasPaneId, C as CanvasItemId, a as CanvasWindowId, b as CanvasJson, i as CanvasItem, h as CanvasController, N as CanvasPresentation, S as CanvasSessionEvent, Q as CanvasSessionAttachReason, R as CanvasSessionDetachReason, M as CanvasPreferredTarget, e as CanvasStoreBinding, L as CanvasPersistencePort, D as CanvasErrorSink, G as CanvasLayoutRules, Z as ToggleKindInput, F as CanvasKindVisibility, f as CanvasState } from './toggleKind-DhDmWExm.cjs';
|
|
4
4
|
|
|
5
5
|
/** One floating thing. Plain data, so a host can draw it in another process. */
|
|
6
6
|
type CanvasFloating = {
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, ReactElement } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { P as CanvasRect, d as CanvasPaneId, C as CanvasItemId, a as CanvasWindowId, b as CanvasJson, i as CanvasItem, h as CanvasController, N as CanvasPresentation, S as CanvasSessionEvent, Q as CanvasSessionAttachReason, R as CanvasSessionDetachReason, M as CanvasPreferredTarget, e as CanvasStoreBinding, L as CanvasPersistencePort, D as CanvasErrorSink, G as CanvasLayoutRules, Z as ToggleKindInput, F as CanvasKindVisibility, f as CanvasState } from './toggleKind-DhDmWExm.js';
|
|
4
4
|
|
|
5
5
|
/** One floating thing. Plain data, so a host can draw it in another process. */
|
|
6
6
|
type CanvasFloating = {
|
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;
|
|
@@ -1936,6 +1969,8 @@ var LayoutGridIcon = (p) => /* @__PURE__ */ jsxs2(Glyph, { ...p, children: [
|
|
|
1936
1969
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1937
1970
|
/* @__PURE__ */ jsx3("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1938
1971
|
] });
|
|
1972
|
+
var ChevronLeftIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m15 18-6-6 6-6" }) });
|
|
1973
|
+
var ChevronRightIcon = (p) => /* @__PURE__ */ jsx3(Glyph, { ...p, children: /* @__PURE__ */ jsx3("path", { d: "m9 18 6-6-6-6" }) });
|
|
1939
1974
|
|
|
1940
1975
|
// src/react/chrome.tsx
|
|
1941
1976
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -2271,6 +2306,21 @@ function useChoices() {
|
|
|
2271
2306
|
}
|
|
2272
2307
|
return { contextual, global };
|
|
2273
2308
|
}
|
|
2309
|
+
var FOLLOW_UP_MS = 600;
|
|
2310
|
+
function swallowFollowUpClicks() {
|
|
2311
|
+
if (typeof window === "undefined") return;
|
|
2312
|
+
const types = ["pointerdown", "mousedown", "pointerup", "mouseup", "click", "dblclick"];
|
|
2313
|
+
const swallow = (event) => {
|
|
2314
|
+
if (event.detail >= 2 || event.type === "dblclick") {
|
|
2315
|
+
event.preventDefault();
|
|
2316
|
+
event.stopPropagation();
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
for (const type of types) window.addEventListener(type, swallow, true);
|
|
2320
|
+
setTimeout(() => {
|
|
2321
|
+
for (const type of types) window.removeEventListener(type, swallow, true);
|
|
2322
|
+
}, FOLLOW_UP_MS);
|
|
2323
|
+
}
|
|
2274
2324
|
function LauncherGrid({ onChoose }) {
|
|
2275
2325
|
const { contextual, global } = useChoices();
|
|
2276
2326
|
if (contextual.length === 0 && global.length === 0) return /* @__PURE__ */ jsx5(CanvasPanelEmpty, { children: "Nothing to open" });
|
|
@@ -2278,10 +2328,23 @@ function LauncherGrid({ onChoose }) {
|
|
|
2278
2328
|
label ? /* @__PURE__ */ jsx5("h3", { className: "mxc-launcher-heading", children: label }) : null,
|
|
2279
2329
|
/* @__PURE__ */ jsx5("div", { className: "mxc-launcher-grid", children: choices.map((choice) => {
|
|
2280
2330
|
const Icon = choice.icon;
|
|
2281
|
-
return /* @__PURE__ */ jsxs4(
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2331
|
+
return /* @__PURE__ */ jsxs4(
|
|
2332
|
+
"button",
|
|
2333
|
+
{
|
|
2334
|
+
type: "button",
|
|
2335
|
+
className: "mxc-launcher-item",
|
|
2336
|
+
onClick: (event) => {
|
|
2337
|
+
if (event.detail >= 2) return;
|
|
2338
|
+
swallowFollowUpClicks();
|
|
2339
|
+
onChoose(choice.input);
|
|
2340
|
+
},
|
|
2341
|
+
children: [
|
|
2342
|
+
/* @__PURE__ */ jsx5(Icon, { className: "mxc-launcher-icon" }),
|
|
2343
|
+
/* @__PURE__ */ jsx5("span", { children: choice.label })
|
|
2344
|
+
]
|
|
2345
|
+
},
|
|
2346
|
+
choice.id
|
|
2347
|
+
);
|
|
2285
2348
|
}) })
|
|
2286
2349
|
] });
|
|
2287
2350
|
return /* @__PURE__ */ jsxs4("div", { className: "mxc-launcher", children: [
|
|
@@ -2394,6 +2457,10 @@ var CanvasSlotsContext = createContext3(null);
|
|
|
2394
2457
|
function useCanvasSlots() {
|
|
2395
2458
|
return useContext3(CanvasSlotsContext);
|
|
2396
2459
|
}
|
|
2460
|
+
var CanvasSinglePaneContext = createContext3(false);
|
|
2461
|
+
function useCanvasSinglePane() {
|
|
2462
|
+
return useContext3(CanvasSinglePaneContext);
|
|
2463
|
+
}
|
|
2397
2464
|
function useSlotSize(slots, paneId) {
|
|
2398
2465
|
return useSyncExternalStore3(
|
|
2399
2466
|
slots.subscribe,
|
|
@@ -2425,19 +2492,36 @@ function usePanePresentation(slots, paneId, isFullscreen, paneCount) {
|
|
|
2425
2492
|
var hostKey = (item) => item.instance ?? item.id;
|
|
2426
2493
|
var SEP2 = "";
|
|
2427
2494
|
var ROW = "";
|
|
2495
|
+
function bornAt(instance, fallback) {
|
|
2496
|
+
const stamp = instance ? Number.parseInt(instance.slice(0, instance.indexOf(":")), 36) : Number.NaN;
|
|
2497
|
+
return Number.isFinite(stamp) ? stamp : fallback;
|
|
2498
|
+
}
|
|
2499
|
+
function keptCanvasSteps(item) {
|
|
2500
|
+
const back = (item.history ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2501
|
+
const forward = (item.kept ?? []).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
2502
|
+
return [...back, ...forward].filter((entry) => !!entry.instance);
|
|
2503
|
+
}
|
|
2428
2504
|
function layerSignature(state, windowId, onlyPane) {
|
|
2429
2505
|
const frame2 = canvasWindowFrame(state, windowId);
|
|
2430
2506
|
if (!frame2) return "";
|
|
2431
2507
|
const rows = [];
|
|
2508
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2509
|
+
const add = (hostId, born, fields) => {
|
|
2510
|
+
if (seen.has(hostId)) return;
|
|
2511
|
+
seen.add(hostId);
|
|
2512
|
+
rows.push({ born, hostId, row: [hostId, ...fields].join(SEP2) });
|
|
2513
|
+
};
|
|
2432
2514
|
const paneIds = listPaneIds(frame2.layout);
|
|
2433
2515
|
for (const paneId of onlyPane ? [onlyPane] : paneIds) {
|
|
2434
2516
|
const pane = state.panes[paneId];
|
|
2435
2517
|
for (const itemId of pane?.itemIds ?? []) {
|
|
2436
2518
|
const item = state.items[itemId];
|
|
2437
|
-
if (item)
|
|
2519
|
+
if (!item) continue;
|
|
2520
|
+
add(hostKey(item), bornAt(item.instance, item.openedAt), [itemId, paneId, pane?.activeItemId === itemId ? "1" : "0", "0"]);
|
|
2521
|
+
for (const step of keptCanvasSteps(item)) add(step.instance, bornAt(step.instance, item.openedAt), [itemId, paneId, "0", "1"]);
|
|
2438
2522
|
}
|
|
2439
2523
|
}
|
|
2440
|
-
rows.sort((a, b) => a.
|
|
2524
|
+
rows.sort((a, b) => a.born - b.born || a.hostId.localeCompare(b.hostId));
|
|
2441
2525
|
const head = [frame2.isOpen ? "1" : "0", frame2.isFullscreen ? "1" : "0", frame2.focusedPaneId, String(paneIds.length)].join(SEP2);
|
|
2442
2526
|
return [head, ...rows.map((r) => r.row)].join(ROW);
|
|
2443
2527
|
}
|
|
@@ -2451,8 +2535,8 @@ function parseSignature(signature) {
|
|
|
2451
2535
|
focusedPaneId,
|
|
2452
2536
|
paneCount: Number(count) || 1,
|
|
2453
2537
|
placed: rows.map((row) => {
|
|
2454
|
-
const [hostId = "", itemId = "", paneId = "", active] = row.split(SEP2);
|
|
2455
|
-
return { hostId, itemId, paneId, active: active === "1" };
|
|
2538
|
+
const [hostId = "", itemId = "", paneId = "", active, kept] = row.split(SEP2);
|
|
2539
|
+
return { hostId, itemId, paneId, active: active === "1", kept: kept === "1" };
|
|
2456
2540
|
})
|
|
2457
2541
|
};
|
|
2458
2542
|
}
|
|
@@ -2499,6 +2583,7 @@ function CanvasBodyLayer() {
|
|
|
2499
2583
|
const { windowId, layer } = useCanvasInternals();
|
|
2500
2584
|
const slots = useCanvasSlots();
|
|
2501
2585
|
const signature = useCanvasState((s) => layerSignature(s, windowId));
|
|
2586
|
+
const single = useCanvasSinglePane();
|
|
2502
2587
|
const root = useRef5(null);
|
|
2503
2588
|
const [placement] = useState4(
|
|
2504
2589
|
() => slots ? createPlacement(() => root.current, slots, () => layer.native.remeasure()) : null
|
|
@@ -2507,13 +2592,14 @@ function CanvasBodyLayer() {
|
|
|
2507
2592
|
useIsoLayoutEffect(() => slots && placement ? slots.subscribe(placement.place) : void 0, [slots, placement]);
|
|
2508
2593
|
const shape = parseSignature(signature);
|
|
2509
2594
|
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(
|
|
2595
|
+
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
2596
|
BodyHost,
|
|
2512
2597
|
{
|
|
2513
2598
|
hostId,
|
|
2514
2599
|
itemId,
|
|
2600
|
+
kept,
|
|
2515
2601
|
paneId,
|
|
2516
|
-
visible: shape.isOpen && active,
|
|
2602
|
+
visible: shape.isOpen && active && (!single || paneId === shape.focusedPaneId),
|
|
2517
2603
|
focused: active && shape.focusedPaneId === paneId,
|
|
2518
2604
|
isFullscreen: shape.isFullscreen,
|
|
2519
2605
|
paneCount: shape.paneCount,
|
|
@@ -2527,11 +2613,12 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2527
2613
|
const { windowId } = useCanvasInternals();
|
|
2528
2614
|
const shape = parseSignature(useCanvasState((s) => layerSignature(s, windowId, paneId)));
|
|
2529
2615
|
if (!shape) return null;
|
|
2530
|
-
return /* @__PURE__ */ jsx6(Fragment, { children: shape.placed.map(({ hostId, itemId, active }) => /* @__PURE__ */ jsx6(
|
|
2616
|
+
return /* @__PURE__ */ jsx6(Fragment, { children: shape.placed.map(({ hostId, itemId, active, kept }) => /* @__PURE__ */ jsx6(
|
|
2531
2617
|
BodyHost,
|
|
2532
2618
|
{
|
|
2533
2619
|
hostId,
|
|
2534
2620
|
itemId,
|
|
2621
|
+
kept,
|
|
2535
2622
|
paneId,
|
|
2536
2623
|
visible: active,
|
|
2537
2624
|
focused: active && shape.focusedPaneId === paneId,
|
|
@@ -2543,9 +2630,22 @@ function CanvasPaneInlineBodies({ paneId, slots }) {
|
|
|
2543
2630
|
hostId
|
|
2544
2631
|
)) });
|
|
2545
2632
|
}
|
|
2633
|
+
function stepAsItem(step, openedAt) {
|
|
2634
|
+
return {
|
|
2635
|
+
id: canvasItemId(step.kind, step.key),
|
|
2636
|
+
kind: step.kind,
|
|
2637
|
+
key: step.key,
|
|
2638
|
+
title: step.title,
|
|
2639
|
+
data: step.data,
|
|
2640
|
+
openedAt,
|
|
2641
|
+
updatedAt: openedAt,
|
|
2642
|
+
...step.instance ? { instance: step.instance } : {}
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2546
2645
|
var BodyHost = memo(function BodyHost2({
|
|
2547
2646
|
hostId,
|
|
2548
2647
|
itemId,
|
|
2648
|
+
kept,
|
|
2549
2649
|
paneId,
|
|
2550
2650
|
visible,
|
|
2551
2651
|
focused,
|
|
@@ -2556,7 +2656,14 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2556
2656
|
}) {
|
|
2557
2657
|
const canvas = useCanvas();
|
|
2558
2658
|
const { windowId, ports } = useCanvasInternals();
|
|
2559
|
-
const
|
|
2659
|
+
const live = useCanvasState((s) => kept ? void 0 : s.items[itemId]);
|
|
2660
|
+
const step = useCanvasState((s) => {
|
|
2661
|
+
const ownerItem = kept ? s.items[itemId] : void 0;
|
|
2662
|
+
return ownerItem ? keptCanvasSteps(ownerItem).find((e) => e.instance === hostId) : void 0;
|
|
2663
|
+
});
|
|
2664
|
+
const owner = useCanvasState((s) => kept ? s.items[itemId]?.openedAt : void 0);
|
|
2665
|
+
const keptItem = useMemo2(() => step ? stepAsItem(step, owner ?? 0) : void 0, [step, owner]);
|
|
2666
|
+
const item = kept ? keptItem : live;
|
|
2560
2667
|
const kind = useItemKind(item?.kind ?? "");
|
|
2561
2668
|
const element = useRef5(null);
|
|
2562
2669
|
const [everShown, setEverShown] = useState4(visible);
|
|
@@ -2580,6 +2687,7 @@ var BodyHost = memo(function BodyHost2({
|
|
|
2580
2687
|
"data-visible": visible ? "true" : "false",
|
|
2581
2688
|
"data-kind": item.kind,
|
|
2582
2689
|
"data-item": item.id,
|
|
2690
|
+
"data-kept": kept ? "" : void 0,
|
|
2583
2691
|
"data-pane": paneId,
|
|
2584
2692
|
inert: !visible,
|
|
2585
2693
|
"aria-hidden": visible ? void 0 : true,
|
|
@@ -2732,6 +2840,25 @@ function itemTitle(item, kind) {
|
|
|
2732
2840
|
return kind?.label ?? item.kind;
|
|
2733
2841
|
}
|
|
2734
2842
|
var isMac = () => typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(navigator.platform ?? "");
|
|
2843
|
+
function windowTabsSignature(state, windowId) {
|
|
2844
|
+
const frame2 = canvasWindowFrame(state, windowId);
|
|
2845
|
+
if (!frame2) return "";
|
|
2846
|
+
const rows = [];
|
|
2847
|
+
for (const paneId of listPaneIds(frame2.layout)) {
|
|
2848
|
+
for (const itemId of state.panes[paneId]?.itemIds ?? []) rows.push(`${itemId}${paneId}`);
|
|
2849
|
+
}
|
|
2850
|
+
return rows.join("");
|
|
2851
|
+
}
|
|
2852
|
+
function parseWindowTabs(signature) {
|
|
2853
|
+
if (!signature) return [];
|
|
2854
|
+
const perPane = /* @__PURE__ */ new Map();
|
|
2855
|
+
return signature.split("").map((row) => {
|
|
2856
|
+
const [itemId = "", paneId = ""] = row.split("");
|
|
2857
|
+
const index = perPane.get(paneId) ?? 0;
|
|
2858
|
+
perPane.set(paneId, index + 1);
|
|
2859
|
+
return { itemId, paneId, index };
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2735
2862
|
function CanvasPaneView({ paneId }) {
|
|
2736
2863
|
const canvas = useCanvas();
|
|
2737
2864
|
const { windowId, tabStrip } = useCanvasInternals();
|
|
@@ -2750,6 +2877,8 @@ function CanvasPaneView({ paneId }) {
|
|
|
2750
2877
|
const role = useCanvasState((s) => windowId === CANVAS_MAIN_WINDOW ? "main" : s.windows[windowId]?.role ?? "window");
|
|
2751
2878
|
const canGoBack = useCanvasState((s) => active ? selectCanvasCanGoBack(s, active.id) : false);
|
|
2752
2879
|
const kind = useItemKind(active?.kind ?? "");
|
|
2880
|
+
const singlePane = useCanvasSinglePane();
|
|
2881
|
+
const windowTabs = useCanvasState((s) => singlePane ? windowTabsSignature(s, windowId) : "");
|
|
2753
2882
|
const sectionRef = useRef6(null);
|
|
2754
2883
|
const bodyRef = useRef6(null);
|
|
2755
2884
|
const presentation = usePanePresentation(slots, paneId, isFullscreen, paneCount);
|
|
@@ -2769,7 +2898,8 @@ function CanvasPaneView({ paneId }) {
|
|
|
2769
2898
|
const Leading = kind?.HeaderLeading;
|
|
2770
2899
|
const Action = kind?.HeaderAction;
|
|
2771
2900
|
const Toolbar = kind?.Toolbar;
|
|
2772
|
-
const
|
|
2901
|
+
const tabs = singlePane ? parseWindowTabs(windowTabs) : pane.itemIds.map((itemId, index) => ({ itemId, paneId, index }));
|
|
2902
|
+
const single = tabStrip === "auto" && tabs.length === 1 && active;
|
|
2773
2903
|
const closeLabel = role === "pop-out" && paneCount === 1 ? "Close window" : paneCount > 1 ? "Close pane" : "Close canvas";
|
|
2774
2904
|
return /* @__PURE__ */ jsxs6(
|
|
2775
2905
|
CanvasPanel,
|
|
@@ -2782,7 +2912,7 @@ function CanvasPaneView({ paneId }) {
|
|
|
2782
2912
|
"data-mxc-pane": paneId,
|
|
2783
2913
|
header: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
2784
2914
|
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 }) :
|
|
2915
|
+
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
2916
|
/* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-new-tab", label: "New tab", onClick: () => void canvas.newTab(paneId), children: /* @__PURE__ */ jsx7(PlusIcon, {}) })
|
|
2787
2917
|
] }),
|
|
2788
2918
|
leading: Leading && kindProps ? /* @__PURE__ */ jsx7(Leading, { ...kindProps }) : null,
|
|
@@ -2831,38 +2961,71 @@ function SingleTitle({ item, kind }) {
|
|
|
2831
2961
|
/* @__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
2962
|
] });
|
|
2833
2963
|
}
|
|
2834
|
-
function TabStrip({
|
|
2964
|
+
function TabStrip({ tabs, activeItemId }) {
|
|
2835
2965
|
const strip = useRef6(null);
|
|
2966
|
+
const [overflow, setOverflow] = useState5({ start: false, end: false });
|
|
2967
|
+
const measure = useRef6(() => void 0);
|
|
2968
|
+
measure.current = () => {
|
|
2969
|
+
const el = strip.current;
|
|
2970
|
+
if (!el) return;
|
|
2971
|
+
const start = el.scrollLeft > 1;
|
|
2972
|
+
const end = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
2973
|
+
setOverflow((prev) => prev.start === start && prev.end === end ? prev : { start, end });
|
|
2974
|
+
};
|
|
2975
|
+
const tabKey = tabs.map((tab) => tab.itemId).join("|");
|
|
2836
2976
|
useIsoLayoutEffect2(() => {
|
|
2837
2977
|
const el = strip.current;
|
|
2838
2978
|
if (!el) return void 0;
|
|
2839
2979
|
const reveal = () => {
|
|
2840
2980
|
const tab = el.querySelector('[role="tab"][aria-selected="true"]');
|
|
2841
|
-
if (
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2981
|
+
if (tab) {
|
|
2982
|
+
const left = tab.offsetLeft;
|
|
2983
|
+
const right = left + tab.offsetWidth;
|
|
2984
|
+
if (left < el.scrollLeft) el.scrollLeft = left;
|
|
2985
|
+
else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth;
|
|
2986
|
+
}
|
|
2987
|
+
measure.current();
|
|
2846
2988
|
};
|
|
2847
2989
|
reveal();
|
|
2848
2990
|
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(reveal);
|
|
2849
2991
|
observer?.observe(el);
|
|
2850
|
-
const
|
|
2851
|
-
if (activeTab) observer?.observe(activeTab);
|
|
2992
|
+
for (const tab of el.querySelectorAll('[role="tab"]')) observer?.observe(tab);
|
|
2852
2993
|
return () => observer?.disconnect();
|
|
2853
|
-
}, [activeItemId,
|
|
2854
|
-
|
|
2994
|
+
}, [activeItemId, tabKey]);
|
|
2995
|
+
const page = (direction) => {
|
|
2996
|
+
const el = strip.current;
|
|
2997
|
+
if (!el) return;
|
|
2998
|
+
const by = Math.max(48, Math.round(el.clientWidth * 0.8)) * direction;
|
|
2999
|
+
if (typeof el.scrollBy === "function") el.scrollBy({ left: by, behavior: "smooth" });
|
|
3000
|
+
else el.scrollLeft += by;
|
|
3001
|
+
};
|
|
3002
|
+
const overflowing = overflow.start || overflow.end;
|
|
3003
|
+
return /* @__PURE__ */ jsxs6(
|
|
2855
3004
|
"div",
|
|
2856
3005
|
{
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
"
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
3006
|
+
className: "mxc-tabstrip",
|
|
3007
|
+
"data-overflow": overflowing ? "" : void 0,
|
|
3008
|
+
"data-overflow-start": overflow.start ? "" : void 0,
|
|
3009
|
+
"data-overflow-end": overflow.end ? "" : void 0,
|
|
3010
|
+
children: [
|
|
3011
|
+
overflowing ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs left", available: overflow.start, onClick: () => page(-1), children: /* @__PURE__ */ jsx7(ChevronLeftIcon, {}) }) : null,
|
|
3012
|
+
/* @__PURE__ */ jsx7(
|
|
3013
|
+
"div",
|
|
3014
|
+
{
|
|
3015
|
+
ref: strip,
|
|
3016
|
+
className: "mxc-tabs",
|
|
3017
|
+
role: "tablist",
|
|
3018
|
+
"aria-label": "Canvas tabs",
|
|
3019
|
+
onScroll: () => measure.current(),
|
|
3020
|
+
onWheel: (event) => {
|
|
3021
|
+
const el = strip.current;
|
|
3022
|
+
if (el && Math.abs(event.deltaY) > Math.abs(event.deltaX)) el.scrollLeft += event.deltaY;
|
|
3023
|
+
},
|
|
3024
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsx7(ItemTab, { itemId: tab.itemId, paneId: tab.paneId, index: tab.index, active: tab.itemId === activeItemId }, tab.itemId))
|
|
3025
|
+
}
|
|
3026
|
+
),
|
|
3027
|
+
overflowing ? /* @__PURE__ */ jsx7(CanvasHeaderButton, { className: "mxc-tabs-scroll", label: "Scroll tabs right", available: overflow.end, onClick: () => page(1), children: /* @__PURE__ */ jsx7(ChevronRightIcon, {}) }) : null
|
|
3028
|
+
]
|
|
2866
3029
|
}
|
|
2867
3030
|
);
|
|
2868
3031
|
}
|
|
@@ -3015,7 +3178,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3015
3178
|
const isFullscreen = useCanvasState((s) => selectCanvasIsFullscreen(s, windowId));
|
|
3016
3179
|
const width = useCanvasState((s) => selectCanvasWidth(s, windowId));
|
|
3017
3180
|
const layout = useCanvasState((s) => selectCanvasLayout(s, windowId));
|
|
3181
|
+
const focusedPaneId = useCanvasState((s) => selectCanvasFocusedPaneId(s, windowId));
|
|
3018
3182
|
const region = useRegionWidth();
|
|
3183
|
+
const viewport = useViewportWidth();
|
|
3184
|
+
const singlePane = placement === "docked" && viewport > 0 && viewport <= CANVAS_PHONE_MAX_WIDTH;
|
|
3019
3185
|
const [dragWidth, setDragWidth] = useState6(null);
|
|
3020
3186
|
const [slots] = useState6(createCanvasSlots);
|
|
3021
3187
|
const ref = useRef7(null);
|
|
@@ -3080,10 +3246,10 @@ function CanvasColumn({ className, style, onLiveWidth, placement = "docked" }) {
|
|
|
3080
3246
|
}
|
|
3081
3247
|
}
|
|
3082
3248
|
) : null,
|
|
3083
|
-
/* @__PURE__ */
|
|
3084
|
-
/* @__PURE__ */ jsx8("div", { className: "mxc-layout", children: /* @__PURE__ */ jsx8(LayoutNodeView, { node: layout }) }),
|
|
3249
|
+
/* @__PURE__ */ jsx8(CanvasSlotsContext.Provider, { value: slots, children: /* @__PURE__ */ jsxs7(CanvasSinglePaneContext.Provider, { value: singlePane, children: [
|
|
3250
|
+
/* @__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
3251
|
/* @__PURE__ */ jsx8(CanvasBodyLayer, {})
|
|
3086
|
-
] })
|
|
3252
|
+
] }) })
|
|
3087
3253
|
] })
|
|
3088
3254
|
}
|
|
3089
3255
|
);
|
|
@@ -3131,6 +3297,10 @@ function WidthHandle({ width, region, onDrag }) {
|
|
|
3131
3297
|
}
|
|
3132
3298
|
);
|
|
3133
3299
|
}
|
|
3300
|
+
function presentedPane(layout, focused) {
|
|
3301
|
+
const ids = listPaneIds(layout);
|
|
3302
|
+
return ids.find((id) => id === focused) ?? ids[0];
|
|
3303
|
+
}
|
|
3134
3304
|
function LayoutNodeView({ node }) {
|
|
3135
3305
|
if (node.type === "pane") return /* @__PURE__ */ jsx8(CanvasPaneView, { paneId: node.paneId });
|
|
3136
3306
|
return /* @__PURE__ */ jsx8(SplitView, { node });
|
|
@@ -3233,6 +3403,7 @@ function CanvasToggle({
|
|
|
3233
3403
|
ariaLabel: isOpen ? "Hide canvas" : "Show canvas",
|
|
3234
3404
|
tooltip: `${isOpen ? "Hide canvas" : "Show canvas"}${hint ? ` (${hint})` : ""}`,
|
|
3235
3405
|
icon: /* @__PURE__ */ jsx8(PanelRightIcon, {}),
|
|
3406
|
+
pressed: isOpen,
|
|
3236
3407
|
onClick: () => controller.toggle()
|
|
3237
3408
|
}
|
|
3238
3409
|
),
|