@wildwinter/app-shell 0.14.0 → 0.16.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/dist/anchored.css +54 -0
- package/dist/anchored.css.map +1 -1
- package/dist/chunk-YLINCOQD.js +39 -0
- package/dist/chunk-YLINCOQD.js.map +1 -0
- package/dist/comments.css +25 -0
- package/dist/comments.css.map +1 -1
- package/dist/index.cjs +109 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -3
- package/dist/index.d.ts +61 -3
- package/dist/index.js +108 -17
- package/dist/index.js.map +1 -1
- package/dist/menu.cjs +26 -0
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +56 -1
- package/dist/menu.d.ts +56 -1
- package/dist/menu.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-R6RO5TXB.js +0 -16
- package/dist/chunk-R6RO5TXB.js.map +0 -1
package/dist/anchored.css
CHANGED
|
@@ -47,4 +47,58 @@
|
|
|
47
47
|
gap: 8px;
|
|
48
48
|
padding: 10px;
|
|
49
49
|
}
|
|
50
|
+
.shell-id-editor .shell-anchored-body {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 8px;
|
|
54
|
+
}
|
|
55
|
+
.shell-id-input {
|
|
56
|
+
font: 0.82rem var(--font-mono, monospace);
|
|
57
|
+
padding: 5px 8px;
|
|
58
|
+
border: 1px solid var(--line);
|
|
59
|
+
border-radius: var(--radius-sm);
|
|
60
|
+
background: var(--card);
|
|
61
|
+
color: var(--ink);
|
|
62
|
+
}
|
|
63
|
+
.shell-id-hint {
|
|
64
|
+
font: 0.74rem/1.4 var(--font-ui);
|
|
65
|
+
color: var(--muted);
|
|
66
|
+
}
|
|
67
|
+
.shell-id-hint.bad {
|
|
68
|
+
color: var(--danger, #b00);
|
|
69
|
+
}
|
|
70
|
+
.shell-id-actions {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: space-between;
|
|
74
|
+
gap: 8px;
|
|
75
|
+
}
|
|
76
|
+
.shell-id-reset {
|
|
77
|
+
font: 0.74rem var(--font-ui);
|
|
78
|
+
padding: 3px 10px;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
background: none;
|
|
81
|
+
border: 1px dashed var(--line);
|
|
82
|
+
border-radius: var(--radius-sm);
|
|
83
|
+
color: var(--muted);
|
|
84
|
+
}
|
|
85
|
+
.shell-id-reset:hover {
|
|
86
|
+
border-color: var(--accent);
|
|
87
|
+
color: var(--accent);
|
|
88
|
+
}
|
|
89
|
+
.shell-id-set {
|
|
90
|
+
font: 0.78rem var(--font-ui);
|
|
91
|
+
padding: 3px 12px;
|
|
92
|
+
cursor: pointer;
|
|
93
|
+
border: 1px solid var(--accent);
|
|
94
|
+
border-radius: var(--radius-sm);
|
|
95
|
+
background: var(--accent);
|
|
96
|
+
color: var(--surface);
|
|
97
|
+
}
|
|
98
|
+
.shell-id-set:disabled {
|
|
99
|
+
cursor: default;
|
|
100
|
+
background: none;
|
|
101
|
+
color: var(--muted);
|
|
102
|
+
border-color: var(--line);
|
|
103
|
+
}
|
|
50
104
|
/*# sourceMappingURL=anchored.css.map */
|
package/dist/anchored.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/anchored.css"],"sourcesContent":["/* The anchored panel (anchored.ts): a floating panel hung off the thing it is\n about. Sized and positioned in script (the placement has to measure); this is\n only what it looks like. Its body scrolls, because the placement caps the\n height rather than letting a long thread run off the screen. */\n.shell-anchored {\n position: fixed; z-index: 40;\n display: flex; flex-direction: column;\n border: 1px solid var(--line);\n border-radius: var(--radius-md);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-2, var(--shadow-1));\n overflow: hidden;\n}\n.shell-anchored-head {\n flex: none;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line-soft);\n}\n.shell-anchored-title {\n font: 600 0.72rem/1 var(--font-ui);\n text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted);\n}\n.shell-anchored-close {\n border: 0; background: none; color: var(--muted);\n font-size: 0.8rem; line-height: 1; padding: 0 2px; cursor: pointer;\n}\n.shell-anchored-close:hover { color: var(--accent); }\n.shell-anchored-body {\n flex: 1; min-height: 0; overflow-y: auto;\n display: flex; flex-direction: column; gap: 8px;\n padding: 10px;\n}\n"],"mappings":";AAIA,CAAC;AACC,YAAU;AAAO,WAAS;AAC1B,WAAS;AAAM,kBAAgB;AAC/B,UAAQ,IAAI,MAAM,IAAI;AACtB,iBAAe,IAAI;AACnB,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI,UAAU,EAAE,IAAI;AAChC,YAAU;AACZ;AACA,CAAC;AACC,QAAM;AACN,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AACzE,WAAS,IAAI;AACb,iBAAe,IAAI,MAAM,IAAI;AAC/B;AACA,CAAC;AACC,QAAM,IAAI,OAAO,CAAC,EAAE,IAAI;AACxB,kBAAgB;AAAW,kBAAgB;AAAQ,SAAO,IAAI;AAChE;AACA,CAAC;AACC,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AACxC,aAAW;AAAQ,eAAa;AAAG,WAAS,EAAE;AAAK,UAAQ;AAC7D;AACA,CAJC,oBAIoB;AAAS,SAAO,IAAI;AAAW;AACpD,CAAC;AACC,QAAM;AAAG,cAAY;AAAG,cAAY;AACpC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,WAAS;AACX;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/anchored.css"],"sourcesContent":["/* The anchored panel (anchored.ts): a floating panel hung off the thing it is\n about. Sized and positioned in script (the placement has to measure); this is\n only what it looks like. Its body scrolls, because the placement caps the\n height rather than letting a long thread run off the screen. */\n.shell-anchored {\n position: fixed; z-index: 40;\n display: flex; flex-direction: column;\n border: 1px solid var(--line);\n border-radius: var(--radius-md);\n background: var(--surface); color: var(--ink);\n box-shadow: var(--shadow-2, var(--shadow-1));\n overflow: hidden;\n}\n.shell-anchored-head {\n flex: none;\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--line-soft);\n}\n.shell-anchored-title {\n font: 600 0.72rem/1 var(--font-ui);\n text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted);\n}\n.shell-anchored-close {\n border: 0; background: none; color: var(--muted);\n font-size: 0.8rem; line-height: 1; padding: 0 2px; cursor: pointer;\n}\n.shell-anchored-close:hover { color: var(--accent); }\n.shell-anchored-body {\n flex: 1; min-height: 0; overflow-y: auto;\n display: flex; flex-direction: column; gap: 8px;\n padding: 10px;\n}\n\n/* The gameId editor (id-editor.ts). Refusal is the whole point of the design, so\n it has to LOOK refused: the hint reddens and Set goes flat. */\n.shell-id-editor .shell-anchored-body { display: flex; flex-direction: column; gap: 8px; }\n.shell-id-input {\n font: 0.82rem var(--font-mono, monospace);\n padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n}\n.shell-id-hint { font: 0.74rem/1.4 var(--font-ui); color: var(--muted); }\n.shell-id-hint.bad { color: var(--danger, #b00); }\n.shell-id-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; }\n.shell-id-reset {\n font: 0.74rem var(--font-ui); padding: 3px 10px; cursor: pointer;\n background: none; border: 1px dashed var(--line); border-radius: var(--radius-sm); color: var(--muted);\n}\n.shell-id-reset:hover { border-color: var(--accent); color: var(--accent); }\n.shell-id-set {\n font: 0.78rem var(--font-ui); padding: 3px 12px; cursor: pointer;\n border: 1px solid var(--accent); border-radius: var(--radius-sm);\n background: var(--accent); color: var(--surface);\n}\n.shell-id-set:disabled {\n cursor: default; background: none; color: var(--muted); border-color: var(--line);\n}\n"],"mappings":";AAIA,CAAC;AACC,YAAU;AAAO,WAAS;AAC1B,WAAS;AAAM,kBAAgB;AAC/B,UAAQ,IAAI,MAAM,IAAI;AACtB,iBAAe,IAAI;AACnB,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,cAAY,IAAI,UAAU,EAAE,IAAI;AAChC,YAAU;AACZ;AACA,CAAC;AACC,QAAM;AACN,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AACzE,WAAS,IAAI;AACb,iBAAe,IAAI,MAAM,IAAI;AAC/B;AACA,CAAC;AACC,QAAM,IAAI,OAAO,CAAC,EAAE,IAAI;AACxB,kBAAgB;AAAW,kBAAgB;AAAQ,SAAO,IAAI;AAChE;AACA,CAAC;AACC,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AACxC,aAAW;AAAQ,eAAa;AAAG,WAAS,EAAE;AAAK,UAAQ;AAC7D;AACA,CAJC,oBAIoB;AAAS,SAAO,IAAI;AAAW;AACpD,CAAC;AACC,QAAM;AAAG,cAAY;AAAG,cAAY;AACpC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAC5C,WAAS;AACX;AAIA,CAAC,gBAAgB,CARhB;AAQuC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACzF,CAAC;AACC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,WAAS,IAAI;AAAK,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AACpE,cAAY,IAAI;AAAS,SAAO,IAAI;AACtC;AACA,CAAC;AAAgB,QAAM,OAAO,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACxE,CADC,aACa,CAAC;AAAM,SAAO,IAAI,QAAQ,EAAE;AAAO;AACjD,CAAC;AAAmB,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAe,OAAK;AAAK;AAClG,CAAC;AACC,QAAM,QAAQ,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACzD,cAAY;AAAM,UAAQ,IAAI,OAAO,IAAI;AAAS,iBAAe,IAAI;AAAc,SAAO,IAAI;AAChG;AACA,CAJC,cAIc;AAAS,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAW;AAC3E,CAAC;AACC,QAAM,QAAQ,IAAI;AAAY,WAAS,IAAI;AAAM,UAAQ;AACzD,UAAQ,IAAI,MAAM,IAAI;AAAW,iBAAe,IAAI;AACpD,cAAY,IAAI;AAAW,SAAO,IAAI;AACxC;AACA,CALC,YAKY;AACX,UAAQ;AAAS,cAAY;AAAM,SAAO,IAAI;AAAU,gBAAc,IAAI;AAC5E;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/menu.ts
|
|
2
|
+
var EDIT_MENU = {
|
|
3
|
+
undo: { label: "Undo", accelerator: "CmdOrCtrl+Z" },
|
|
4
|
+
redo: { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z" },
|
|
5
|
+
duplicate: { label: "Duplicate", accelerator: "CmdOrCtrl+D" },
|
|
6
|
+
find: { label: "Find\u2026", accelerator: "CmdOrCtrl+F" }
|
|
7
|
+
};
|
|
8
|
+
var PANE_MENU = {
|
|
9
|
+
showNav: { label: "Show Navigator", accelerator: "CmdOrCtrl+1" },
|
|
10
|
+
showInspector: { label: "Show Inspector", accelerator: "CmdOrCtrl+2" },
|
|
11
|
+
resetView: { label: "Reset View" }
|
|
12
|
+
};
|
|
13
|
+
var HELP_MENU = {
|
|
14
|
+
checkForUpdates: { label: "Check for Updates\u2026" }
|
|
15
|
+
};
|
|
16
|
+
var APP_MENU = {
|
|
17
|
+
/** Name + optional email, used to sign edits and comments. One label across the suite. */
|
|
18
|
+
userInfo: { label: "User Information\u2026" }
|
|
19
|
+
};
|
|
20
|
+
function namedMenuItems(naming) {
|
|
21
|
+
const suite = naming.suiteName ?? "Patter";
|
|
22
|
+
return {
|
|
23
|
+
about: { label: `About ${naming.appName}`, ready: true },
|
|
24
|
+
docs: {
|
|
25
|
+
label: `${naming.appName} Documentation`,
|
|
26
|
+
...naming.docsUrl ? { url: naming.docsUrl } : {},
|
|
27
|
+
ready: naming.docsUrl !== void 0
|
|
28
|
+
},
|
|
29
|
+
suiteDocs: {
|
|
30
|
+
label: `${suite} Documentation Home`,
|
|
31
|
+
...naming.suiteDocsUrl ? { url: naming.suiteDocsUrl } : {},
|
|
32
|
+
ready: naming.suiteDocsUrl !== void 0
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { APP_MENU, EDIT_MENU, HELP_MENU, PANE_MENU, namedMenuItems };
|
|
38
|
+
//# sourceMappingURL=chunk-YLINCOQD.js.map
|
|
39
|
+
//# sourceMappingURL=chunk-YLINCOQD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/menu.ts"],"names":[],"mappings":";AA2BO,IAAM,SAAA,GAKT;AAAA,EACF,IAAA,EAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,aAAa,aAAA,EAAc;AAAA,EAClD,IAAA,EAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,aAAa,mBAAA,EAAoB;AAAA,EACxD,SAAA,EAAW,EAAE,KAAA,EAAO,WAAA,EAAa,aAAa,aAAA,EAAc;AAAA,EAC5D,IAAA,EAAM,EAAE,KAAA,EAAO,YAAA,EAAS,aAAa,aAAA;AACvC;AAGO,IAAM,SAAA,GAIT;AAAA,EACF,OAAA,EAAS,EAAE,KAAA,EAAO,gBAAA,EAAkB,aAAa,aAAA,EAAc;AAAA,EAC/D,aAAA,EAAe,EAAE,KAAA,EAAO,gBAAA,EAAkB,aAAa,aAAA,EAAc;AAAA,EACrE,SAAA,EAAW,EAAE,KAAA,EAAO,YAAA;AACtB;AAkBO,IAAM,SAAA,GAET;AAAA,EACF,eAAA,EAAiB,EAAE,KAAA,EAAO,yBAAA;AAC5B;AAGO,IAAM,QAAA,GAET;AAAA;AAAA,EAEF,QAAA,EAAU,EAAE,KAAA,EAAO,wBAAA;AACrB;AA+BO,SAAS,eAAe,MAAA,EAI7B;AACA,EAAA,MAAM,KAAA,GAAQ,OAAO,SAAA,IAAa,QAAA;AAClC,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,EAAE,KAAA,EAAO,CAAA,MAAA,EAAS,OAAO,OAAO,CAAA,CAAA,EAAI,OAAO,IAAA,EAAK;AAAA,IACvD,IAAA,EAAM;AAAA,MACJ,KAAA,EAAO,CAAA,EAAG,MAAA,CAAO,OAAO,CAAA,cAAA,CAAA;AAAA,MACxB,GAAI,OAAO,OAAA,GAAU,EAAE,KAAK,MAAA,CAAO,OAAA,KAAY,EAAC;AAAA,MAChD,KAAA,EAAO,OAAO,OAAA,KAAY;AAAA,KAC5B;AAAA,IACA,SAAA,EAAW;AAAA,MACT,KAAA,EAAO,GAAG,KAAK,CAAA,mBAAA,CAAA;AAAA,MACf,GAAI,OAAO,YAAA,GAAe,EAAE,KAAK,MAAA,CAAO,YAAA,KAAiB,EAAC;AAAA,MAC1D,KAAA,EAAO,OAAO,YAAA,KAAiB;AAAA;AACjC,GACF;AACF","file":"chunk-YLINCOQD.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Shell-owned menu naming: the labels + accelerators for the pane-frame menu\n// items, single-sourced so every app in the family names them identically\n// (and a rename here flows to all of them). Pure data - no electron, no DOM -\n// so a main-process menu builder can import it. Each app builds its own\n// MenuItem from these and wires the click to its shell controller.\n// ---------------------------------------------------------------------------\n\nexport interface MenuLabel {\n label: string;\n /** Electron accelerator string, when the item has one. */\n accelerator?: string;\n}\n\n/**\n * The Edit menu items the family shares.\n *\n * Undo and Redo are here and the MECHANISM is not, which is the whole finding\n * about undo: Patterpad's is ProseMirror's document history, Storyletter's is a\n * replay of file bytes, and those have nothing in common but this. See\n * `design/shared-shell.md`.\n *\n * The rule both apps discovered separately, and the reason these are spelled\n * out rather than left as Electron's `role: \"undo\"`: the native role runs a DOM\n * text-field undo, which knows nothing about either app's model and will\n * silently undo the wrong thing.\n */\nexport const EDIT_MENU: {\n undo: MenuLabel;\n redo: MenuLabel;\n duplicate: MenuLabel;\n find: MenuLabel;\n} = {\n undo: { label: \"Undo\", accelerator: \"CmdOrCtrl+Z\" },\n redo: { label: \"Redo\", accelerator: \"Shift+CmdOrCtrl+Z\" },\n duplicate: { label: \"Duplicate\", accelerator: \"CmdOrCtrl+D\" },\n find: { label: \"Find…\", accelerator: \"CmdOrCtrl+F\" },\n};\n\n/** The pane-frame menu items (a View menu, conventionally). */\nexport const PANE_MENU: {\n showNav: MenuLabel;\n showInspector: MenuLabel;\n resetView: MenuLabel;\n} = {\n showNav: { label: \"Show Navigator\", accelerator: \"CmdOrCtrl+1\" },\n showInspector: { label: \"Show Inspector\", accelerator: \"CmdOrCtrl+2\" },\n resetView: { label: \"Reset View\" },\n};\n\n/**\n * The Help menu items the family shares, and the one platform rule that goes\n * with them.\n *\n * Lifted from Patterpad, which is the only app that has ever had these: a\n * second app writing its own updater or About is pure waste (`design/\n * shared-shell.md`), and a suite is recognised by finding Check for Updates\n * where you left it. Storyletter will grow both; until it does, these labels\n * are the agreement rather than a description of two implementations.\n *\n * PLACEMENT, which differs by platform and is easy to get wrong:\n * - macOS keeps About in the APP menu (the one named after the product), and\n * Help carries documentation + Check for Updates only.\n * - Windows and Linux have no app menu, so About goes at the foot of Help,\n * after a separator. That is the conventional home there.\n */\nexport const HELP_MENU: {\n checkForUpdates: MenuLabel;\n} = {\n checkForUpdates: { label: \"Check for Updates\\u2026\" },\n};\n\n/** The app-menu items the family shares (macOS; the identity item also belongs in File elsewhere). */\nexport const APP_MENU: {\n userInfo: MenuLabel;\n} = {\n /** Name + optional email, used to sign edits and comments. One label across the suite. */\n userInfo: { label: \"User Information\\u2026\" },\n};\n\n/** What an app must tell the shell to build its named menu items. */\nexport interface MenuNaming {\n /** The product, as it appears in menus: \"Patterpad\", \"Storyletter\". */\n appName: string;\n /** The product's own documentation URL. Omit while an app has no docs yet and\n * the item is rendered disabled rather than opening a broken link. */\n docsUrl?: string;\n /** The suite's documentation home, shared by every app in the family. */\n suiteDocsUrl?: string;\n /** The suite, as it appears in \"<suite> Documentation Home\". Defaults to \"Patter\". */\n suiteName?: string;\n}\n\n/** A named menu item: the label, plus the URL it opens (absent = nothing wired yet). */\nexport interface NamedMenuItem extends MenuLabel {\n url?: string;\n /** False when the app has not supplied a URL, so the builder can render it\n * disabled instead of opening nothing. A placeholder, honestly labelled. */\n ready: boolean;\n}\n\n/**\n * The menu labels that carry a product or suite name, so no app spells its own\n * About or Documentation item differently from the rest of the family.\n *\n * An app with no documentation site yet still gets the ITEM, marked\n * `ready: false`, so the menu has its family shape from day one and wiring the\n * URL later is a one-line change rather than a menu redesign.\n */\nexport function namedMenuItems(naming: MenuNaming): {\n about: NamedMenuItem;\n docs: NamedMenuItem;\n suiteDocs: NamedMenuItem;\n} {\n const suite = naming.suiteName ?? \"Patter\";\n return {\n about: { label: `About ${naming.appName}`, ready: true },\n docs: {\n label: `${naming.appName} Documentation`,\n ...(naming.docsUrl ? { url: naming.docsUrl } : {}),\n ready: naming.docsUrl !== undefined,\n },\n suiteDocs: {\n label: `${suite} Documentation Home`,\n ...(naming.suiteDocsUrl ? { url: naming.suiteDocsUrl } : {}),\n ready: naming.suiteDocsUrl !== undefined,\n },\n };\n}\n"]}
|
package/dist/comments.css
CHANGED
|
@@ -12,6 +12,31 @@
|
|
|
12
12
|
gap: 8px;
|
|
13
13
|
align-items: baseline;
|
|
14
14
|
}
|
|
15
|
+
.shell-cmt-del {
|
|
16
|
+
margin-left: auto;
|
|
17
|
+
flex: none;
|
|
18
|
+
padding: 0 4px;
|
|
19
|
+
line-height: 1;
|
|
20
|
+
background: none;
|
|
21
|
+
border: none;
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
font-size: 0.72rem;
|
|
24
|
+
color: var(--muted);
|
|
25
|
+
opacity: 0.55;
|
|
26
|
+
transition: opacity var(--dur-quick, 120ms) var(--ease-standard, ease), color var(--dur-quick, 120ms) var(--ease-standard, ease);
|
|
27
|
+
}
|
|
28
|
+
.shell-cmt-msg:hover .shell-cmt-del,
|
|
29
|
+
.shell-cmt-del:focus-visible {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
}
|
|
32
|
+
.shell-cmt-del:hover {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
color: var(--danger, #b00);
|
|
35
|
+
}
|
|
36
|
+
.shell-cmt-msg.gone .shell-cmt-body {
|
|
37
|
+
font-style: italic;
|
|
38
|
+
color: var(--muted);
|
|
39
|
+
}
|
|
15
40
|
.shell-cmt-author {
|
|
16
41
|
font: 600 0.76rem/1.2 var(--font-ui);
|
|
17
42
|
}
|
package/dist/comments.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/comments.css"],"sourcesContent":["/* A comment thread inside the anchored panel (comments.ts). */\n.shell-cmt-list { display: flex; flex-direction: column; gap: 8px; }\n.shell-cmt-list.resolved { opacity: 0.55; }\n.shell-cmt-msg-head { display: flex; gap: 8px; align-items: baseline; }\n.shell-cmt-author { font: 600 0.76rem/1.2 var(--font-ui); }\n.shell-cmt-ts { font: 0.68rem/1.2 var(--font-ui); color: var(--muted); }\n.shell-cmt-body { font: 0.82rem/1.45 var(--font-read, var(--font-ui)); white-space: pre-wrap; }\n.shell-cmt-input {\n width: 100%; box-sizing: border-box; resize: vertical;\n border: 1px solid var(--line-soft); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n font: 0.82rem/1.4 var(--font-read, var(--font-ui)); padding: 6px 8px;\n}\n.shell-cmt-input:focus { outline: none; border-color: var(--accent); }\n.shell-cmt-actions { display: flex; gap: 6px; justify-content: flex-end; }\n.shell-cmt-btn {\n font: 0.76rem/1 var(--font-ui); padding: 5px 10px;\n border-radius: var(--radius-sm); border: 1px solid var(--line);\n background: var(--card); color: var(--ink); cursor: pointer;\n}\n.shell-cmt-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent, #fff); }\n/* The picker, when one thing has more than one thread. */\n.shell-cmt-row {\n display: flex; gap: 6px; align-items: baseline; width: 100%; text-align: left;\n border: 0; background: none; color: var(--ink); cursor: pointer;\n padding: 6px 4px; border-radius: var(--radius-sm);\n}\n.shell-cmt-row:hover { background: var(--accent-soft); }\n.shell-cmt-row.resolved { opacity: 0.55; }\n.shell-cmt-row-author { font: 600 0.74rem/1.2 var(--font-ui); flex: none; }\n.shell-cmt-row-gist {\n font: 0.78rem/1.3 var(--font-read, var(--font-ui)); color: var(--muted);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.shell-cmt-row-count { margin-left: auto; font: 0.7rem/1 var(--font-ui); color: var(--muted); }\n.shell-cmt-new {\n align-self: flex-start; font: 0.76rem/1 var(--font-ui);\n border: 1px dashed var(--line); background: none; color: var(--muted);\n border-radius: var(--radius-sm); padding: 5px 10px; cursor: pointer;\n}\n.shell-cmt-new:hover { color: var(--accent); border-color: var(--accent); }\n"],"mappings":";AACA,CAAC;AAAiB,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACnE,CADC,cACc,CAAC;AAAW,WAAS;AAAM;AAC1C,CAAC;AAAqB,WAAS;AAAM,OAAK;AAAK,eAAa;AAAU;
|
|
1
|
+
{"version":3,"sources":["../src/comments.css"],"sourcesContent":["/* A comment thread inside the anchored panel (comments.ts). */\n.shell-cmt-list { display: flex; flex-direction: column; gap: 8px; }\n.shell-cmt-list.resolved { opacity: 0.55; }\n.shell-cmt-msg-head { display: flex; gap: 8px; align-items: baseline; }\n/* The delete: QUIET but always there.\n It was rollover-revealed at first, following the grip grammar the lists use,\n and that was the wrong borrowing: a list row hides its grip because a dense\n column of them would be clutter, whereas this popover holds two or three\n messages and is already a surface you opened on purpose. Hidden, the only\n way to delete a comment was to discover that hovering one exact row revealed\n something - which is to say there was no way to delete a comment. Reported as\n \"there is apparently no delete option\", which is exactly right.\n Low contrast carries \"secondary\"; invisibility carries \"absent\". */\n.shell-cmt-del {\n margin-left: auto; flex: none; padding: 0 4px; line-height: 1;\n background: none; border: none; cursor: pointer;\n font-size: 0.72rem; color: var(--muted); opacity: 0.55;\n transition: opacity var(--dur-quick, 120ms) var(--ease-standard, ease),\n color var(--dur-quick, 120ms) var(--ease-standard, ease);\n}\n.shell-cmt-msg:hover .shell-cmt-del, .shell-cmt-del:focus-visible { opacity: 1; }\n.shell-cmt-del:hover { opacity: 1; color: var(--danger, #b00); }\n/* A withdrawn message keeps its place in the thread and stops asking to be read. */\n.shell-cmt-msg.gone .shell-cmt-body { font-style: italic; color: var(--muted); }\n.shell-cmt-author { font: 600 0.76rem/1.2 var(--font-ui); }\n.shell-cmt-ts { font: 0.68rem/1.2 var(--font-ui); color: var(--muted); }\n.shell-cmt-body { font: 0.82rem/1.45 var(--font-read, var(--font-ui)); white-space: pre-wrap; }\n.shell-cmt-input {\n width: 100%; box-sizing: border-box; resize: vertical;\n border: 1px solid var(--line-soft); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n font: 0.82rem/1.4 var(--font-read, var(--font-ui)); padding: 6px 8px;\n}\n.shell-cmt-input:focus { outline: none; border-color: var(--accent); }\n.shell-cmt-actions { display: flex; gap: 6px; justify-content: flex-end; }\n.shell-cmt-btn {\n font: 0.76rem/1 var(--font-ui); padding: 5px 10px;\n border-radius: var(--radius-sm); border: 1px solid var(--line);\n background: var(--card); color: var(--ink); cursor: pointer;\n}\n.shell-cmt-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent, #fff); }\n/* The picker, when one thing has more than one thread. */\n.shell-cmt-row {\n display: flex; gap: 6px; align-items: baseline; width: 100%; text-align: left;\n border: 0; background: none; color: var(--ink); cursor: pointer;\n padding: 6px 4px; border-radius: var(--radius-sm);\n}\n.shell-cmt-row:hover { background: var(--accent-soft); }\n.shell-cmt-row.resolved { opacity: 0.55; }\n.shell-cmt-row-author { font: 600 0.74rem/1.2 var(--font-ui); flex: none; }\n.shell-cmt-row-gist {\n font: 0.78rem/1.3 var(--font-read, var(--font-ui)); color: var(--muted);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.shell-cmt-row-count { margin-left: auto; font: 0.7rem/1 var(--font-ui); color: var(--muted); }\n.shell-cmt-new {\n align-self: flex-start; font: 0.76rem/1 var(--font-ui);\n border: 1px dashed var(--line); background: none; color: var(--muted);\n border-radius: var(--radius-sm); padding: 5px 10px; cursor: pointer;\n}\n.shell-cmt-new:hover { color: var(--accent); border-color: var(--accent); }\n"],"mappings":";AACA,CAAC;AAAiB,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACnE,CADC,cACc,CAAC;AAAW,WAAS;AAAM;AAC1C,CAAC;AAAqB,WAAS;AAAM,OAAK;AAAK,eAAa;AAAU;AAUtE,CAAC;AACC,eAAa;AAAM,QAAM;AAAM,WAAS,EAAE;AAAK,eAAa;AAC5D,cAAY;AAAM,UAAQ;AAAM,UAAQ;AACxC,aAAW;AAAS,SAAO,IAAI;AAAU,WAAS;AAClD,cAAY,QAAQ,IAAI,WAAW,EAAE,OAAO,IAAI,eAAe,EAAE,KAAK,EACpE,MAAM,IAAI,WAAW,EAAE,OAAO,IAAI,eAAe,EAAE;AACvD;AACA,CAAC,aAAa,OAAO,CAPpB;AAOoC,CAPpC,aAOkD;AAAiB,WAAS;AAAG;AAChF,CARC,aAQa;AAAS,WAAS;AAAG,SAAO,IAAI,QAAQ,EAAE;AAAO;AAE/D,CAHC,aAGa,CAAC,KAAK,CAAC;AAAiB,cAAY;AAAQ,SAAO,IAAI;AAAU;AAC/E,CAAC;AAAmB,QAAM,IAAI,OAAO,CAAC,IAAI,IAAI;AAAY;AAC1D,CAAC;AAAe,QAAM,OAAO,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACvE,CAHqB;AAGH,QAAM,OAAO,CAAC,KAAK,IAAI,WAAW,EAAE,IAAI;AAAa,eAAa;AAAU;AAC9F,CAAC;AACC,SAAO;AAAM,cAAY;AAAY,UAAQ;AAC7C,UAAQ,IAAI,MAAM,IAAI;AAAc,iBAAe,IAAI;AACvD,cAAY,IAAI;AAAS,SAAO,IAAI;AACpC,QAAM,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,IAAI;AAAa,WAAS,IAAI;AACnE;AACA,CANC,eAMe;AAAS,WAAS;AAAM,gBAAc,IAAI;AAAW;AACrE,CAAC;AAAoB,WAAS;AAAM,OAAK;AAAK,mBAAiB;AAAU;AACzE,CAAC;AACC,QAAM,OAAO,CAAC,EAAE,IAAI;AAAY,WAAS,IAAI;AAC7C,iBAAe,IAAI;AAAc,UAAQ,IAAI,MAAM,IAAI;AACvD,cAAY,IAAI;AAAS,SAAO,IAAI;AAAQ,UAAQ;AACtD;AACA,CALC,aAKa,CAAC;AAAU,cAAY,IAAI;AAAW,gBAAc,IAAI;AAAW,SAAO,IAAI,WAAW,EAAE;AAAO;AAEhH,CAAC;AACC,WAAS;AAAM,OAAK;AAAK,eAAa;AAAU,SAAO;AAAM,cAAY;AACzE,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AAAQ,UAAQ;AACxD,WAAS,IAAI;AAAK,iBAAe,IAAI;AACvC;AACA,CALC,aAKa;AAAS,cAAY,IAAI;AAAgB;AACvD,CANC,aAMa,CA9CE;AA8CU,WAAS;AAAM;AACzC,CAAC;AAAuB,QAAM,IAAI,OAAO,CAAC,IAAI,IAAI;AAAY,QAAM;AAAM;AAC1E,CAAC;AACC,QAAM,OAAO,CAAC,IAAI,IAAI,WAAW,EAAE,IAAI;AAAa,SAAO,IAAI;AAC/D,YAAU;AAAQ,iBAAe;AAAU,eAAa;AAC1D;AACA,CAAC;AAAsB,eAAa;AAAM,QAAM,MAAM,CAAC,EAAE,IAAI;AAAY,SAAO,IAAI;AAAU;AAC9F,CAAC;AACC,cAAY;AAAY,QAAM,OAAO,CAAC,EAAE,IAAI;AAC5C,UAAQ,IAAI,OAAO,IAAI;AAAS,cAAY;AAAM,SAAO,IAAI;AAC7D,iBAAe,IAAI;AAAc,WAAS,IAAI;AAAM,UAAQ;AAC9D;AACA,CALC,aAKa;AAAS,SAAO,IAAI;AAAW,gBAAc,IAAI;AAAW;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -640,9 +640,14 @@ function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
|
640
640
|
const CAP = window.innerHeight * 0.7;
|
|
641
641
|
const MIN_H = 140;
|
|
642
642
|
panel.style.width = `${width}px`;
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
643
|
+
if (prefer === "centre") {
|
|
644
|
+
const centred = a.left + a.width / 2 - width / 2;
|
|
645
|
+
panel.style.left = `${Math.round(Math.min(Math.max(8, centred), window.innerWidth - width - 8))}px`;
|
|
646
|
+
} else {
|
|
647
|
+
const leftOf = a.left - width - GAP2;
|
|
648
|
+
const alignedRight = Math.min(a.right - width, window.innerWidth - width - 8);
|
|
649
|
+
panel.style.left = `${Math.round(prefer === "left" && leftOf >= 8 ? leftOf : Math.max(8, prefer === "left" ? a.left : alignedRight))}px`;
|
|
650
|
+
}
|
|
646
651
|
let bottomLimit = window.innerHeight - 8;
|
|
647
652
|
for (const selector of keepClear) {
|
|
648
653
|
const bar = document.querySelector(selector);
|
|
@@ -650,6 +655,16 @@ function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
|
650
655
|
const r = bar.getBoundingClientRect();
|
|
651
656
|
if (r.height > 0) bottomLimit = Math.min(bottomLimit, r.top - 6);
|
|
652
657
|
}
|
|
658
|
+
if (prefer === "centre") {
|
|
659
|
+
const height = Math.min(CAP, Math.max(MIN_H, panel.offsetHeight));
|
|
660
|
+
const mid = a.top + a.height / 2 - height / 2;
|
|
661
|
+
if (mid >= 8 && mid + height <= bottomLimit) {
|
|
662
|
+
panel.style.top = `${Math.round(mid)}px`;
|
|
663
|
+
panel.style.bottom = "auto";
|
|
664
|
+
panel.style.maxHeight = `${Math.round(CAP)}px`;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
653
668
|
const roomBelow = bottomLimit - (a.bottom + GAP2);
|
|
654
669
|
const roomAbove = a.top - GAP2 - 8;
|
|
655
670
|
if (roomAbove > roomBelow) {
|
|
@@ -663,6 +678,75 @@ function placeAnchored(panel, anchor, width, prefer, keepClear) {
|
|
|
663
678
|
}
|
|
664
679
|
}
|
|
665
680
|
|
|
681
|
+
// src/ids.ts
|
|
682
|
+
function gameIdify(text) {
|
|
683
|
+
return text.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
684
|
+
}
|
|
685
|
+
function isValidGameId(gameId) {
|
|
686
|
+
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/id-editor.ts
|
|
690
|
+
function openGameIdEditor(opts) {
|
|
691
|
+
const slugify = opts.slugify ?? gameIdify;
|
|
692
|
+
const valid = opts.validate ?? isValidGameId;
|
|
693
|
+
const panel = openAnchoredPanel({
|
|
694
|
+
anchor: opts.anchor,
|
|
695
|
+
className: "shell-id-editor",
|
|
696
|
+
title: opts.title ?? "Game ID",
|
|
697
|
+
width: 260,
|
|
698
|
+
...opts.prefer !== void 0 ? { prefer: opts.prefer } : {}
|
|
699
|
+
});
|
|
700
|
+
if (!panel) return false;
|
|
701
|
+
const input = el("input", "shell-id-input");
|
|
702
|
+
input.type = "text";
|
|
703
|
+
input.value = opts.value;
|
|
704
|
+
input.placeholder = opts.derived || "<game-id>";
|
|
705
|
+
input.spellcheck = false;
|
|
706
|
+
const hint = el("div", "shell-id-hint", "Lower case, digits, hyphens. Empty = auto from the name.");
|
|
707
|
+
const actions = el("div", "shell-id-actions");
|
|
708
|
+
const reset = el("button", "shell-id-reset", "Reset to auto");
|
|
709
|
+
const set = el("button", "shell-id-set", "Set");
|
|
710
|
+
reset.type = "button";
|
|
711
|
+
set.type = "button";
|
|
712
|
+
actions.append(reset, set);
|
|
713
|
+
panel.body.append(input, hint, actions);
|
|
714
|
+
const legal = () => {
|
|
715
|
+
const v = input.value.trim();
|
|
716
|
+
return v === "" || valid(v);
|
|
717
|
+
};
|
|
718
|
+
const sync = () => {
|
|
719
|
+
set.disabled = !legal();
|
|
720
|
+
hint.classList.toggle("bad", !legal());
|
|
721
|
+
};
|
|
722
|
+
const commit = () => {
|
|
723
|
+
if (!legal()) return;
|
|
724
|
+
opts.onCommit(input.value.trim());
|
|
725
|
+
panel.close();
|
|
726
|
+
};
|
|
727
|
+
input.addEventListener("input", sync);
|
|
728
|
+
input.addEventListener("keydown", (e) => {
|
|
729
|
+
e.stopPropagation();
|
|
730
|
+
if (e.key === "Enter") {
|
|
731
|
+
e.preventDefault();
|
|
732
|
+
commit();
|
|
733
|
+
}
|
|
734
|
+
if (e.key === "Tab" && input.value.trim() !== "") {
|
|
735
|
+
e.preventDefault();
|
|
736
|
+
input.value = slugify(input.value);
|
|
737
|
+
sync();
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
set.addEventListener("click", commit);
|
|
741
|
+
reset.addEventListener("click", () => {
|
|
742
|
+
opts.onCommit("");
|
|
743
|
+
panel.close();
|
|
744
|
+
});
|
|
745
|
+
sync();
|
|
746
|
+
setTimeout(() => input.focus(), 0);
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
|
|
666
750
|
// src/vc-view.ts
|
|
667
751
|
function foldVc(shards, keys) {
|
|
668
752
|
if (!keys) return void 0;
|
|
@@ -896,7 +980,7 @@ var stamp = (ts) => {
|
|
|
896
980
|
return when.toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
|
|
897
981
|
};
|
|
898
982
|
var gist = (thread) => {
|
|
899
|
-
const first = thread.messages
|
|
983
|
+
const first = thread.messages.find((m) => m.deleted !== true)?.body ?? "";
|
|
900
984
|
return first.length > 60 ? `${first.slice(0, 57)}\u2026` : first;
|
|
901
985
|
};
|
|
902
986
|
function openComments(opts) {
|
|
@@ -909,7 +993,7 @@ function openComments(opts) {
|
|
|
909
993
|
// Below its button (the topline), unlike Patterpad's inspector rows which
|
|
910
994
|
// want it to the left; and never over the problems bar, which an author may
|
|
911
995
|
// be stepping through while reading the thread.
|
|
912
|
-
prefer: "below",
|
|
996
|
+
prefer: opts.prefer ?? "below",
|
|
913
997
|
keepClear: [".problembar"]
|
|
914
998
|
});
|
|
915
999
|
if (!panel) return;
|
|
@@ -945,19 +1029,26 @@ function openComments(opts) {
|
|
|
945
1029
|
const thread = open;
|
|
946
1030
|
if (thread) {
|
|
947
1031
|
const list = el("div", { className: `shell-cmt-list${thread.resolved === true ? " resolved" : ""}` });
|
|
948
|
-
|
|
1032
|
+
thread.messages.forEach((message, index) => {
|
|
1033
|
+
const head = el(
|
|
1034
|
+
"div",
|
|
1035
|
+
{ className: "shell-cmt-msg-head" },
|
|
1036
|
+
el("span", { className: "shell-cmt-author", text: message.author === "" ? "Someone" : message.author }),
|
|
1037
|
+
el("span", { className: "shell-cmt-ts", text: stamp(message.ts) })
|
|
1038
|
+
);
|
|
1039
|
+
if (opts.deleteMessage && message.deleted !== true) {
|
|
1040
|
+
const bin = el("button", { className: "shell-cmt-del", text: "\u2715" });
|
|
1041
|
+
bin.title = "Delete this comment";
|
|
1042
|
+
bin.addEventListener("click", () => opts.deleteMessage?.(thread.id, index));
|
|
1043
|
+
head.append(bin);
|
|
1044
|
+
}
|
|
949
1045
|
list.append(el(
|
|
950
1046
|
"div",
|
|
951
|
-
{ className:
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
{ className: "shell-cmt-msg-head" },
|
|
955
|
-
el("span", { className: "shell-cmt-author", text: message.author === "" ? "Someone" : message.author }),
|
|
956
|
-
el("span", { className: "shell-cmt-ts", text: stamp(message.ts) })
|
|
957
|
-
),
|
|
958
|
-
el("div", { className: "shell-cmt-body", text: message.body })
|
|
1047
|
+
{ className: `shell-cmt-msg${message.deleted === true ? " gone" : ""}` },
|
|
1048
|
+
head,
|
|
1049
|
+
el("div", { className: "shell-cmt-body", text: message.deleted === true ? "Comment deleted" : message.body })
|
|
959
1050
|
));
|
|
960
|
-
}
|
|
1051
|
+
});
|
|
961
1052
|
body.append(list);
|
|
962
1053
|
}
|
|
963
1054
|
const input = el("textarea", { className: "shell-cmt-input" });
|
|
@@ -1304,11 +1395,13 @@ exports.el = el;
|
|
|
1304
1395
|
exports.expandableRow = expandableRow;
|
|
1305
1396
|
exports.focusNewRow = focusNewRow;
|
|
1306
1397
|
exports.foldVc = foldVc;
|
|
1398
|
+
exports.gameIdify = gameIdify;
|
|
1307
1399
|
exports.hideTip = hideTip;
|
|
1308
1400
|
exports.icon = icon;
|
|
1309
1401
|
exports.iconBtn = iconBtn;
|
|
1310
1402
|
exports.iconSvg = iconSvg;
|
|
1311
1403
|
exports.initTooltips = initTooltips;
|
|
1404
|
+
exports.isValidGameId = isValidGameId;
|
|
1312
1405
|
exports.labelled = labelled;
|
|
1313
1406
|
exports.lockControls = lockControls;
|
|
1314
1407
|
exports.lockNotice = lockNotice;
|
|
@@ -1318,6 +1411,7 @@ exports.mountSettingsDialog = mountSettingsDialog;
|
|
|
1318
1411
|
exports.moveItem = moveItem;
|
|
1319
1412
|
exports.openAnchoredPanel = openAnchoredPanel;
|
|
1320
1413
|
exports.openComments = openComments;
|
|
1414
|
+
exports.openGameIdEditor = openGameIdEditor;
|
|
1321
1415
|
exports.openNotesEditor = openNotesEditor;
|
|
1322
1416
|
exports.paintVcBadges = paintVcBadges;
|
|
1323
1417
|
exports.pinButton = pinButton;
|