isotone 1.0.6 → 1.0.8
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/AGENTS.md +3 -1
- package/CHANGELOG.md +8 -0
- package/README.md +54 -48
- package/dist/{chunk-UYMMI2IH.js → chunk-JGIX4XEH.js} +21 -1
- package/dist/chunk-JGIX4XEH.js.map +1 -0
- package/dist/{chunk-DD6BKC3R.js → chunk-UT2NLGBM.js} +2 -2
- package/dist/{chunk-AP7O357L.js → chunk-X6V2AKJL.js} +25 -5
- package/dist/chunk-X6V2AKJL.js.map +1 -0
- package/dist/{chunk-DBIKWXBE.js → chunk-YFQXGHNT.js} +2 -2
- package/dist/{chunk-DBIKWXBE.js.map → chunk-YFQXGHNT.js.map} +1 -1
- package/dist/cli.js +40 -10
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +6 -0
- package/dist/core.js +1 -1
- package/dist/index.js +204 -28
- package/dist/index.js.map +1 -1
- package/dist/isotone.global.js +52 -24
- package/dist/serve.js +2 -2
- package/dist/server.d.ts +3 -0
- package/dist/server.js +2 -2
- package/dist/vite-plugin.js +3 -3
- package/docs/assets/preview.jpg +0 -0
- package/docs/integrations.md +14 -1
- package/package.json +1 -1
- package/dist/chunk-AP7O357L.js.map +0 -1
- package/dist/chunk-UYMMI2IH.js.map +0 -1
- /package/dist/{chunk-DD6BKC3R.js.map → chunk-UT2NLGBM.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
describeIterationElement,
|
|
5
5
|
elementAsNode,
|
|
6
6
|
isRouteVisible
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JGIX4XEH.js";
|
|
8
8
|
import {
|
|
9
9
|
FEEDBACK_STORAGE_KEY,
|
|
10
10
|
KNOBS_STORAGE_KEY,
|
|
@@ -781,7 +781,17 @@ ${APPEARANCE_CSS}
|
|
|
781
781
|
.isotone-panel[data-open='true'] { display: flex; }
|
|
782
782
|
.isotone-variants-panel { flex-shrink: 1; max-height: calc(100dvh - 76px); }
|
|
783
783
|
.isotone-notes-panel { flex-shrink: 0; max-height: calc(100dvh - 76px); padding: var(--iso-space); gap: var(--iso-space); }
|
|
784
|
-
.isotone-root .isotone-panel { background: var(--iso-bg); border: 1px solid var(--iso-line); border-radius: 12px; box-shadow: var(--iso-panel-shadow);
|
|
784
|
+
.isotone-root .isotone-panel { background: var(--iso-bg); border: 1px solid var(--iso-line); border-radius: 12px; box-shadow: var(--iso-panel-shadow); }
|
|
785
|
+
/* The close control sits on the panel's top-left corner, half outside it, with
|
|
786
|
+
the same face as a note's delete button, a touch larger. It fades in while the
|
|
787
|
+
pointer is over the panel; keyboard focus and touch screens always show it. */
|
|
788
|
+
.isotone-panel-close { position: absolute; left: -9px; top: -9px; z-index: 3; width: 26px; height: 26px; display: grid; place-items: center; padding: 0; border: 0; isolation: isolate; color: var(--iso-muted); background: none; opacity: 0; }
|
|
789
|
+
.isotone-panel-close::before { content: ''; position: absolute; inset: 4px; border-radius: 50%; z-index: -1; background: var(--iso-bg); box-shadow: 0 0 0 1px var(--iso-edge); transition: background-color 120ms ease; }
|
|
790
|
+
.isotone-root .isotone-panel > .isotone-panel-close { transition: opacity 220ms ease, color 120ms ease; }
|
|
791
|
+
.isotone-root .isotone-panel:hover > .isotone-panel-close, .isotone-panel-close:focus-visible { opacity: 1; }
|
|
792
|
+
.isotone-root .isotone-panel > .isotone-panel-close:hover { color: var(--iso-fg); }
|
|
793
|
+
.isotone-panel-close:hover::before { background: var(--iso-control-hover-face); }
|
|
794
|
+
@media (hover: none) { .isotone-panel-close { opacity: 1; } }
|
|
785
795
|
.isotone-panel-head { flex: 0 0 42px; min-height: 42px; display: flex; align-items: center; justify-content: space-between; padding: 0 8px 0 14px; gap: 8px; border-bottom: 1px solid var(--iso-line); }
|
|
786
796
|
.isotone-root .isotone-notes-panel > .isotone-panel-head { display: none; }
|
|
787
797
|
.isotone-root .isotone-vars > .isotone-panel-head { order: 2; flex: 0 0 auto; min-height: 48px; padding: var(--iso-space); border: 0; }
|
|
@@ -795,6 +805,8 @@ ${APPEARANCE_CSS}
|
|
|
795
805
|
.isotone-count { margin-left: 5px; color: var(--iso-muted); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
796
806
|
.isotone-vars { flex: 1; display: flex; flex-direction: column; min-height: 0; position: relative; }
|
|
797
807
|
.isotone-vars { min-height: min(var(--iso-menu-need, 0px), calc(100dvh - 76px)); }
|
|
808
|
+
/* The panel no longer clips, so its corner close can overhang; the variants body clips itself. */
|
|
809
|
+
.isotone-root .isotone-panel > .isotone-vars { overflow: hidden; border-radius: 11px; }
|
|
798
810
|
.isotone-vars-body { flex: 1; overflow-y: auto; min-height: 0; overscroll-behavior: contain; padding: var(--iso-space); display: flex; flex-direction: column; gap: var(--iso-space); }
|
|
799
811
|
/* The footer overlaps only the scroller's bottom padding: content fades beneath
|
|
800
812
|
it while scrolling, but the last control remains fully visible at the end. */
|
|
@@ -878,6 +890,17 @@ ${PILL_ACTIVE_CSS}
|
|
|
878
890
|
surrounds a 16px face without reserving trailing space in every row. */
|
|
879
891
|
.isotone-note > .isotone-x { position: absolute; right: -8px; top: -8px; width: 24px; height: 24px; isolation: isolate; opacity: .6; background: none; box-shadow: none; }
|
|
880
892
|
.isotone-x::before { content: ''; position: absolute; inset: 4px; border-radius: 50%; z-index: -1; background: var(--iso-bg); box-shadow: 0 0 0 1px var(--iso-edge); }
|
|
893
|
+
/* One chip per page that has open notes, this page first. */
|
|
894
|
+
.isotone-pages { display: flex; flex: none; min-width: 0; border-top: 0; border-bottom: 0; border-inline: 0; gap: 4px; overflow-x: auto; overscroll-behavior: contain; margin: 0 calc(0px - var(--iso-space)); padding: 0 var(--iso-space); }
|
|
895
|
+
.isotone-page { flex: 0 1 auto; display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 160px; height: 22px; padding: 0 7px; border-radius: var(--iso-control-radius); border: 1px solid var(--iso-edge); background: transparent; color: var(--iso-muted); font-size: 10px; }
|
|
896
|
+
.isotone-page > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
897
|
+
.isotone-page small { color: var(--iso-faint); font-size: 9px; font-variant-numeric: tabular-nums; }
|
|
898
|
+
.isotone-page[aria-pressed='true'] { color: var(--iso-fg); background: var(--iso-label-face); box-shadow: var(--iso-label-lift-shadow); }
|
|
899
|
+
/* An agent has said it is working on a note: a soft pulse on the note, and a ring on the launcher.
|
|
900
|
+
Both are driven by one shared clock (see syncPulse), so they beat together. */
|
|
901
|
+
.isotone-note-hit { position: relative; }
|
|
902
|
+
.isotone-working { position: absolute; left: 3px; top: 5px; width: 6px; height: 6px; border-radius: 50%; background: var(--iso-focus); }
|
|
903
|
+
.isotone-ring { position: absolute; inset: 0; border-radius: 999px; pointer-events: none; }
|
|
881
904
|
.isotone-list:empty { display: none; }
|
|
882
905
|
${FEEDBACK_LAYOUT_CSS}
|
|
883
906
|
.isotone-compose { flex: none; display: flex; flex-direction: column; }
|
|
@@ -903,7 +926,9 @@ ${FEEDBACK_LAYOUT_CSS}
|
|
|
903
926
|
.isotone-pick::after, .isotone-send::after { content: ''; position: absolute; inset: -2px; border-radius: inherit; }
|
|
904
927
|
.isotone-send { display: grid; place-items: center; margin-left: auto; flex: none; }
|
|
905
928
|
.isotone-notice { font-size: 10px; color: var(--iso-muted); }
|
|
906
|
-
.isotone-hint { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 12px; padding: 8px 10px 8px 14px; border-radius: 24px; background: var(--iso-bg); box-shadow: var(--iso-shadow); font-size: 11px; white-space: nowrap; }
|
|
929
|
+
.isotone-hint { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 12px; padding: 8px 10px 8px 14px; border-radius: 24px; background: var(--iso-bg); box-shadow: var(--iso-shadow); font-size: 11px; white-space: nowrap; transition: opacity 120ms ease; }
|
|
930
|
+
/* Near the pointer the hint fades and lets the page underneath be hovered and pinned. */
|
|
931
|
+
.isotone-root .isotone-hint[data-away='true'] { opacity: 0; pointer-events: none; }
|
|
907
932
|
.isotone-hint button { display: flex; align-items: center; gap: 8px; border-radius: 14px; padding: 5px 9px; }
|
|
908
933
|
.isotone-hint kbd { font: inherit; color: var(--iso-muted); }
|
|
909
934
|
.isotone-hover { position: fixed; pointer-events: none; border: 1px solid var(--iso-pick-line); background: var(--iso-pick-fill); border-radius: 5px; z-index: 2147483001; }
|
|
@@ -924,12 +949,15 @@ ${FEEDBACK_LAYOUT_CSS}
|
|
|
924
949
|
}
|
|
925
950
|
@media (max-height: 580px) { .isotone-list { --iso-list-height: 72px; } .isotone-root .isotone-textarea { max-height: calc(64px + var(--iso-textarea-inset)); } }
|
|
926
951
|
@media (prefers-reduced-motion: reduce) {
|
|
952
|
+
.isotone-ring { box-shadow: 0 0 0 2px color-mix(in srgb, var(--iso-focus) 50%, transparent); }
|
|
927
953
|
.isotone-root .isotone-rail-track { transition: none; }
|
|
928
954
|
.isotone-root .isotone-panel, .isotone-root .isotone-axis-menu, .isotone-root .isotone-rail-tools { transition-property: opacity, visibility; }
|
|
929
955
|
.isotone-root button { transition: opacity 125ms ease; }
|
|
930
956
|
.isotone-root button:not(:disabled):active > svg { transform: none; }
|
|
931
957
|
}
|
|
932
958
|
.isotone-root[data-instant='true'] *, .isotone-root[data-instant='true'] *::before, .isotone-root[data-instant='true'] *::after { transition-duration: 0s !important; transition-delay: 0s !important; animation: none !important; }
|
|
959
|
+
/* Instant mode follows any key press, but the corner close only ever appears under the pointer, so it keeps its fade. */
|
|
960
|
+
.isotone-root[data-instant='true'] .isotone-panel > .isotone-panel-close { transition-duration: 220ms, 120ms !important; }
|
|
933
961
|
${UPDATE_NOTICE_CSS}
|
|
934
962
|
`;
|
|
935
963
|
function ensurePanelStyles(target) {
|
|
@@ -1611,10 +1639,11 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-run
|
|
|
1611
1639
|
function SummaryTag({ label }) {
|
|
1612
1640
|
return /* @__PURE__ */ jsx4("span", { className: "isotone-chip", children: /* @__PURE__ */ jsx4("span", { onPointerOver: showOverflowTitle, children: label }) });
|
|
1613
1641
|
}
|
|
1614
|
-
function NoteSummary({ note }) {
|
|
1642
|
+
function NoteSummary({ note, page }) {
|
|
1615
1643
|
if (!note.feedback.startsWith("[set-defaults]")) {
|
|
1616
1644
|
const elements = note.elements ?? (note.element ? [note.element] : []);
|
|
1617
1645
|
const references = [
|
|
1646
|
+
...page ? [{ key: `page:${page}`, label: page }] : [],
|
|
1618
1647
|
...note.variant ? [{
|
|
1619
1648
|
key: `variant:${note.variant.axisId}`,
|
|
1620
1649
|
label: `${note.variant.label}${note.variant.optionLabel ? ` / ${note.variant.optionLabel}` : ""}`
|
|
@@ -1646,7 +1675,8 @@ function NoteSummary({ note }) {
|
|
|
1646
1675
|
}
|
|
1647
1676
|
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1648
1677
|
/* @__PURE__ */ jsx4("p", { children: note.feedback }),
|
|
1649
|
-
tags.length > 0 && /* @__PURE__ */ jsxs3("span", { className: "isotone-meta", children: [
|
|
1678
|
+
(page || tags.length > 0) && /* @__PURE__ */ jsxs3("span", { className: "isotone-meta", children: [
|
|
1679
|
+
page && /* @__PURE__ */ jsx4(SummaryTag, { label: page }),
|
|
1650
1680
|
tags.slice(0, 3).map((tag) => /* @__PURE__ */ jsx4(SummaryTag, { label: `${tag.name} ${tag.value}` }, `${tag.name}-${tag.value}`)),
|
|
1651
1681
|
tags.length > 3 && /* @__PURE__ */ jsxs3("span", { className: "isotone-request-more", children: [
|
|
1652
1682
|
"+",
|
|
@@ -1757,6 +1787,33 @@ function useDockMotion(ref, visible) {
|
|
|
1757
1787
|
|
|
1758
1788
|
// src/react/IterationPanel.tsx
|
|
1759
1789
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1790
|
+
var PULSE_MS = 2e3;
|
|
1791
|
+
function syncPulse(spread) {
|
|
1792
|
+
return (node) => {
|
|
1793
|
+
if (!node || typeof node.animate !== "function") return;
|
|
1794
|
+
if (node.closest('[data-instant="true"]') || typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
1795
|
+
const tint = "color-mix(in srgb, var(--iso-focus) 55%, transparent)";
|
|
1796
|
+
const pulse = node.animate([
|
|
1797
|
+
{ boxShadow: `0 0 0 0 ${tint}`, offset: 0 },
|
|
1798
|
+
{ boxShadow: `0 0 0 ${spread}px transparent`, offset: 0.7 },
|
|
1799
|
+
{ boxShadow: `0 0 0 ${spread}px transparent`, offset: 1 }
|
|
1800
|
+
], { duration: PULSE_MS, iterations: Infinity, easing: "ease-out" });
|
|
1801
|
+
pulse.startTime = 0;
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
var dotPulse = syncPulse(6);
|
|
1805
|
+
var ringPulse = syncPulse(8);
|
|
1806
|
+
var POLL_MS = 2500;
|
|
1807
|
+
var POLL_MAX_MS = 3e4;
|
|
1808
|
+
function sameNotes(a, b) {
|
|
1809
|
+
if (a.length !== b.length) return false;
|
|
1810
|
+
const byId = new Map(a.map((note) => [note.id, note]));
|
|
1811
|
+
return b.every((note) => {
|
|
1812
|
+
const old = byId.get(note.id);
|
|
1813
|
+
return old !== void 0 && JSON.stringify(old) === JSON.stringify(note);
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
var WORKING_FRESH_MS = 20 * 60 * 1e3;
|
|
1760
1817
|
var DEFAULT_SHORTCUT = {
|
|
1761
1818
|
code: "Period",
|
|
1762
1819
|
mod: true,
|
|
@@ -1786,9 +1843,14 @@ function IterationPanel({
|
|
|
1786
1843
|
null
|
|
1787
1844
|
);
|
|
1788
1845
|
const [hoverRect, setHoverRect] = useState3(null);
|
|
1846
|
+
const [hintAway, setHintAway] = useState3(false);
|
|
1847
|
+
const hintRef = useRef4(null);
|
|
1789
1848
|
const [rects, setRects] = useState3([]);
|
|
1790
1849
|
const [text, setText] = useState3("");
|
|
1791
1850
|
const [notes, setNotes] = useState3([]);
|
|
1851
|
+
const [scope, setScope] = useState3(null);
|
|
1852
|
+
const [clock, setClock] = useState3(() => Math.floor(Date.now() / 6e4) * 6e4);
|
|
1853
|
+
const pollRev = useRef4(null);
|
|
1792
1854
|
const noteRevision = useRef4(0);
|
|
1793
1855
|
const pendingWrites = useRef4(0);
|
|
1794
1856
|
const noteRequest = useRef4(0);
|
|
@@ -1870,7 +1932,25 @@ function IterationPanel({
|
|
|
1870
1932
|
if (active === fabRef.current) rootRef.current?.querySelector(".isotone-rail-button")?.focus();
|
|
1871
1933
|
}, [railOpen, instant]);
|
|
1872
1934
|
const draftKey = `${nsKey}::draft::${route}`;
|
|
1873
|
-
const
|
|
1935
|
+
const openNotes = useMemo3(() => notes.filter((n) => !n.done), [notes]);
|
|
1936
|
+
const pages = useMemo3(() => {
|
|
1937
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1938
|
+
for (const n of openNotes) counts.set(n.route, (counts.get(n.route) ?? 0) + 1);
|
|
1939
|
+
const others = [...counts.keys()].filter((r) => r !== route).sort();
|
|
1940
|
+
return { counts, others };
|
|
1941
|
+
}, [openNotes, route]);
|
|
1942
|
+
const selectedPages = useMemo3(() => {
|
|
1943
|
+
const chosen = (scope ?? [route]).filter((r) => r === route || pages.counts.has(r));
|
|
1944
|
+
return new Set(chosen.length ? chosen : [route]);
|
|
1945
|
+
}, [scope, route, pages]);
|
|
1946
|
+
const visibleNotes = useMemo3(() => openNotes.filter((n) => selectedPages.has(n.route)).sort((a, b) => a.createdAt.localeCompare(b.createdAt)), [openNotes, selectedPages]);
|
|
1947
|
+
const working = useMemo3(() => new Set(visibleNotes.filter((n) => n.workingAt && clock - Date.parse(n.workingAt) < WORKING_FRESH_MS).map((n) => n.id)), [visibleNotes, clock]);
|
|
1948
|
+
function togglePage(page) {
|
|
1949
|
+
const next = new Set(selectedPages);
|
|
1950
|
+
if (next.has(page)) next.delete(page);
|
|
1951
|
+
else next.add(page);
|
|
1952
|
+
setScope(next.size ? [...next] : [route]);
|
|
1953
|
+
}
|
|
1874
1954
|
const pendingActions = useMemo3(() => notes.flatMap((n) => !n.done && n.route === route && n.variantAction ? [n.variantAction] : []), [notes, route]);
|
|
1875
1955
|
const shortcutLabel = shortcut ? [
|
|
1876
1956
|
shortcut.mod ? "\u2318/Ctrl" : "",
|
|
@@ -1882,8 +1962,9 @@ function IterationPanel({
|
|
|
1882
1962
|
if (!notesAvailable) return;
|
|
1883
1963
|
const request = ++noteRequest.current;
|
|
1884
1964
|
const revision = noteRevision.current;
|
|
1885
|
-
void store.
|
|
1965
|
+
void store.pollNotes(null).then((result) => ({ rev: result.rev, notes: result.notes ?? [] })).catch(() => store.pendingNotes().then((fresh) => ({ rev: null, notes: fresh }))).then(({ rev, notes: fresh }) => {
|
|
1886
1966
|
if (request !== noteRequest.current || revision !== noteRevision.current || pendingWrites.current) return;
|
|
1967
|
+
pollRev.current = rev;
|
|
1887
1968
|
setNotes(fresh);
|
|
1888
1969
|
}).catch(() => {
|
|
1889
1970
|
});
|
|
@@ -2050,6 +2131,55 @@ function IterationPanel({
|
|
|
2050
2131
|
useEffect3(() => {
|
|
2051
2132
|
loadNotes();
|
|
2052
2133
|
}, [notesOpen, variantsOpen, route]);
|
|
2134
|
+
useEffect3(() => {
|
|
2135
|
+
if (!notesAvailable || !visible) return;
|
|
2136
|
+
let stopped = false;
|
|
2137
|
+
let running = false;
|
|
2138
|
+
let failures = 0;
|
|
2139
|
+
let timer;
|
|
2140
|
+
const schedule = () => {
|
|
2141
|
+
clearTimeout(timer);
|
|
2142
|
+
if (stopped || document.hidden) return;
|
|
2143
|
+
timer = setTimeout(() => void tick(), Math.min(POLL_MS * 2 ** failures, POLL_MAX_MS));
|
|
2144
|
+
};
|
|
2145
|
+
const tick = async () => {
|
|
2146
|
+
if (stopped || running || document.hidden) return;
|
|
2147
|
+
running = true;
|
|
2148
|
+
const request = noteRequest.current;
|
|
2149
|
+
const revision = noteRevision.current;
|
|
2150
|
+
try {
|
|
2151
|
+
const result = await store.pollNotes(pollRev.current);
|
|
2152
|
+
failures = 0;
|
|
2153
|
+
if (stopped || request !== noteRequest.current || revision !== noteRevision.current || pendingWrites.current) return;
|
|
2154
|
+
pollRev.current = result.rev;
|
|
2155
|
+
const fresh = result.notes;
|
|
2156
|
+
if (fresh) setNotes((prev) => sameNotes(prev, fresh) ? prev : fresh);
|
|
2157
|
+
setClock(Math.floor(Date.now() / 6e4) * 6e4);
|
|
2158
|
+
} catch {
|
|
2159
|
+
failures = Math.min(failures + 1, 8);
|
|
2160
|
+
} finally {
|
|
2161
|
+
running = false;
|
|
2162
|
+
schedule();
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
const wake = () => {
|
|
2166
|
+
if (document.hidden) {
|
|
2167
|
+
clearTimeout(timer);
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
failures = 0;
|
|
2171
|
+
void tick();
|
|
2172
|
+
};
|
|
2173
|
+
schedule();
|
|
2174
|
+
document.addEventListener("visibilitychange", wake);
|
|
2175
|
+
window.addEventListener("focus", wake);
|
|
2176
|
+
return () => {
|
|
2177
|
+
stopped = true;
|
|
2178
|
+
clearTimeout(timer);
|
|
2179
|
+
document.removeEventListener("visibilitychange", wake);
|
|
2180
|
+
window.removeEventListener("focus", wake);
|
|
2181
|
+
};
|
|
2182
|
+
}, [notesAvailable, visible, store]);
|
|
2053
2183
|
useEffect3(() => {
|
|
2054
2184
|
if (variants && notesOpen && !variantsOpen) refreshSource(variants);
|
|
2055
2185
|
}, [notesOpen, variantsOpen, variants]);
|
|
@@ -2164,6 +2294,11 @@ function IterationPanel({
|
|
|
2164
2294
|
cancelAnimationFrame(frame);
|
|
2165
2295
|
const deferred = e.shiftKey;
|
|
2166
2296
|
frame = requestAnimationFrame(() => {
|
|
2297
|
+
const hint = hintRef.current?.getBoundingClientRect();
|
|
2298
|
+
const reach = 48;
|
|
2299
|
+
setHintAway(
|
|
2300
|
+
!!hint && e.clientX > hint.left - reach && e.clientX < hint.right + reach && e.clientY > hint.top - reach && e.clientY < hint.bottom + reach
|
|
2301
|
+
);
|
|
2167
2302
|
const el = deferred ? null : elementAt(e);
|
|
2168
2303
|
setHoverRect(el ? el.getBoundingClientRect() : null);
|
|
2169
2304
|
});
|
|
@@ -2208,6 +2343,7 @@ function IterationPanel({
|
|
|
2208
2343
|
document.body.style.cursor = cursor;
|
|
2209
2344
|
cancelAnimationFrame(frame);
|
|
2210
2345
|
setHoverRect(null);
|
|
2346
|
+
setHintAway(false);
|
|
2211
2347
|
window.removeEventListener("mousemove", onMove, true);
|
|
2212
2348
|
window.removeEventListener("click", onClick, true);
|
|
2213
2349
|
window.removeEventListener("keydown", onKey, true);
|
|
@@ -2217,7 +2353,7 @@ function IterationPanel({
|
|
|
2217
2353
|
const shadow = rootRef.current?.getRootNode();
|
|
2218
2354
|
if (!(shadow instanceof ShadowRoot)) return;
|
|
2219
2355
|
const stop = (e) => e.stopPropagation();
|
|
2220
|
-
const types = ["pointerdown", "
|
|
2356
|
+
const types = ["pointerdown", "mousedown", "touchstart", "click"];
|
|
2221
2357
|
for (const type of types) shadow.addEventListener(type, stop);
|
|
2222
2358
|
return () => {
|
|
2223
2359
|
for (const type of types) shadow.removeEventListener(type, stop);
|
|
@@ -2264,7 +2400,7 @@ function IterationPanel({
|
|
|
2264
2400
|
window.removeEventListener("resize", schedule);
|
|
2265
2401
|
};
|
|
2266
2402
|
}, [targets, picking]);
|
|
2267
|
-
const variantsPanel = variants && /* @__PURE__ */
|
|
2403
|
+
const variantsPanel = variants && /* @__PURE__ */ jsxs4(
|
|
2268
2404
|
"section",
|
|
2269
2405
|
{
|
|
2270
2406
|
id: `${id}-variants`,
|
|
@@ -2276,21 +2412,27 @@ function IterationPanel({
|
|
|
2276
2412
|
ref: (node) => {
|
|
2277
2413
|
node?.toggleAttribute("inert", !variantsOpen);
|
|
2278
2414
|
},
|
|
2279
|
-
children:
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2415
|
+
children: [
|
|
2416
|
+
/* @__PURE__ */ jsx5(PanelClose, { label: "Close variants", onClose: () => {
|
|
2417
|
+
setVariantsOpen(false);
|
|
2418
|
+
setExpanded(false);
|
|
2419
|
+
} }),
|
|
2420
|
+
/* @__PURE__ */ jsx5(
|
|
2421
|
+
VariantsView,
|
|
2422
|
+
{
|
|
2423
|
+
source: variants,
|
|
2424
|
+
active: variantsOpen,
|
|
2425
|
+
onClose: () => {
|
|
2426
|
+
setVariantsOpen(false);
|
|
2427
|
+
setExpanded(false);
|
|
2428
|
+
},
|
|
2429
|
+
onFeedback: notesAvailable ? feedback : void 0,
|
|
2430
|
+
onSaved: loadNotes,
|
|
2431
|
+
onManage: notesAvailable ? queueRemoval : void 0,
|
|
2432
|
+
pendingActions
|
|
2433
|
+
}
|
|
2434
|
+
)
|
|
2435
|
+
]
|
|
2294
2436
|
}
|
|
2295
2437
|
);
|
|
2296
2438
|
const notesPanel = notesAvailable && /* @__PURE__ */ jsxs4(
|
|
@@ -2306,6 +2448,17 @@ function IterationPanel({
|
|
|
2306
2448
|
node?.toggleAttribute("inert", !notesOpen);
|
|
2307
2449
|
},
|
|
2308
2450
|
children: [
|
|
2451
|
+
/* @__PURE__ */ jsx5(
|
|
2452
|
+
PanelClose,
|
|
2453
|
+
{
|
|
2454
|
+
label: "Close feedback",
|
|
2455
|
+
onClose: () => {
|
|
2456
|
+
setNotesOpen(false);
|
|
2457
|
+
setExpanded(false);
|
|
2458
|
+
setPicking(false);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
),
|
|
2309
2462
|
/* @__PURE__ */ jsxs4("div", { className: "isotone-panel-head", children: [
|
|
2310
2463
|
/* @__PURE__ */ jsxs4("h2", { children: [
|
|
2311
2464
|
"Feedback",
|
|
@@ -2328,20 +2481,38 @@ function IterationPanel({
|
|
|
2328
2481
|
}
|
|
2329
2482
|
)
|
|
2330
2483
|
] }),
|
|
2484
|
+
pages.counts.size > 1 && /* @__PURE__ */ jsx5("fieldset", { className: "isotone-pages", "aria-label": "Pages", children: [route, ...pages.others].map((page) => /* @__PURE__ */ jsxs4(
|
|
2485
|
+
"button",
|
|
2486
|
+
{
|
|
2487
|
+
type: "button",
|
|
2488
|
+
className: "isotone-page",
|
|
2489
|
+
"aria-pressed": selectedPages.has(page),
|
|
2490
|
+
title: page === route ? `This page \xB7 ${page}` : page,
|
|
2491
|
+
onClick: () => togglePage(page),
|
|
2492
|
+
children: [
|
|
2493
|
+
/* @__PURE__ */ jsx5("span", { onPointerOver: showOverflowTitle, children: page === route ? "This page" : page }),
|
|
2494
|
+
/* @__PURE__ */ jsx5("small", { children: pages.counts.get(page) ?? 0 })
|
|
2495
|
+
]
|
|
2496
|
+
},
|
|
2497
|
+
page
|
|
2498
|
+
)) }),
|
|
2331
2499
|
/* @__PURE__ */ jsx5("ul", { ref: listRef, className: "isotone-list", children: visibleNotes.map((note) => /* @__PURE__ */ jsxs4(
|
|
2332
2500
|
"li",
|
|
2333
2501
|
{
|
|
2334
2502
|
"data-motion-key": note.id,
|
|
2335
2503
|
className: `isotone-note${editing?.id === note.id ? " isotone-note-editing" : ""}`,
|
|
2336
2504
|
children: [
|
|
2337
|
-
/* @__PURE__ */
|
|
2505
|
+
/* @__PURE__ */ jsxs4(
|
|
2338
2506
|
"button",
|
|
2339
2507
|
{
|
|
2340
2508
|
type: "button",
|
|
2341
2509
|
className: "isotone-note-hit",
|
|
2342
2510
|
"aria-current": editing?.id === note.id ? true : void 0,
|
|
2343
2511
|
onClick: () => beginEdit(note),
|
|
2344
|
-
children:
|
|
2512
|
+
children: [
|
|
2513
|
+
working.has(note.id) && /* @__PURE__ */ jsx5("span", { className: "isotone-working", ref: dotPulse, role: "img", "aria-label": "An agent is working on this", title: "An agent is working on this" }),
|
|
2514
|
+
/* @__PURE__ */ jsx5(NoteSummary, { note, page: note.route !== route ? note.route : void 0 })
|
|
2515
|
+
]
|
|
2345
2516
|
}
|
|
2346
2517
|
),
|
|
2347
2518
|
/* @__PURE__ */ jsx5(
|
|
@@ -2498,11 +2669,12 @@ function IterationPanel({
|
|
|
2498
2669
|
"data-tools": Number(Boolean(variants)) + Number(notesAvailable),
|
|
2499
2670
|
"data-instant": instant,
|
|
2500
2671
|
"data-notes-open": notesOpen,
|
|
2672
|
+
"data-working": working.size > 0,
|
|
2501
2673
|
"data-open": open,
|
|
2502
2674
|
className: "isotone-root",
|
|
2503
2675
|
children: [
|
|
2504
2676
|
/* @__PURE__ */ jsx5("div", { className: "isotone-rail-track", "aria-hidden": "true" }),
|
|
2505
|
-
picking && /* @__PURE__ */ jsxs4("div", { className: "isotone-hint", "data-isotone": true, children: [
|
|
2677
|
+
picking && /* @__PURE__ */ jsxs4("div", { ref: hintRef, className: "isotone-hint", "data-isotone": true, "data-away": hintAway, children: [
|
|
2506
2678
|
/* @__PURE__ */ jsxs4("span", { children: [
|
|
2507
2679
|
targets.length,
|
|
2508
2680
|
" selected \xB7 click to add or remove \xB7 hold \u21E7 to use the page"
|
|
@@ -2634,7 +2806,7 @@ function IterationPanel({
|
|
|
2634
2806
|
"aria-hidden": railOpen,
|
|
2635
2807
|
tabIndex: railOpen ? -1 : 0,
|
|
2636
2808
|
"aria-label": notesAvailable ? "Isotone" : "Variants",
|
|
2637
|
-
title: `Isotone${shortcutLabel ? ` \xB7 ${shortcutLabel}` : ""}`,
|
|
2809
|
+
title: `Isotone${working.size ? ` \xB7 ${working.size} in progress` : ""}${shortcutLabel ? ` \xB7 ${shortcutLabel}` : ""}`,
|
|
2638
2810
|
"aria-expanded": railOpen,
|
|
2639
2811
|
onClick: () => {
|
|
2640
2812
|
if (open) closeAll();
|
|
@@ -2642,6 +2814,7 @@ function IterationPanel({
|
|
|
2642
2814
|
else openInitial();
|
|
2643
2815
|
},
|
|
2644
2816
|
children: [
|
|
2817
|
+
working.size > 0 && /* @__PURE__ */ jsx5("span", { className: "isotone-ring", ref: ringPulse }),
|
|
2645
2818
|
/* @__PURE__ */ jsx5(Icon2, { name: "iterate" }),
|
|
2646
2819
|
visibleNotes.length > 0 && /* @__PURE__ */ jsx5("span", { className: "isotone-badge", children: visibleNotes.length })
|
|
2647
2820
|
]
|
|
@@ -2654,6 +2827,9 @@ function IterationPanel({
|
|
|
2654
2827
|
}
|
|
2655
2828
|
);
|
|
2656
2829
|
}
|
|
2830
|
+
function PanelClose({ label, onClose }) {
|
|
2831
|
+
return /* @__PURE__ */ jsx5("button", { type: "button", className: "isotone-panel-close", "aria-label": label, title: label, onClick: onClose, children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 }) });
|
|
2832
|
+
}
|
|
2657
2833
|
|
|
2658
2834
|
// src/react/IterationMount.tsx
|
|
2659
2835
|
import { createRoot } from "react-dom/client";
|