@wildwinter/app-shell 0.14.0 → 0.15.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 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 */
@@ -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":[]}
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
  }
@@ -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;AACtE,CAAC;AAAmB,QAAM,IAAI,OAAO,CAAC,IAAI,IAAI;AAAY;AAC1D,CAAC;AAAe,QAAM,OAAO,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACvE,CAAC;AAAiB,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,CA1BE;AA0BU,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":[]}
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
- const leftOf = a.left - width - GAP2;
644
- const alignedRight = Math.min(a.right - width, window.innerWidth - width - 8);
645
- panel.style.left = `${Math.round(prefer === "left" && leftOf >= 8 ? leftOf : Math.max(8, prefer === "left" ? a.left : alignedRight))}px`;
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[0]?.body ?? "";
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
- for (const message of thread.messages) {
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: "shell-cmt-msg" },
952
- el(
953
- "div",
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;