@npmjsdnt/turn-reply 0.0.0-stage → 0.1.1
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/tr.css +89 -0
- package/dist/tr.js +962 -0
- package/dist/tr.min.js +13 -0
- package/package.json +19 -4
- package/README.md +0 -3
package/dist/tr.css
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.trw{
|
|
2
|
+
--c-bg:var(--surface-2);--c-sunken:var(--surface-1);
|
|
3
|
+
--c-hover:var(--fill-ghost-hover,var(--surface-1));--c-soft:var(--fill-ghost-selected,var(--surface-1));
|
|
4
|
+
--c-solid:var(--fill-primary,var(--text-primary));--c-on-solid:var(--on-primary,var(--surface-2));
|
|
5
|
+
--c-line:var(--border);--c-line-s:var(--border-strong);--c-line-x:var(--border-stronger,var(--border-strong));
|
|
6
|
+
--c-ink1:var(--text-primary);--c-ink2:var(--text-secondary);--c-ink3:var(--text-muted);--c-ink4:var(--text-disabled,var(--text-muted));
|
|
7
|
+
--c-attn:var(--text-warning);--c-ok:var(--text-success);--c-focus:var(--border-accent,var(--text-accent));--c-r:6px;
|
|
8
|
+
--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 5%,transparent);--c-lift:var(--surface-3,var(--surface-2));--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 4%,transparent);
|
|
9
|
+
container-type:inline-size;position:relative;box-sizing:border-box;
|
|
10
|
+
font-family:var(--font-sans);font-size:13px;line-height:20px;font-weight:400;
|
|
11
|
+
font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;
|
|
12
|
+
color:var(--c-ink1);background:transparent;border:0;border-radius:0;padding:0
|
|
13
|
+
}
|
|
14
|
+
.trw.dark{--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 15%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent)}
|
|
15
|
+
.trw *,.trw *::before,.trw *::after{box-sizing:border-box}
|
|
16
|
+
.trw [hidden]{display:none!important}
|
|
17
|
+
.trw .mono{font-family:var(--font-mono);font-size:12px}
|
|
18
|
+
.trw .grp{display:flex;align-items:flex-start;padding:6px 0;border-top:0.5px solid var(--c-line)}
|
|
19
|
+
.trw .bd>.grp:first-child{border-top:0}
|
|
20
|
+
.trw .gl{flex:0 0 60px;padding-top:6px;font-size:11px;line-height:16px;color:var(--c-ink2);white-space:nowrap;cursor:default}
|
|
21
|
+
.trw .rs{flex:1 1 auto;min-width:0}
|
|
22
|
+
.trw .it{margin:0 -6px;padding:0 6px;border-radius:0}
|
|
23
|
+
.trw .it:hover,.trw.kb .it:focus-within{background:var(--c-hover)}
|
|
24
|
+
.trw .ln{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;min-height:28px}
|
|
25
|
+
.trw .bd>.grp:first-child .it:first-child .ln{padding-right:40px}
|
|
26
|
+
.trw .q{flex:0 0 var(--qw,240px);min-width:0;display:flex;align-items:center;gap:6px;white-space:nowrap;overflow:hidden}
|
|
27
|
+
.trw.measuring .q{flex:none!important;width:max-content!important}
|
|
28
|
+
.trw .qt{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
29
|
+
.trw .n{flex:none;min-width:14px;color:var(--c-ink2)}
|
|
30
|
+
.trw .it.core .qt{font-weight:500}
|
|
31
|
+
.trw .q.info .qt{font-size:12px;color:var(--c-ink2)}
|
|
32
|
+
.trw a.tk{font-family:var(--font-mono);font-size:12px;color:var(--c-ink1);text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:2px}
|
|
33
|
+
.trw .ctl{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;padding:0 4px;margin:0 -4px}
|
|
34
|
+
.trw .cb{flex:none;width:9px;height:9px;background:var(--c-attn);clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
|
|
35
|
+
.trw .seg{display:inline-flex;align-items:center;gap:2px;height:26px;padding:2px;border:0;border-radius:7px;background:var(--c-track)}
|
|
36
|
+
.trw button.sg{position:relative;font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:20px!important;height:22px!important;min-height:0!important;margin:0!important;padding:0 9px!important;border:0!important;border-radius:5px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap;text-align:center;justify-content:center}
|
|
37
|
+
.trw button.sg[aria-checked="false"]:hover{background:var(--c-hov)!important;color:var(--c-ink1)!important}
|
|
38
|
+
.trw .ck.rec>span::before{content:"";display:inline-block;width:4px;height:4px;border-radius:50%;background:currentColor;margin-right:5px;vertical-align:2px}
|
|
39
|
+
.trw button.sg.rec{padding:0 4px 0 14px!important}
|
|
40
|
+
.trw button.sg.rec::before{content:"";position:absolute;left:6px;top:50%;width:4px;height:4px;margin-top:-2px;border-radius:50%;background:currentColor;opacity:.4}
|
|
41
|
+
.trw button.sg.sk{color:var(--c-ink3)!important}
|
|
42
|
+
.trw button.sg[aria-checked="true"]{background:var(--c-lift)!important;color:var(--c-ink1)!important;box-shadow:none!important}
|
|
43
|
+
.trw .dseg button.sg[aria-checked="true"]:hover::before{content:"×";position:absolute;left:4px;top:0;width:8px;height:auto;margin:0;border-radius:0;background:none;line-height:22px;text-align:center;opacity:1;color:var(--c-ink3)}
|
|
44
|
+
.trw .k{display:none;position:absolute;top:-8px;right:-4px;font:11px/14px var(--font-mono);padding:0 3px;border-radius:4px;background:var(--c-bg);color:var(--c-ink3);border:0.5px solid var(--c-line-s);pointer-events:none;z-index:1}
|
|
45
|
+
.trw.kb .it:focus-within .k{display:block}
|
|
46
|
+
.trw .ck{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 6px;border-radius:var(--c-r);font-size:12px;color:var(--c-ink2);cursor:pointer;white-space:nowrap;user-select:none}
|
|
47
|
+
.trw .ck:hover{background:var(--c-hover);color:var(--c-ink1)}
|
|
48
|
+
.trw .ck input{width:13px;height:13px;margin:0;accent-color:var(--c-solid);cursor:pointer}
|
|
49
|
+
.trw .ck:has(input:checked){color:var(--c-ink1)}
|
|
50
|
+
.trw button.gh{display:inline-flex;align-items:center;gap:5px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:26px!important;min-height:0!important;margin:0!important;padding:0 8px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}
|
|
51
|
+
.trw button.gh:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}
|
|
52
|
+
.trw svg.i{width:14px;height:14px;flex:none;fill:none;stroke:currentColor;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
|
|
53
|
+
.trw .ed{display:flex;padding:4px 0 8px calc(var(--qw,240px) + 8px)}
|
|
54
|
+
.trw .it:last-child>.ed{padding-bottom:4px}
|
|
55
|
+
.trw .ed textarea,.trw textarea.ask{display:block;flex:1;min-width:0;min-height:28px;height:28px;resize:none!important;overflow:hidden;margin:0!important;padding:4px 10px!important;font:12px/18px var(--font-sans,system-ui)!important;white-space:pre-wrap;overflow-wrap:anywhere;border:0.5px solid var(--c-line-s)!important;border-radius:var(--c-r)!important;background:var(--c-bg)!important;color:var(--c-ink1)!important;box-shadow:none!important}
|
|
56
|
+
.trw .ed textarea::placeholder,.trw textarea.ask::placeholder{color:var(--c-ink3)}
|
|
57
|
+
.trw textarea.ask{flex:1 1 160px;min-height:26px;height:26px;padding:3px 10px!important}
|
|
58
|
+
.trw .ln.top{align-items:flex-start}
|
|
59
|
+
.trw .ln.top .q{min-height:26px}
|
|
60
|
+
.trw .foot{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;padding:8px 0 2px;border-top:0.5px solid var(--c-line)}
|
|
61
|
+
.trw .sum{flex:1 1 auto;display:inline-flex;align-items:center;gap:8px;font-size:12px;line-height:18px;color:var(--c-ink3);white-space:nowrap}
|
|
62
|
+
.trw .sum b{font-weight:500;color:var(--c-ink2)}
|
|
63
|
+
.trw button.lk{font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:18px!important;height:auto!important;min-height:0!important;margin:0!important;padding:0 2px!important;border:0!important;border-radius:4px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:3px}
|
|
64
|
+
.trw button.btn{display:inline-flex;align-items:center;gap:6px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:28px!important;min-height:0!important;margin:0!important;padding:0 12px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}
|
|
65
|
+
.trw button.btn:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}
|
|
66
|
+
.trw button.btn.pri{border-color:var(--c-line-x)!important;color:var(--c-ink1)!important}
|
|
67
|
+
.trw button.btn:disabled,.trw button.gh:disabled{color:var(--c-ink4)!important;background:transparent!important;cursor:default}
|
|
68
|
+
.trw pre.pv{font:12px/18px var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--c-sunken);padding:8px 10px;border-radius:var(--c-r);margin:0 0 8px;color:var(--c-ink2)}
|
|
69
|
+
.trw .fold{display:none;align-items:center;gap:6px;min-height:34px;font-size:12px;color:var(--c-ink2);cursor:pointer;padding:0 40px 0 6px;margin:0 -6px}
|
|
70
|
+
.trw .fold .fs{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
71
|
+
.trw .mk-ok{flex:none;width:10px;height:8px;background:var(--c-ok);clip-path:polygon(0 56%,15% 38%,38% 62%,85% 0,100% 19%,38% 100%)}
|
|
72
|
+
.trw.folded .fold{display:flex}
|
|
73
|
+
.trw.folded>:not(.fold){display:none!important}
|
|
74
|
+
.trw.folded:hover{background:var(--c-hover);border-radius:0}
|
|
75
|
+
.trw button:focus-visible{outline:2px solid var(--c-focus)!important;outline-offset:-2px}
|
|
76
|
+
.trw .ed textarea:focus,.trw textarea.ask:focus{outline:none!important;border-color:var(--c-focus)!important;box-shadow:inset 0 0 0 1px var(--c-focus)!important}
|
|
77
|
+
@container (max-width:480px){
|
|
78
|
+
.trw .grp{display:block}
|
|
79
|
+
.trw .ln{flex-wrap:wrap;row-gap:2px;padding-bottom:2px}
|
|
80
|
+
.trw .q{flex:1 1 auto;white-space:normal}
|
|
81
|
+
.trw .ed{padding-left:0}
|
|
82
|
+
}
|
|
83
|
+
.trw .err{padding:6px 0;font-size:12px;line-height:18px;color:var(--c-attn);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
84
|
+
/* 0.1.1:暗色模式加大轨道、选中块与建议点的对比;宿主拒收提示行 */
|
|
85
|
+
.trw.dark{--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent);--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 26%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 12%,transparent)}
|
|
86
|
+
.trw.dark button.sg.rec::before{opacity:.75}
|
|
87
|
+
.trw.dark button.sg[aria-checked="true"]{font-weight:500!important}
|
|
88
|
+
.trw .ns{display:none;font-size:12px;line-height:18px;color:var(--c-attn);padding:6px 0 0}
|
|
89
|
+
.trw.notsent .ns{display:block}
|
package/dist/tr.js
ADDED
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
/*! @npmjsdnt/turn-reply@0.1.1 turn-reply panel */
|
|
2
|
+
(() => {
|
|
3
|
+
// trw:css
|
|
4
|
+
var css_default = `.trw{
|
|
5
|
+
--c-bg:var(--surface-2);--c-sunken:var(--surface-1);
|
|
6
|
+
--c-hover:var(--fill-ghost-hover,var(--surface-1));--c-soft:var(--fill-ghost-selected,var(--surface-1));
|
|
7
|
+
--c-solid:var(--fill-primary,var(--text-primary));--c-on-solid:var(--on-primary,var(--surface-2));
|
|
8
|
+
--c-line:var(--border);--c-line-s:var(--border-strong);--c-line-x:var(--border-stronger,var(--border-strong));
|
|
9
|
+
--c-ink1:var(--text-primary);--c-ink2:var(--text-secondary);--c-ink3:var(--text-muted);--c-ink4:var(--text-disabled,var(--text-muted));
|
|
10
|
+
--c-attn:var(--text-warning);--c-ok:var(--text-success);--c-focus:var(--border-accent,var(--text-accent));--c-r:6px;
|
|
11
|
+
--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 5%,transparent);--c-lift:var(--surface-3,var(--surface-2));--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 4%,transparent);
|
|
12
|
+
container-type:inline-size;position:relative;box-sizing:border-box;
|
|
13
|
+
font-family:var(--font-sans);font-size:13px;line-height:20px;font-weight:400;
|
|
14
|
+
font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;
|
|
15
|
+
color:var(--c-ink1);background:transparent;border:0;border-radius:0;padding:0
|
|
16
|
+
}
|
|
17
|
+
.trw.dark{--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 15%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent)}
|
|
18
|
+
.trw *,.trw *::before,.trw *::after{box-sizing:border-box}
|
|
19
|
+
.trw [hidden]{display:none!important}
|
|
20
|
+
.trw .mono{font-family:var(--font-mono);font-size:12px}
|
|
21
|
+
.trw .grp{display:flex;align-items:flex-start;padding:6px 0;border-top:0.5px solid var(--c-line)}
|
|
22
|
+
.trw .bd>.grp:first-child{border-top:0}
|
|
23
|
+
.trw .gl{flex:0 0 60px;padding-top:6px;font-size:11px;line-height:16px;color:var(--c-ink2);white-space:nowrap;cursor:default}
|
|
24
|
+
.trw .rs{flex:1 1 auto;min-width:0}
|
|
25
|
+
.trw .it{margin:0 -6px;padding:0 6px;border-radius:0}
|
|
26
|
+
.trw .it:hover,.trw.kb .it:focus-within{background:var(--c-hover)}
|
|
27
|
+
.trw .ln{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;min-height:28px}
|
|
28
|
+
.trw .bd>.grp:first-child .it:first-child .ln{padding-right:40px}
|
|
29
|
+
.trw .q{flex:0 0 var(--qw,240px);min-width:0;display:flex;align-items:center;gap:6px;white-space:nowrap;overflow:hidden}
|
|
30
|
+
.trw.measuring .q{flex:none!important;width:max-content!important}
|
|
31
|
+
.trw .qt{min-width:0;overflow:hidden;text-overflow:ellipsis}
|
|
32
|
+
.trw .n{flex:none;min-width:14px;color:var(--c-ink2)}
|
|
33
|
+
.trw .it.core .qt{font-weight:500}
|
|
34
|
+
.trw .q.info .qt{font-size:12px;color:var(--c-ink2)}
|
|
35
|
+
.trw a.tk{font-family:var(--font-mono);font-size:12px;color:var(--c-ink1);text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:2px}
|
|
36
|
+
.trw .ctl{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;padding:0 4px;margin:0 -4px}
|
|
37
|
+
.trw .cb{flex:none;width:9px;height:9px;background:var(--c-attn);clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
|
|
38
|
+
.trw .seg{display:inline-flex;align-items:center;gap:2px;height:26px;padding:2px;border:0;border-radius:7px;background:var(--c-track)}
|
|
39
|
+
.trw button.sg{position:relative;font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:20px!important;height:22px!important;min-height:0!important;margin:0!important;padding:0 9px!important;border:0!important;border-radius:5px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap;text-align:center;justify-content:center}
|
|
40
|
+
.trw button.sg[aria-checked="false"]:hover{background:var(--c-hov)!important;color:var(--c-ink1)!important}
|
|
41
|
+
.trw .ck.rec>span::before{content:"";display:inline-block;width:4px;height:4px;border-radius:50%;background:currentColor;margin-right:5px;vertical-align:2px}
|
|
42
|
+
.trw button.sg.rec{padding:0 4px 0 14px!important}
|
|
43
|
+
.trw button.sg.rec::before{content:"";position:absolute;left:6px;top:50%;width:4px;height:4px;margin-top:-2px;border-radius:50%;background:currentColor;opacity:.4}
|
|
44
|
+
.trw button.sg.sk{color:var(--c-ink3)!important}
|
|
45
|
+
.trw button.sg[aria-checked="true"]{background:var(--c-lift)!important;color:var(--c-ink1)!important;box-shadow:none!important}
|
|
46
|
+
.trw .dseg button.sg[aria-checked="true"]:hover::before{content:"×";position:absolute;left:4px;top:0;width:8px;height:auto;margin:0;border-radius:0;background:none;line-height:22px;text-align:center;opacity:1;color:var(--c-ink3)}
|
|
47
|
+
.trw .k{display:none;position:absolute;top:-8px;right:-4px;font:11px/14px var(--font-mono);padding:0 3px;border-radius:4px;background:var(--c-bg);color:var(--c-ink3);border:0.5px solid var(--c-line-s);pointer-events:none;z-index:1}
|
|
48
|
+
.trw.kb .it:focus-within .k{display:block}
|
|
49
|
+
.trw .ck{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 6px;border-radius:var(--c-r);font-size:12px;color:var(--c-ink2);cursor:pointer;white-space:nowrap;user-select:none}
|
|
50
|
+
.trw .ck:hover{background:var(--c-hover);color:var(--c-ink1)}
|
|
51
|
+
.trw .ck input{width:13px;height:13px;margin:0;accent-color:var(--c-solid);cursor:pointer}
|
|
52
|
+
.trw .ck:has(input:checked){color:var(--c-ink1)}
|
|
53
|
+
.trw button.gh{display:inline-flex;align-items:center;gap:5px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:26px!important;min-height:0!important;margin:0!important;padding:0 8px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}
|
|
54
|
+
.trw button.gh:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}
|
|
55
|
+
.trw svg.i{width:14px;height:14px;flex:none;fill:none;stroke:currentColor;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
|
|
56
|
+
.trw .ed{display:flex;padding:4px 0 8px calc(var(--qw,240px) + 8px)}
|
|
57
|
+
.trw .it:last-child>.ed{padding-bottom:4px}
|
|
58
|
+
.trw .ed textarea,.trw textarea.ask{display:block;flex:1;min-width:0;min-height:28px;height:28px;resize:none!important;overflow:hidden;margin:0!important;padding:4px 10px!important;font:12px/18px var(--font-sans,system-ui)!important;white-space:pre-wrap;overflow-wrap:anywhere;border:0.5px solid var(--c-line-s)!important;border-radius:var(--c-r)!important;background:var(--c-bg)!important;color:var(--c-ink1)!important;box-shadow:none!important}
|
|
59
|
+
.trw .ed textarea::placeholder,.trw textarea.ask::placeholder{color:var(--c-ink3)}
|
|
60
|
+
.trw textarea.ask{flex:1 1 160px;min-height:26px;height:26px;padding:3px 10px!important}
|
|
61
|
+
.trw .ln.top{align-items:flex-start}
|
|
62
|
+
.trw .ln.top .q{min-height:26px}
|
|
63
|
+
.trw .foot{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;padding:8px 0 2px;border-top:0.5px solid var(--c-line)}
|
|
64
|
+
.trw .sum{flex:1 1 auto;display:inline-flex;align-items:center;gap:8px;font-size:12px;line-height:18px;color:var(--c-ink3);white-space:nowrap}
|
|
65
|
+
.trw .sum b{font-weight:500;color:var(--c-ink2)}
|
|
66
|
+
.trw button.lk{font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:18px!important;height:auto!important;min-height:0!important;margin:0!important;padding:0 2px!important;border:0!important;border-radius:4px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:3px}
|
|
67
|
+
.trw button.btn{display:inline-flex;align-items:center;gap:6px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:28px!important;min-height:0!important;margin:0!important;padding:0 12px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}
|
|
68
|
+
.trw button.btn:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}
|
|
69
|
+
.trw button.btn.pri{border-color:var(--c-line-x)!important;color:var(--c-ink1)!important}
|
|
70
|
+
.trw button.btn:disabled,.trw button.gh:disabled{color:var(--c-ink4)!important;background:transparent!important;cursor:default}
|
|
71
|
+
.trw pre.pv{font:12px/18px var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--c-sunken);padding:8px 10px;border-radius:var(--c-r);margin:0 0 8px;color:var(--c-ink2)}
|
|
72
|
+
.trw .fold{display:none;align-items:center;gap:6px;min-height:34px;font-size:12px;color:var(--c-ink2);cursor:pointer;padding:0 40px 0 6px;margin:0 -6px}
|
|
73
|
+
.trw .fold .fs{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
74
|
+
.trw .mk-ok{flex:none;width:10px;height:8px;background:var(--c-ok);clip-path:polygon(0 56%,15% 38%,38% 62%,85% 0,100% 19%,38% 100%)}
|
|
75
|
+
.trw.folded .fold{display:flex}
|
|
76
|
+
.trw.folded>:not(.fold){display:none!important}
|
|
77
|
+
.trw.folded:hover{background:var(--c-hover);border-radius:0}
|
|
78
|
+
.trw button:focus-visible{outline:2px solid var(--c-focus)!important;outline-offset:-2px}
|
|
79
|
+
.trw .ed textarea:focus,.trw textarea.ask:focus{outline:none!important;border-color:var(--c-focus)!important;box-shadow:inset 0 0 0 1px var(--c-focus)!important}
|
|
80
|
+
@container (max-width:480px){
|
|
81
|
+
.trw .grp{display:block}
|
|
82
|
+
.trw .ln{flex-wrap:wrap;row-gap:2px;padding-bottom:2px}
|
|
83
|
+
.trw .q{flex:1 1 auto;white-space:normal}
|
|
84
|
+
.trw .ed{padding-left:0}
|
|
85
|
+
}
|
|
86
|
+
.trw .err{padding:6px 0;font-size:12px;line-height:18px;color:var(--c-attn);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
87
|
+
/* 0.1.1:暗色模式加大轨道、选中块与建议点的对比;宿主拒收提示行 */
|
|
88
|
+
.trw.dark{--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent);--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 26%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 12%,transparent)}
|
|
89
|
+
.trw.dark button.sg.rec::before{opacity:.75}
|
|
90
|
+
.trw.dark button.sg[aria-checked="true"]{font-weight:500!important}
|
|
91
|
+
.trw .ns{display:none;font-size:12px;line-height:18px;color:var(--c-attn);padding:6px 0 0}
|
|
92
|
+
.trw.notsent .ns{display:block}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
// ../schema/validate.ts
|
|
96
|
+
var isStr = (x, max = Infinity, min = 1) => typeof x === "string" && x.length >= min && x.length <= max;
|
|
97
|
+
var only = (o, keys, at, e) => {
|
|
98
|
+
for (const k of Object.keys(o))
|
|
99
|
+
if (!keys.includes(k))
|
|
100
|
+
e.push(`${at}.${k} 未定义的字段`);
|
|
101
|
+
};
|
|
102
|
+
function validate(s) {
|
|
103
|
+
const e = [];
|
|
104
|
+
if (!s || typeof s !== "object" || Array.isArray(s))
|
|
105
|
+
return ["$ 不是对象"];
|
|
106
|
+
only(s, ["v", "turn", "ticketUrl", "decisions", "actions", "delivery", "optional"], "$", e);
|
|
107
|
+
if (s.v !== 1)
|
|
108
|
+
e.push("$.v 必须为 1");
|
|
109
|
+
if (!isStr(s.turn, 40) || !/^[\w.:-]+$/.test(s.turn))
|
|
110
|
+
e.push("$.turn 缺失或含非法字符");
|
|
111
|
+
if (s.ticketUrl !== undefined && !(isStr(s.ticketUrl) && s.ticketUrl.startsWith("https://")))
|
|
112
|
+
e.push("$.ticketUrl 须为 https:// 前缀");
|
|
113
|
+
const seen = new Set;
|
|
114
|
+
(s.decisions ?? []).forEach((d, i) => {
|
|
115
|
+
const at = `$.decisions[${i}]`;
|
|
116
|
+
if (!d || typeof d !== "object")
|
|
117
|
+
return e.push(`${at} 不是对象`);
|
|
118
|
+
only(d, ["n", "group", "core", "q", "opts", "rec", "def", "multi", "ask", "ph"], at, e);
|
|
119
|
+
if (!Number.isInteger(d.n) || d.n < 1)
|
|
120
|
+
e.push(`${at}.n 须为正整数`);
|
|
121
|
+
else if (seen.has(d.n))
|
|
122
|
+
e.push(`${at}.n=${d.n} 重复`);
|
|
123
|
+
else
|
|
124
|
+
seen.add(d.n);
|
|
125
|
+
if (!["now", "defaulted", "pending"].includes(d.group))
|
|
126
|
+
e.push(`${at}.group 非法`);
|
|
127
|
+
if (!isStr(d.q, 40))
|
|
128
|
+
e.push(`${at}.q 须为 1–40 字`);
|
|
129
|
+
if (d.core !== undefined && typeof d.core !== "boolean")
|
|
130
|
+
e.push(`${at}.core 须为布尔`);
|
|
131
|
+
if (d.def !== undefined && !isStr(d.def, 60))
|
|
132
|
+
e.push(`${at}.def 须为 ≤60 字`);
|
|
133
|
+
if (d.multi !== undefined && d.multi !== true)
|
|
134
|
+
e.push(`${at}.multi 只能为 true`);
|
|
135
|
+
if (d.ask !== undefined && d.ask !== true)
|
|
136
|
+
e.push(`${at}.ask 只能为 true`);
|
|
137
|
+
const opts = d.opts;
|
|
138
|
+
if (opts !== undefined && !(Array.isArray(opts) && opts.every((o) => isStr(o, 12))))
|
|
139
|
+
e.push(`${at}.opts 须为 1–12 字的字符串数组`);
|
|
140
|
+
const n = Array.isArray(opts) ? opts.length : 0;
|
|
141
|
+
const kind = d.group === "defaulted" ? "defaulted" : d.ask ? "ask" : d.multi ? "multi" : "one";
|
|
142
|
+
if (d.ask && d.multi)
|
|
143
|
+
e.push(`${at} ask 与 multi 不能同时出现`);
|
|
144
|
+
if (kind === "defaulted" && (opts || d.rec !== undefined || d.ask || d.multi))
|
|
145
|
+
e.push(`${at} defaulted 条目只有「推翻」,不带 opts/rec/ask/multi`);
|
|
146
|
+
if (kind === "ask" && (opts || d.rec !== undefined))
|
|
147
|
+
e.push(`${at} ask 条目不带 opts/rec`);
|
|
148
|
+
if (d.ph !== undefined && (kind !== "ask" || !isStr(d.ph, 40)))
|
|
149
|
+
e.push(`${at}.ph 只用于 ask,≤40 字`);
|
|
150
|
+
if (kind === "one" && (n < 2 || n > 4))
|
|
151
|
+
e.push(`${at} 单选须有 2–4 个 opts(现 ${n})`);
|
|
152
|
+
if (kind === "multi" && (n < 2 || n > 8))
|
|
153
|
+
e.push(`${at} 多选须有 2–8 个 opts(现 ${n})`);
|
|
154
|
+
if (kind === "one" && d.rec !== undefined && !(Number.isInteger(d.rec) && d.rec >= 0 && d.rec < n))
|
|
155
|
+
e.push(`${at}.rec 须为 opts 下标`);
|
|
156
|
+
if (kind === "multi" && d.rec !== undefined && !(Array.isArray(d.rec) && d.rec.every((r) => Number.isInteger(r) && r >= 0 && r < n) && new Set(d.rec).size === d.rec.length))
|
|
157
|
+
e.push(`${at}.rec 须为不重复的 opts 下标数组`);
|
|
158
|
+
if (Array.isArray(opts) && new Set(opts).size !== n)
|
|
159
|
+
e.push(`${at}.opts 有重复`);
|
|
160
|
+
});
|
|
161
|
+
const ns = [...seen].sort((a, b) => a - b);
|
|
162
|
+
if (ns.length && ns.some((x, k) => k && x !== ns[k - 1] + 1))
|
|
163
|
+
e.push(`$.decisions 编号不连续:${ns.join(",")}`);
|
|
164
|
+
(s.actions ?? []).forEach((a, i) => {
|
|
165
|
+
const at = `$.actions[${i}]`;
|
|
166
|
+
if (!a || typeof a !== "object")
|
|
167
|
+
return e.push(`${at} 不是对象`);
|
|
168
|
+
only(a, ["text", "short", "cmd"], at, e);
|
|
169
|
+
if (!isStr(a.text, 120))
|
|
170
|
+
e.push(`${at}.text 须为 1–120 字`);
|
|
171
|
+
if (!isStr(a.short, 24))
|
|
172
|
+
e.push(`${at}.short 须为 1–24 字`);
|
|
173
|
+
if (a.cmd !== undefined && !isStr(a.cmd))
|
|
174
|
+
e.push(`${at}.cmd 须为非空字符串`);
|
|
175
|
+
});
|
|
176
|
+
if (s.delivery !== undefined) {
|
|
177
|
+
const d = s.delivery;
|
|
178
|
+
if (!d || typeof d !== "object")
|
|
179
|
+
e.push("$.delivery 不是对象");
|
|
180
|
+
else {
|
|
181
|
+
only(d, ["info", "toggles"], "$.delivery", e);
|
|
182
|
+
if (d.info !== undefined && !isStr(d.info, 120))
|
|
183
|
+
e.push("$.delivery.info 须为 ≤120 字");
|
|
184
|
+
(d.toggles ?? []).forEach((t, i) => {
|
|
185
|
+
only(t ?? {}, ["label", "rec"], `$.delivery.toggles[${i}]`, e);
|
|
186
|
+
if (!isStr(t?.label, 20))
|
|
187
|
+
e.push(`$.delivery.toggles[${i}].label 须为 1–20 字`);
|
|
188
|
+
if (t?.rec !== undefined && typeof t.rec !== "boolean")
|
|
189
|
+
e.push(`$.delivery.toggles[${i}].rec 须为布尔`);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
(s.optional ?? []).forEach((o, i) => {
|
|
194
|
+
only(o ?? {}, ["text"], `$.optional[${i}]`, e);
|
|
195
|
+
if (!isStr(o?.text, 120))
|
|
196
|
+
e.push(`$.optional[${i}].text 须为 1–120 字`);
|
|
197
|
+
});
|
|
198
|
+
for (const k of ["decisions", "actions", "optional"])
|
|
199
|
+
if (s[k] !== undefined && !Array.isArray(s[k]))
|
|
200
|
+
e.push(`$.${k} 须为数组`);
|
|
201
|
+
return e;
|
|
202
|
+
}
|
|
203
|
+
if (false) {}
|
|
204
|
+
|
|
205
|
+
// src/panel.js
|
|
206
|
+
var renderPanel = (() => {
|
|
207
|
+
const el = (t, a = {}, ...k) => {
|
|
208
|
+
const e = document.createElement(t);
|
|
209
|
+
for (const [x, v] of Object.entries(a)) {
|
|
210
|
+
if (v == null || v === false)
|
|
211
|
+
continue;
|
|
212
|
+
if (x === "cls")
|
|
213
|
+
e.className = v;
|
|
214
|
+
else
|
|
215
|
+
e.setAttribute(x, v);
|
|
216
|
+
}
|
|
217
|
+
k.flat().forEach((c) => {
|
|
218
|
+
if (c != null && c !== false)
|
|
219
|
+
e.append(c);
|
|
220
|
+
});
|
|
221
|
+
return e;
|
|
222
|
+
};
|
|
223
|
+
const ICON = {
|
|
224
|
+
copy: '<rect x="5.5" y="5.5" width="7.5" height="7.5" rx="1.2"/><path d="M10.5 3.5V3a.5.5 0 0 0-.5-.5H3a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 .5.5h.5"/>',
|
|
225
|
+
reset: '<path d="M3.2 8a4.8 4.8 0 1 0 1.4-3.4"/><path d="M3.2 2.6v2.6h2.6"/>'
|
|
226
|
+
};
|
|
227
|
+
const icon = (n) => {
|
|
228
|
+
const s = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
229
|
+
s.setAttribute("viewBox", "0 0 16 16");
|
|
230
|
+
s.setAttribute("class", "i");
|
|
231
|
+
s.setAttribute("aria-hidden", "true");
|
|
232
|
+
s.innerHTML = ICON[n];
|
|
233
|
+
return s;
|
|
234
|
+
};
|
|
235
|
+
let LIN = null;
|
|
236
|
+
const linkify = (s) => {
|
|
237
|
+
if (!s)
|
|
238
|
+
return [];
|
|
239
|
+
const out = [];
|
|
240
|
+
let last = 0;
|
|
241
|
+
s.replace(/\b[A-Z]{2,6}-\d+\b|\b[0-9a-f]{7,40}\b/g, (m, i) => {
|
|
242
|
+
const tk = /^[A-Z]/.test(m);
|
|
243
|
+
if (!tk && !(/\d/.test(m) && /[a-f]/.test(m)))
|
|
244
|
+
return m;
|
|
245
|
+
if (tk && !LIN)
|
|
246
|
+
return m;
|
|
247
|
+
out.push(s.slice(last, i), tk ? el("a", { cls: "tk", href: LIN + m, target: "_blank", rel: "noopener" }, m) : el("span", { cls: "mono" }, m.slice(0, 7)));
|
|
248
|
+
last = i + m.length;
|
|
249
|
+
return m;
|
|
250
|
+
});
|
|
251
|
+
out.push(s.slice(last));
|
|
252
|
+
return out;
|
|
253
|
+
};
|
|
254
|
+
const copyText = (t) => {
|
|
255
|
+
const fb = () => {
|
|
256
|
+
const a = el("textarea", { style: "position:absolute;left:-9999px;top:0", "aria-hidden": "true" });
|
|
257
|
+
a.value = t;
|
|
258
|
+
document.body.append(a);
|
|
259
|
+
a.select();
|
|
260
|
+
try {
|
|
261
|
+
document.execCommand("copy");
|
|
262
|
+
} catch (_) {}
|
|
263
|
+
a.remove();
|
|
264
|
+
};
|
|
265
|
+
try {
|
|
266
|
+
navigator.clipboard.writeText(t).catch(fb);
|
|
267
|
+
} catch (_) {
|
|
268
|
+
fb();
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
const flash = (lb, t) => {
|
|
272
|
+
if (!lb.dataset.l)
|
|
273
|
+
lb.dataset.l = lb.textContent;
|
|
274
|
+
lb.textContent = t;
|
|
275
|
+
clearTimeout(lb._t);
|
|
276
|
+
lb._t = setTimeout(() => {
|
|
277
|
+
lb.textContent = lb.dataset.l;
|
|
278
|
+
}, 1400);
|
|
279
|
+
};
|
|
280
|
+
const count = (t) => t ? t.split(`
|
|
281
|
+
`).filter(Boolean).length : 0;
|
|
282
|
+
const cut = (s, n) => s.length > n ? s.slice(0, n) + "…" : s;
|
|
283
|
+
const sameSet = (a, b) => a.size === b.size && [...a].every((v) => b.has(v));
|
|
284
|
+
const LAB = { done: "已做", issue: "有问题", no: "没做", you: "你来做" };
|
|
285
|
+
function render(root, spec) {
|
|
286
|
+
root.replaceChildren();
|
|
287
|
+
root.classList.remove("folded", "kb");
|
|
288
|
+
{
|
|
289
|
+
const c = (getComputedStyle(root).color.match(/\d+/g) || [0, 0, 0]).slice(0, 3).map(Number);
|
|
290
|
+
root.classList.toggle("dark", (c[0] * 299 + c[1] * 587 + c[2] * 114) / 1000 > 140);
|
|
291
|
+
}
|
|
292
|
+
LIN = spec.ticketUrl || null;
|
|
293
|
+
const TURN = spec.turn ? String(spec.turn).replace(/[^\w.:-]/g, "") : "";
|
|
294
|
+
const hasSend = typeof sendPrompt === "function";
|
|
295
|
+
const S = { dec: [], act: [], del: [], opt: [] };
|
|
296
|
+
const items = [];
|
|
297
|
+
const bd = el("div", { cls: "bd" });
|
|
298
|
+
const group = (label, hint) => {
|
|
299
|
+
const rs = el("div", { cls: "rs" });
|
|
300
|
+
bd.append(el("div", { cls: "grp" }, el("div", { cls: "gl", title: hint || null }, label), rs));
|
|
301
|
+
return rs;
|
|
302
|
+
};
|
|
303
|
+
const item = (rs, ln, extra) => {
|
|
304
|
+
const it = el("div", { cls: "it" }, ln, ...extra || []);
|
|
305
|
+
rs.append(it);
|
|
306
|
+
items.push(it);
|
|
307
|
+
return it;
|
|
308
|
+
};
|
|
309
|
+
const qcell = (d, text) => {
|
|
310
|
+
const qt = el("span", { cls: "qt" }, ...linkify(text));
|
|
311
|
+
const q = el("div", { cls: "q", title: text || null }, d && d.n != null ? el("span", { cls: "n" }, d.n + ".") : null, d && d.core ? el("span", { cls: "cb", role: "img", "aria-label": "核心决策", title: "核心决策" }) : null, qt);
|
|
312
|
+
return { q, qt };
|
|
313
|
+
};
|
|
314
|
+
const grow = (t) => {
|
|
315
|
+
if (!t.value) {
|
|
316
|
+
t.style.removeProperty("height");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
t.style.setProperty("height", "auto", "important");
|
|
320
|
+
t.style.setProperty("height", t.scrollHeight + (t.offsetHeight - t.clientHeight) + "px", "important");
|
|
321
|
+
};
|
|
322
|
+
const area = (ph, cls) => {
|
|
323
|
+
const t = el("textarea", { rows: "1", cls: cls || null, placeholder: ph, "aria-label": ph });
|
|
324
|
+
t.addEventListener("input", () => grow(t));
|
|
325
|
+
return t;
|
|
326
|
+
};
|
|
327
|
+
const editor = (st, ph) => {
|
|
328
|
+
const box = el("div", { cls: "ed" });
|
|
329
|
+
const i = area(ph);
|
|
330
|
+
i.addEventListener("input", () => {
|
|
331
|
+
st.text = i.value;
|
|
332
|
+
upd();
|
|
333
|
+
});
|
|
334
|
+
box.append(i);
|
|
335
|
+
box.hidden = true;
|
|
336
|
+
st.ed = box;
|
|
337
|
+
return { box, i, show(on, focus) {
|
|
338
|
+
box.hidden = !on;
|
|
339
|
+
if (on) {
|
|
340
|
+
grow(i);
|
|
341
|
+
if (focus)
|
|
342
|
+
i.focus();
|
|
343
|
+
}
|
|
344
|
+
upd();
|
|
345
|
+
} };
|
|
346
|
+
};
|
|
347
|
+
const txt = (st) => st.ed && !st.ed.hidden ? (st.text || "").trim() : "";
|
|
348
|
+
const seg = (opts, st, o = {}) => {
|
|
349
|
+
const init = o.init === undefined ? null : o.init;
|
|
350
|
+
const w = el("div", { cls: "seg" + (o.cls ? " " + o.cls : ""), role: "radiogroup", "aria-label": o.label || null, title: o.title || null });
|
|
351
|
+
const bs = opts.map((op, i) => el("button", { type: "button", role: "radio", cls: "sg" + (op.rec ? " rec" : ""), "data-col": String(i), title: op.title || null }, op.label, el("span", { cls: "k", "aria-hidden": "true" }, String(i + 1))));
|
|
352
|
+
const paint = () => {
|
|
353
|
+
const idx = opts.findIndex((x) => x.v === st.pick);
|
|
354
|
+
const chg = st.pick !== init;
|
|
355
|
+
bs.forEach((b, i) => {
|
|
356
|
+
b.setAttribute("aria-checked", String(i === idx));
|
|
357
|
+
b.classList.toggle("chg", i === idx && chg);
|
|
358
|
+
b.tabIndex = i === (idx < 0 ? 0 : idx) ? 0 : -1;
|
|
359
|
+
});
|
|
360
|
+
};
|
|
361
|
+
const set = (v, kb) => {
|
|
362
|
+
st.pick = v;
|
|
363
|
+
paint();
|
|
364
|
+
if (o.onChange)
|
|
365
|
+
o.onChange(kb);
|
|
366
|
+
upd();
|
|
367
|
+
};
|
|
368
|
+
const choose = (i, kb) => {
|
|
369
|
+
if (opts[i])
|
|
370
|
+
set(o.toggle && st.pick === opts[i].v ? null : opts[i].v, kb);
|
|
371
|
+
};
|
|
372
|
+
bs.forEach((b, i) => b.addEventListener("click", () => choose(i, false)));
|
|
373
|
+
w.addEventListener("keydown", (e) => {
|
|
374
|
+
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft")
|
|
375
|
+
return;
|
|
376
|
+
e.preventDefault();
|
|
377
|
+
const c = Math.max(0, bs.indexOf(document.activeElement));
|
|
378
|
+
const n = (c + (e.key === "ArrowRight" ? 1 : -1) + bs.length) % bs.length;
|
|
379
|
+
set(opts[n].v, true);
|
|
380
|
+
bs[n].focus();
|
|
381
|
+
});
|
|
382
|
+
paint();
|
|
383
|
+
w.append(...bs);
|
|
384
|
+
return { w, set, choose, focus: () => (bs.find((b) => b.tabIndex === 0) || bs[0]).focus() };
|
|
385
|
+
};
|
|
386
|
+
const check = (label, on, o = {}) => {
|
|
387
|
+
const i = el("input", { type: "checkbox" });
|
|
388
|
+
i.checked = !!on;
|
|
389
|
+
const lb = el("label", { cls: "ck" + (o.rec ? " rec" : ""), title: o.title || null }, i, el("span", {}, label));
|
|
390
|
+
return { lb, i };
|
|
391
|
+
};
|
|
392
|
+
const multi = (labels, recs, st, initOn) => {
|
|
393
|
+
const w = el("div", { cls: "ctl", role: "group" });
|
|
394
|
+
const cs = labels.map((l, i) => check(l, initOn.has(i), { rec: recs.includes(i), title: recs.includes(i) ? "建议" : null }));
|
|
395
|
+
const paint = () => cs.forEach((c, i) => c.lb.classList.toggle("chg", st.sel.has(i) !== initOn.has(i)));
|
|
396
|
+
cs.forEach((c, i) => c.i.addEventListener("change", () => {
|
|
397
|
+
c.i.checked ? st.sel.add(i) : st.sel.delete(i);
|
|
398
|
+
paint();
|
|
399
|
+
upd();
|
|
400
|
+
}));
|
|
401
|
+
w.append(...cs.map((c) => c.lb));
|
|
402
|
+
return { w, set: (s) => {
|
|
403
|
+
st.sel = new Set(s);
|
|
404
|
+
cs.forEach((c, i) => {
|
|
405
|
+
c.i.checked = st.sel.has(i);
|
|
406
|
+
});
|
|
407
|
+
paint();
|
|
408
|
+
upd();
|
|
409
|
+
}, toggle: (i) => {
|
|
410
|
+
if (cs[i])
|
|
411
|
+
cs[i].i.click();
|
|
412
|
+
}, focus: () => cs[0] && cs[0].i.focus() };
|
|
413
|
+
};
|
|
414
|
+
const ghost = (label, o = {}) => el("button", { type: "button", cls: "gh", title: o.title || null }, o.icon ? icon(o.icon) : null, label ? el("span", {}, label) : null);
|
|
415
|
+
const groups = [["本轮拍板", "需要你拍板", (d) => d.group === "now"], ["早前", "上轮按默认处理(勾「推翻」才回复)与早前尚未决策(默认不回复)", (d) => d.group !== "now"]];
|
|
416
|
+
groups.forEach(([label, hint, f]) => {
|
|
417
|
+
const ds = (spec.decisions || []).filter(f).sort((a, b) => a.n - b.n);
|
|
418
|
+
if (!ds.length)
|
|
419
|
+
return;
|
|
420
|
+
const rs = group(label, hint);
|
|
421
|
+
ds.forEach((d) => {
|
|
422
|
+
const st = { pick: null, text: "" };
|
|
423
|
+
const x = { d, st };
|
|
424
|
+
S.dec.push(x);
|
|
425
|
+
const { q } = qcell(d, d.q);
|
|
426
|
+
const extra = [];
|
|
427
|
+
let ctl, pick, focus, note = null;
|
|
428
|
+
if (d.group === "defaulted") {
|
|
429
|
+
const ed = editor(st, "改成什么");
|
|
430
|
+
const c = check("推翻", false, { title: "推翻上轮的默认处理" });
|
|
431
|
+
c.i.addEventListener("change", () => {
|
|
432
|
+
st.pick = c.i.checked ? "over" : null;
|
|
433
|
+
c.lb.classList.toggle("chg", c.i.checked);
|
|
434
|
+
ed.show(c.i.checked, true);
|
|
435
|
+
});
|
|
436
|
+
ctl = el("div", { cls: "ctl" }, c.lb);
|
|
437
|
+
extra.push(ed.box);
|
|
438
|
+
pick = (i) => {
|
|
439
|
+
if (i === 0)
|
|
440
|
+
c.i.click();
|
|
441
|
+
};
|
|
442
|
+
focus = () => c.i.focus();
|
|
443
|
+
ed.i.__esc = () => c.i.focus();
|
|
444
|
+
x.changed = () => st.pick === "over";
|
|
445
|
+
x.sum = () => "推翻";
|
|
446
|
+
} else if (d.ask) {
|
|
447
|
+
const inp = area(d.ph || "填写;要附文件就留空,到输入框里附", "ask");
|
|
448
|
+
inp.addEventListener("input", () => {
|
|
449
|
+
st.text = inp.value;
|
|
450
|
+
upd();
|
|
451
|
+
});
|
|
452
|
+
ctl = inp;
|
|
453
|
+
x.top = true;
|
|
454
|
+
pick = () => {};
|
|
455
|
+
focus = () => inp.focus();
|
|
456
|
+
x.changed = () => !!(st.text || "").trim();
|
|
457
|
+
x.sum = () => "已填";
|
|
458
|
+
} else if (d.multi) {
|
|
459
|
+
const os = d.opts || [];
|
|
460
|
+
const recs = Array.isArray(d.rec) ? d.rec : d.rec != null ? [d.rec] : [];
|
|
461
|
+
const initOn = new Set(d.group === "now" ? recs : []);
|
|
462
|
+
st.sel = new Set(initOn);
|
|
463
|
+
x.init = initOn;
|
|
464
|
+
const m = multi(os, recs, st, initOn);
|
|
465
|
+
const ed = editor(st, "附一句;不勾选项时即为你的回复");
|
|
466
|
+
note = () => {
|
|
467
|
+
const on = ed.box.hidden;
|
|
468
|
+
ed.show(on, true);
|
|
469
|
+
if (!on)
|
|
470
|
+
m.focus();
|
|
471
|
+
};
|
|
472
|
+
ctl = m.w;
|
|
473
|
+
extra.push(ed.box);
|
|
474
|
+
x.reset = () => m.set(initOn);
|
|
475
|
+
pick = (i) => m.toggle(i);
|
|
476
|
+
focus = m.focus;
|
|
477
|
+
ed.i.__esc = () => {
|
|
478
|
+
ed.show(false);
|
|
479
|
+
m.focus();
|
|
480
|
+
};
|
|
481
|
+
x.changed = () => !sameSet(st.sel, initOn) || !!txt(st);
|
|
482
|
+
x.sum = () => {
|
|
483
|
+
const s = [...st.sel].sort((a, b) => a - b).map((i) => os[i]);
|
|
484
|
+
return (s.length ? s.join("、") : "都不选") + (txt(st) ? "(附言)" : "");
|
|
485
|
+
};
|
|
486
|
+
} else {
|
|
487
|
+
const os = d.opts || [];
|
|
488
|
+
const opts = os.map((l, i) => ({ v: "o" + i, label: l, rec: i === d.rec, title: i === d.rec ? "建议;再点一次即不回复" : "再点一次即不回复" }));
|
|
489
|
+
st.pick = d.group === "now" && os[d.rec] != null ? "o" + d.rec : null;
|
|
490
|
+
x.init = st.pick;
|
|
491
|
+
const sg = seg(opts, st, { label: d.q, init: x.init, toggle: true, cls: "dseg", title: d.def || null });
|
|
492
|
+
const ed = editor(st, "附一句;不选选项时即为你的回复");
|
|
493
|
+
note = () => {
|
|
494
|
+
const on = ed.box.hidden;
|
|
495
|
+
ed.show(on, true);
|
|
496
|
+
if (!on)
|
|
497
|
+
sg.focus();
|
|
498
|
+
};
|
|
499
|
+
ctl = el("div", { cls: "ctl" }, sg.w);
|
|
500
|
+
extra.push(ed.box);
|
|
501
|
+
x.reset = () => sg.set(x.init);
|
|
502
|
+
pick = (i) => sg.choose(i, false);
|
|
503
|
+
focus = sg.focus;
|
|
504
|
+
ed.i.__esc = () => {
|
|
505
|
+
ed.show(false);
|
|
506
|
+
sg.focus();
|
|
507
|
+
};
|
|
508
|
+
x.changed = () => st.pick !== x.init || !!txt(st);
|
|
509
|
+
x.sum = () => (st.pick ? os[+st.pick.slice(1)] : "不回复") + (txt(st) ? "(附言)" : "");
|
|
510
|
+
}
|
|
511
|
+
const ln = el("div", { cls: "ln" + (x.top ? " top" : "") }, q, ctl);
|
|
512
|
+
const it = item(rs, ln, extra);
|
|
513
|
+
if (note) {
|
|
514
|
+
ln.addEventListener("click", (e) => {
|
|
515
|
+
if (e.target.closest("button,input,textarea,label,a,.ctl"))
|
|
516
|
+
return;
|
|
517
|
+
note();
|
|
518
|
+
});
|
|
519
|
+
it.__note = note;
|
|
520
|
+
}
|
|
521
|
+
if (d.core)
|
|
522
|
+
it.classList.add("core");
|
|
523
|
+
it.__pick = pick;
|
|
524
|
+
it.__focus = focus;
|
|
525
|
+
x.it = it;
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
if (spec.actions && spec.actions.length) {
|
|
529
|
+
const rs = group("你要做的", "做完后在这里回执");
|
|
530
|
+
spec.actions.forEach((a) => {
|
|
531
|
+
const st = { pick: null, text: "" };
|
|
532
|
+
const ed = editor(st, "贴结果、报错或原因");
|
|
533
|
+
const opts = [["done", "已做"], ["issue", "有问题"], ["no", "没做"], ["you", "你来做"]].map(([v, label]) => ({ v, label }));
|
|
534
|
+
const sg = seg(opts, st, { toggle: true, init: null, label: a.short || a.text, onChange: (kb) => {
|
|
535
|
+
const on = st.pick === "issue" || st.pick === "no";
|
|
536
|
+
ed.box.hidden = !on;
|
|
537
|
+
if (on) {
|
|
538
|
+
grow(ed.i);
|
|
539
|
+
if (!kb)
|
|
540
|
+
ed.i.focus();
|
|
541
|
+
}
|
|
542
|
+
} });
|
|
543
|
+
let cp = null;
|
|
544
|
+
if (a.cmd) {
|
|
545
|
+
cp = ghost("复制命令", { icon: "copy", title: a.cmd });
|
|
546
|
+
const lb = cp.querySelector("span");
|
|
547
|
+
cp.addEventListener("click", () => {
|
|
548
|
+
copyText(a.cmd);
|
|
549
|
+
flash(lb, "已复制");
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
const { q } = qcell(null, a.text);
|
|
553
|
+
const it = item(rs, el("div", { cls: "ln" }, q, el("div", { cls: "ctl" }, sg.w, cp)), [ed.box]);
|
|
554
|
+
it.__pick = (i) => sg.choose(i, false);
|
|
555
|
+
it.__focus = sg.focus;
|
|
556
|
+
ed.i.__esc = sg.focus;
|
|
557
|
+
S.act.push({ a, st, it });
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
const dl = spec.delivery;
|
|
561
|
+
const optl = spec.optional || [];
|
|
562
|
+
if (dl || optl.length) {
|
|
563
|
+
const rs = group("收尾");
|
|
564
|
+
if (dl) {
|
|
565
|
+
const cs = [];
|
|
566
|
+
(dl.toggles || []).forEach((t) => {
|
|
567
|
+
const x = { t, init: !!t.rec, st: { on: !!t.rec } };
|
|
568
|
+
const c = check(t.label, x.init, { rec: t.rec, title: t.rec ? "建议;取消勾选即不表态" : "勾选才表态" });
|
|
569
|
+
c.i.addEventListener("change", () => {
|
|
570
|
+
x.st.on = c.i.checked;
|
|
571
|
+
c.lb.classList.toggle("chg", x.st.on !== x.init);
|
|
572
|
+
upd();
|
|
573
|
+
});
|
|
574
|
+
S.del.push(x);
|
|
575
|
+
cs.push(c);
|
|
576
|
+
});
|
|
577
|
+
const { q } = qcell(null, dl.info || "");
|
|
578
|
+
q.classList.add("info");
|
|
579
|
+
const it = item(rs, el("div", { cls: "ln" }, q, el("div", { cls: "ctl" }, ...cs.map((c) => c.lb))));
|
|
580
|
+
it.__pick = (i) => {
|
|
581
|
+
if (cs[i])
|
|
582
|
+
cs[i].i.click();
|
|
583
|
+
};
|
|
584
|
+
it.__focus = () => {
|
|
585
|
+
if (cs[0])
|
|
586
|
+
cs[0].i.focus();
|
|
587
|
+
};
|
|
588
|
+
S.delIt = it;
|
|
589
|
+
}
|
|
590
|
+
optl.forEach((o) => {
|
|
591
|
+
const st = { on: false };
|
|
592
|
+
const c = check("也做", false);
|
|
593
|
+
c.i.addEventListener("change", () => {
|
|
594
|
+
st.on = c.i.checked;
|
|
595
|
+
c.lb.classList.toggle("chg", st.on);
|
|
596
|
+
upd();
|
|
597
|
+
});
|
|
598
|
+
const { q } = qcell(null, o.text);
|
|
599
|
+
const it = item(rs, el("div", { cls: "ln" }, q, el("div", { cls: "ctl" }, c.lb)));
|
|
600
|
+
it.__pick = (i) => {
|
|
601
|
+
if (i === 0)
|
|
602
|
+
c.i.click();
|
|
603
|
+
};
|
|
604
|
+
it.__focus = () => c.i.focus();
|
|
605
|
+
S.opt.push({ o, st, it });
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
const fmt = (head, t, ind) => {
|
|
609
|
+
let ls = String(t).split(`
|
|
610
|
+
`).map((l) => l.replace(/\s+$/, ""));
|
|
611
|
+
while (ls.length && !ls[0])
|
|
612
|
+
ls.shift();
|
|
613
|
+
while (ls.length && !ls[ls.length - 1])
|
|
614
|
+
ls.pop();
|
|
615
|
+
ls = ls.filter((l, k) => l || ls[k - 1]);
|
|
616
|
+
if (ls.length <= 1)
|
|
617
|
+
return head + (ls[0] || "");
|
|
618
|
+
const pad = " ".repeat(ind);
|
|
619
|
+
return head + ls[0] + `
|
|
620
|
+
` + ls.slice(1).map((l) => pad + l).join(`
|
|
621
|
+
`);
|
|
622
|
+
};
|
|
623
|
+
const line = (x) => {
|
|
624
|
+
const { d, st } = x, t = txt(st), h = d.n + ". ", w = h.length;
|
|
625
|
+
if (d.ask) {
|
|
626
|
+
const v = (st.text || "").trim();
|
|
627
|
+
return v ? fmt(h, v, w) : null;
|
|
628
|
+
}
|
|
629
|
+
if (d.group === "defaulted")
|
|
630
|
+
return st.pick === "over" ? fmt(h + "推翻:", t || "(待补充)", w) : null;
|
|
631
|
+
if (d.multi) {
|
|
632
|
+
const s = [...st.sel].sort((a, b) => a - b).map((i) => d.opts[i]);
|
|
633
|
+
if (s.length)
|
|
634
|
+
return fmt(h + s.join("、") + (t ? "," : ""), t, w);
|
|
635
|
+
if (x.init.size)
|
|
636
|
+
return fmt(h + "都不选" + (t ? "," : ""), t, w);
|
|
637
|
+
return t ? fmt(h, t, w) : null;
|
|
638
|
+
}
|
|
639
|
+
if (st.pick && st.pick[0] === "o")
|
|
640
|
+
return fmt(h + d.opts[+st.pick.slice(1)] + (t ? "," : ""), t, w);
|
|
641
|
+
return t ? fmt(h, t, w) : null;
|
|
642
|
+
};
|
|
643
|
+
const put = (L, arr) => {
|
|
644
|
+
const sep = arr.some((v) => v.includes(`
|
|
645
|
+
`));
|
|
646
|
+
arr.forEach((v, k) => {
|
|
647
|
+
if (sep && k)
|
|
648
|
+
L.push("");
|
|
649
|
+
L.push(v);
|
|
650
|
+
});
|
|
651
|
+
return sep;
|
|
652
|
+
};
|
|
653
|
+
function build() {
|
|
654
|
+
const L = [];
|
|
655
|
+
const now = S.dec.filter((x) => x.d.group !== "pending").map(line).filter(Boolean);
|
|
656
|
+
const pend = S.dec.filter((x) => x.d.group === "pending").map(line).filter(Boolean);
|
|
657
|
+
if (now.length || pend.length) {
|
|
658
|
+
L.push("决策:");
|
|
659
|
+
const sep = put(L, now);
|
|
660
|
+
if (pend.length) {
|
|
661
|
+
if (sep || pend.some((v) => v.includes(`
|
|
662
|
+
`)))
|
|
663
|
+
L.push("");
|
|
664
|
+
L.push("--早前尚未决策--");
|
|
665
|
+
put(L, pend);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
const act = S.act.filter((x) => x.st.pick);
|
|
669
|
+
if (act.length) {
|
|
670
|
+
if (L.length)
|
|
671
|
+
L.push("");
|
|
672
|
+
L.push("反馈:");
|
|
673
|
+
put(L, act.map((x) => {
|
|
674
|
+
const t = txt(x.st);
|
|
675
|
+
return fmt("- " + x.a.short + ":" + LAB[x.st.pick] + (t ? ":" : ""), t, 2);
|
|
676
|
+
}));
|
|
677
|
+
}
|
|
678
|
+
const del = S.del.filter((x) => x.st.on).map((x) => x.t.label);
|
|
679
|
+
if (del.length) {
|
|
680
|
+
if (L.length)
|
|
681
|
+
L.push("");
|
|
682
|
+
L.push("交付:" + del.join(","));
|
|
683
|
+
}
|
|
684
|
+
const opt = S.opt.filter((x) => x.st.on);
|
|
685
|
+
if (opt.length) {
|
|
686
|
+
if (L.length)
|
|
687
|
+
L.push("");
|
|
688
|
+
opt.forEach((x) => L.push("也做:" + x.o.text));
|
|
689
|
+
}
|
|
690
|
+
return L.join(`
|
|
691
|
+
`);
|
|
692
|
+
}
|
|
693
|
+
const devs = () => {
|
|
694
|
+
const a = [];
|
|
695
|
+
S.dec.forEach((x) => {
|
|
696
|
+
if (x.changed())
|
|
697
|
+
a.push(x.d.n + " " + x.sum());
|
|
698
|
+
});
|
|
699
|
+
S.act.forEach((x) => {
|
|
700
|
+
if (x.st.pick)
|
|
701
|
+
a.push(x.a.short + ":" + LAB[x.st.pick]);
|
|
702
|
+
});
|
|
703
|
+
S.del.forEach((x) => {
|
|
704
|
+
if (x.st.on !== x.init)
|
|
705
|
+
a.push((x.st.on ? "" : "不") + x.t.label);
|
|
706
|
+
});
|
|
707
|
+
S.opt.forEach((x) => {
|
|
708
|
+
if (x.st.on)
|
|
709
|
+
a.push("也做 " + cut(x.o.text, 10));
|
|
710
|
+
});
|
|
711
|
+
return a;
|
|
712
|
+
};
|
|
713
|
+
const wrap = (t) => t ? "<turn-reply" + (TURN ? ' turn="' + TURN + '"' : "") + `>
|
|
714
|
+
` + t + `
|
|
715
|
+
</turn-reply>` : t;
|
|
716
|
+
const nowDec = S.dec.filter((x) => x.reset && x.d.group === "now");
|
|
717
|
+
let allRec = null;
|
|
718
|
+
if (nowDec.length) {
|
|
719
|
+
allRec = ghost("全部按建议", { icon: "reset", title: "把本轮拍板恢复为建议项" });
|
|
720
|
+
allRec.addEventListener("click", () => nowDec.forEach((x) => x.reset()));
|
|
721
|
+
}
|
|
722
|
+
const cnt = el("span", {});
|
|
723
|
+
const pv = el("button", { type: "button", cls: "lk", "aria-expanded": "false" }, "预览");
|
|
724
|
+
const sum = el("span", { cls: "sum", "aria-live": "polite" }, cnt, pv);
|
|
725
|
+
const pre = el("pre", { cls: "pv" });
|
|
726
|
+
pre.hidden = true;
|
|
727
|
+
const closePre = () => {
|
|
728
|
+
pre.hidden = true;
|
|
729
|
+
pv.textContent = "预览";
|
|
730
|
+
pv.setAttribute("aria-expanded", "false");
|
|
731
|
+
};
|
|
732
|
+
pv.addEventListener("click", () => {
|
|
733
|
+
if (pre.hidden) {
|
|
734
|
+
pre.hidden = false;
|
|
735
|
+
pv.textContent = "收起";
|
|
736
|
+
pv.setAttribute("aria-expanded", "true");
|
|
737
|
+
} else
|
|
738
|
+
closePre();
|
|
739
|
+
});
|
|
740
|
+
const cpLb = el("span", {}, "复制");
|
|
741
|
+
const cp = el("button", { type: "button", cls: "btn" }, cpLb);
|
|
742
|
+
cp.addEventListener("click", () => {
|
|
743
|
+
const t = build();
|
|
744
|
+
if (t) {
|
|
745
|
+
copyText(wrap(t));
|
|
746
|
+
flash(cpLb, "已复制");
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
let go = null;
|
|
750
|
+
if (hasSend) {
|
|
751
|
+
go = el("button", { type: "button", cls: "btn pri" }, "填入输入框");
|
|
752
|
+
go.addEventListener("click", (e) => {
|
|
753
|
+
e.stopPropagation();
|
|
754
|
+
fill();
|
|
755
|
+
});
|
|
756
|
+
} else {
|
|
757
|
+
cp.classList.add("pri");
|
|
758
|
+
}
|
|
759
|
+
const foot = el("div", { cls: "foot" }, allRec, sum, cp, go);
|
|
760
|
+
const fsum = el("span", { cls: "fs" });
|
|
761
|
+
const fx = el("button", { type: "button", cls: "lk" }, "展开");
|
|
762
|
+
const fold = el("div", { cls: "fold" }, el("span", { cls: "mk-ok", "aria-hidden": "true" }), fsum, fx);
|
|
763
|
+
const setFold = (on) => {
|
|
764
|
+
root.classList.toggle("folded", on);
|
|
765
|
+
if (on)
|
|
766
|
+
closePre();
|
|
767
|
+
else if (root.classList.contains("kb") && items[0])
|
|
768
|
+
items[0].__focus();
|
|
769
|
+
};
|
|
770
|
+
fold.addEventListener("click", () => setFold(false));
|
|
771
|
+
const ns = el("div", { cls: "ns", role: "status" }, "宿主报告没有填进输入框。若输入框里已经有这段文字,可忽略;否则点「复制」后手动粘贴。");
|
|
772
|
+
let nsObs = null, nsTimer = 0;
|
|
773
|
+
const watchNotSent = () => {
|
|
774
|
+
if (nsObs)
|
|
775
|
+
nsObs.disconnect();
|
|
776
|
+
clearTimeout(nsTimer);
|
|
777
|
+
root.classList.remove("notsent");
|
|
778
|
+
const b = document.body;
|
|
779
|
+
if (!b || typeof MutationObserver !== "function")
|
|
780
|
+
return;
|
|
781
|
+
const hit = () => {
|
|
782
|
+
nsObs.disconnect();
|
|
783
|
+
clearTimeout(nsTimer);
|
|
784
|
+
root.classList.add("notsent");
|
|
785
|
+
setFold(false);
|
|
786
|
+
};
|
|
787
|
+
nsObs = new MutationObserver(() => {
|
|
788
|
+
if (b.hasAttribute("data-not-sent"))
|
|
789
|
+
hit();
|
|
790
|
+
});
|
|
791
|
+
nsObs.observe(b, { attributes: true, attributeFilter: ["data-not-sent"] });
|
|
792
|
+
nsTimer = setTimeout(() => nsObs.disconnect(), 5000);
|
|
793
|
+
};
|
|
794
|
+
function fill() {
|
|
795
|
+
const t = build();
|
|
796
|
+
if (!t)
|
|
797
|
+
return;
|
|
798
|
+
watchNotSent();
|
|
799
|
+
sendPrompt(wrap(t));
|
|
800
|
+
const dv = devs();
|
|
801
|
+
fsum.textContent = "已填入 · " + (dv.length ? dv.join(" · ") : "全部按建议");
|
|
802
|
+
fsum.title = fsum.textContent;
|
|
803
|
+
setFold(true);
|
|
804
|
+
}
|
|
805
|
+
const primary = () => {
|
|
806
|
+
if (hasSend)
|
|
807
|
+
fill();
|
|
808
|
+
else
|
|
809
|
+
cp.click();
|
|
810
|
+
};
|
|
811
|
+
root.addEventListener("mousedown", () => root.classList.remove("kb"));
|
|
812
|
+
root.addEventListener("keydown", (e) => {
|
|
813
|
+
if (e.key === "Tab" || e.key.startsWith("Arrow") || /^[0-9]$/.test(e.key))
|
|
814
|
+
root.classList.add("kb");
|
|
815
|
+
if (root.classList.contains("folded"))
|
|
816
|
+
return;
|
|
817
|
+
const tg = e.target;
|
|
818
|
+
if (tg.tagName === "TEXTAREA") {
|
|
819
|
+
if (e.isComposing || e.keyCode === 229)
|
|
820
|
+
return;
|
|
821
|
+
if (e.key === "Escape" && tg.__esc) {
|
|
822
|
+
e.preventDefault();
|
|
823
|
+
tg.__esc();
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (e.metaKey || e.ctrlKey || e.altKey)
|
|
828
|
+
return;
|
|
829
|
+
const it = tg.closest && tg.closest(".it");
|
|
830
|
+
if (!it)
|
|
831
|
+
return;
|
|
832
|
+
if (e.key === "/" && it.__note) {
|
|
833
|
+
e.preventDefault();
|
|
834
|
+
it.__note();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (/^[1-9]$/.test(e.key) && it.__pick) {
|
|
838
|
+
e.preventDefault();
|
|
839
|
+
it.__pick(+e.key - 1);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
843
|
+
const i = items.indexOf(it);
|
|
844
|
+
const nx = items[i + (e.key === "ArrowDown" ? 1 : -1)];
|
|
845
|
+
if (nx) {
|
|
846
|
+
e.preventDefault();
|
|
847
|
+
nx.__focus();
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
root.append(fold, bd, foot, ns, pre);
|
|
852
|
+
function upd() {
|
|
853
|
+
const t = build();
|
|
854
|
+
const n = count(t);
|
|
855
|
+
const dv = devs();
|
|
856
|
+
S.dec.forEach((x) => x.it.classList.toggle("chg", x.changed()));
|
|
857
|
+
S.act.forEach((x) => x.it.classList.toggle("chg", !!x.st.pick));
|
|
858
|
+
if (S.delIt)
|
|
859
|
+
S.delIt.classList.toggle("chg", S.del.some((x) => x.st.on !== x.init));
|
|
860
|
+
S.opt.forEach((x) => x.it.classList.toggle("chg", x.st.on));
|
|
861
|
+
const parts = [];
|
|
862
|
+
parts.push(dv.length ? "改了 " : "全部按建议", ...dv.length ? [el("b", {}, String(dv.length)), " 项"] : []);
|
|
863
|
+
if (n)
|
|
864
|
+
parts.push(" · 将填入 ", el("b", {}, String(n)), " 行");
|
|
865
|
+
else
|
|
866
|
+
parts.push(" · 没有要回复的内容");
|
|
867
|
+
cnt.replaceChildren(...parts);
|
|
868
|
+
pre.textContent = wrap(t);
|
|
869
|
+
pv.hidden = !n;
|
|
870
|
+
if (!n)
|
|
871
|
+
closePre();
|
|
872
|
+
cp.disabled = !n;
|
|
873
|
+
if (go)
|
|
874
|
+
go.disabled = !n;
|
|
875
|
+
if (allRec)
|
|
876
|
+
allRec.disabled = nowDec.every((x) => !x.changed());
|
|
877
|
+
}
|
|
878
|
+
upd();
|
|
879
|
+
const cw = {};
|
|
880
|
+
const dbs = [...root.querySelectorAll(".dseg button.sg")];
|
|
881
|
+
dbs.forEach((b) => {
|
|
882
|
+
const c = b.dataset.col;
|
|
883
|
+
cw[c] = Math.max(cw[c] || 0, b.offsetWidth);
|
|
884
|
+
});
|
|
885
|
+
dbs.forEach((b) => {
|
|
886
|
+
b.style.setProperty("width", Math.ceil(cw[b.dataset.col]) + 12 + "px", "important");
|
|
887
|
+
});
|
|
888
|
+
root.classList.add("measuring");
|
|
889
|
+
let m = 0;
|
|
890
|
+
root.querySelectorAll(".q").forEach((q) => {
|
|
891
|
+
m = Math.max(m, q.offsetWidth);
|
|
892
|
+
});
|
|
893
|
+
root.classList.remove("measuring");
|
|
894
|
+
if (m > 0)
|
|
895
|
+
root.style.setProperty("--qw", Math.min(Math.ceil(m) + 2, 260) + "px");
|
|
896
|
+
}
|
|
897
|
+
return render;
|
|
898
|
+
})();
|
|
899
|
+
|
|
900
|
+
// src/index.js
|
|
901
|
+
var version = "0.1.1";
|
|
902
|
+
var STYLE_ID = "trw-style-" + version;
|
|
903
|
+
function injectCSS(root) {
|
|
904
|
+
const d = root.ownerDocument || document;
|
|
905
|
+
if (d.getElementById(STYLE_ID))
|
|
906
|
+
return;
|
|
907
|
+
const s = d.createElement("style");
|
|
908
|
+
s.id = STYLE_ID;
|
|
909
|
+
s.textContent = css_default;
|
|
910
|
+
(d.head || root.parentNode || d.documentElement).appendChild(s);
|
|
911
|
+
}
|
|
912
|
+
function fail(root, msg) {
|
|
913
|
+
root.replaceChildren();
|
|
914
|
+
root.classList.remove("folded", "kb");
|
|
915
|
+
const p = (root.ownerDocument || document).createElement("div");
|
|
916
|
+
p.className = "err";
|
|
917
|
+
p.setAttribute("role", "alert");
|
|
918
|
+
p.title = msg;
|
|
919
|
+
p.textContent = msg;
|
|
920
|
+
root.append(p);
|
|
921
|
+
}
|
|
922
|
+
var TAIL = ",请直接在输入框回复";
|
|
923
|
+
function render(root, spec) {
|
|
924
|
+
if (!root || typeof root.append !== "function") {
|
|
925
|
+
console.error("[turn-reply] root 不是元素");
|
|
926
|
+
return ["root 不是元素"];
|
|
927
|
+
}
|
|
928
|
+
root.classList.add("trw");
|
|
929
|
+
injectCSS(root);
|
|
930
|
+
let s = spec;
|
|
931
|
+
if (typeof s === "string") {
|
|
932
|
+
try {
|
|
933
|
+
s = JSON.parse(s);
|
|
934
|
+
} catch (x) {
|
|
935
|
+
fail(root, "面板数据不是合法 JSON" + TAIL);
|
|
936
|
+
return ["JSON 解析失败"];
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
const errs = validate(s);
|
|
940
|
+
if (errs.length) {
|
|
941
|
+
console.error(`[turn-reply] spec 不合法:
|
|
942
|
+
` + errs.join(`
|
|
943
|
+
`));
|
|
944
|
+
fail(root, "面板数据不合法(" + errs.length + " 处,首处 " + errs[0] + ")" + TAIL);
|
|
945
|
+
return errs;
|
|
946
|
+
}
|
|
947
|
+
try {
|
|
948
|
+
renderPanel(root, s);
|
|
949
|
+
} catch (x) {
|
|
950
|
+
console.error("[turn-reply] 渲染出错", x);
|
|
951
|
+
const m = "面板渲染出错(" + (x && x.message || x) + ")" + TAIL;
|
|
952
|
+
fail(root, m);
|
|
953
|
+
return [m];
|
|
954
|
+
}
|
|
955
|
+
return [];
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// src/entry.js
|
|
959
|
+
var g = typeof window !== "undefined" ? window : globalThis;
|
|
960
|
+
if (!g.TR || g.TR.version !== version)
|
|
961
|
+
g.TR = { render, validate, version, css: css_default };
|
|
962
|
+
})();
|
package/dist/tr.min.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*! @npmjsdnt/turn-reply@0.1.1 turn-reply panel */
|
|
2
|
+
(()=>{var rt=".trw{--c-bg:var(--surface-2);--c-sunken:var(--surface-1);--c-hover:var(--fill-ghost-hover,var(--surface-1));--c-soft:var(--fill-ghost-selected,var(--surface-1));--c-solid:var(--fill-primary,var(--text-primary));--c-on-solid:var(--on-primary,var(--surface-2));--c-line:var(--border);--c-line-s:var(--border-strong);--c-line-x:var(--border-stronger,var(--border-strong));--c-ink1:var(--text-primary);--c-ink2:var(--text-secondary);--c-ink3:var(--text-muted);--c-ink4:var(--text-disabled,var(--text-muted));--c-attn:var(--text-warning);--c-ok:var(--text-success);--c-focus:var(--border-accent,var(--text-accent));--c-r:6px;--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 5%,transparent);--c-lift:var(--surface-3,var(--surface-2));--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 4%,transparent);container-type:inline-size;position:relative;box-sizing:border-box;font-family:var(--font-sans);font-size:13px;line-height:20px;font-weight:400;font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;color:var(--c-ink1);background:transparent;border:0;border-radius:0;padding:0}.trw.dark{--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 15%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent)}.trw *,.trw *::before,.trw *::after{box-sizing:border-box}.trw [hidden]{display:none!important}.trw .mono{font-family:var(--font-mono);font-size:12px}.trw .grp{display:flex;align-items:flex-start;padding:6px 0;border-top:0.5px solid var(--c-line)}.trw .bd>.grp:first-child{border-top:0}.trw .gl{flex:0 0 60px;padding-top:6px;font-size:11px;line-height:16px;color:var(--c-ink2);white-space:nowrap;cursor:default}.trw .rs{flex:1 1 auto;min-width:0}.trw .it{margin:0 -6px;padding:0 6px;border-radius:0}.trw .it:hover,.trw.kb .it:focus-within{background:var(--c-hover)}.trw .ln{display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;min-height:28px}.trw .bd>.grp:first-child .it:first-child .ln{padding-right:40px}.trw .q{flex:0 0 var(--qw,240px);min-width:0;display:flex;align-items:center;gap:6px;white-space:nowrap;overflow:hidden}.trw.measuring .q{flex:none!important;width:max-content!important}.trw .qt{min-width:0;overflow:hidden;text-overflow:ellipsis}.trw .n{flex:none;min-width:14px;color:var(--c-ink2)}.trw .it.core .qt{font-weight:500}.trw .q.info .qt{font-size:12px;color:var(--c-ink2)}.trw a.tk{font-family:var(--font-mono);font-size:12px;color:var(--c-ink1);text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:2px}.trw .ctl{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;padding:0 4px;margin:0 -4px}.trw .cb{flex:none;width:9px;height:9px;background:var(--c-attn);clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}.trw .seg{display:inline-flex;align-items:center;gap:2px;height:26px;padding:2px;border:0;border-radius:7px;background:var(--c-track)}.trw button.sg{position:relative;font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:20px!important;height:22px!important;min-height:0!important;margin:0!important;padding:0 9px!important;border:0!important;border-radius:5px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap;text-align:center;justify-content:center}.trw button.sg[aria-checked=\"false\"]:hover{background:var(--c-hov)!important;color:var(--c-ink1)!important}.trw .ck.rec>span::before{content:\"\";display:inline-block;width:4px;height:4px;border-radius:50%;background:currentColor;margin-right:5px;vertical-align:2px}.trw button.sg.rec{padding:0 4px 0 14px!important}.trw button.sg.rec::before{content:\"\";position:absolute;left:6px;top:50%;width:4px;height:4px;margin-top:-2px;border-radius:50%;background:currentColor;opacity:.4}.trw button.sg.sk{color:var(--c-ink3)!important}.trw button.sg[aria-checked=\"true\"]{background:var(--c-lift)!important;color:var(--c-ink1)!important;box-shadow:none!important}.trw .dseg button.sg[aria-checked=\"true\"]:hover::before{content:\"×\";position:absolute;left:4px;top:0;width:8px;height:auto;margin:0;border-radius:0;background:none;line-height:22px;text-align:center;opacity:1;color:var(--c-ink3)}.trw .k{display:none;position:absolute;top:-8px;right:-4px;font:11px/14px var(--font-mono);padding:0 3px;border-radius:4px;background:var(--c-bg);color:var(--c-ink3);border:0.5px solid var(--c-line-s);pointer-events:none;z-index:1}.trw.kb .it:focus-within .k{display:block}.trw .ck{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 6px;border-radius:var(--c-r);font-size:12px;color:var(--c-ink2);cursor:pointer;white-space:nowrap;user-select:none}.trw .ck:hover{background:var(--c-hover);color:var(--c-ink1)}.trw .ck input{width:13px;height:13px;margin:0;accent-color:var(--c-solid);cursor:pointer}.trw .ck:has(input:checked){color:var(--c-ink1)}.trw button.gh{display:inline-flex;align-items:center;gap:5px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:26px!important;min-height:0!important;margin:0!important;padding:0 8px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}.trw button.gh:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}.trw svg.i{width:14px;height:14px;flex:none;fill:none;stroke:currentColor;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}.trw .ed{display:flex;padding:4px 0 8px calc(var(--qw,240px) + 8px)}.trw .it:last-child>.ed{padding-bottom:4px}.trw .ed textarea,.trw textarea.ask{display:block;flex:1;min-width:0;min-height:28px;height:28px;resize:none!important;overflow:hidden;margin:0!important;padding:4px 10px!important;font:12px/18px var(--font-sans,system-ui)!important;white-space:pre-wrap;overflow-wrap:anywhere;border:0.5px solid var(--c-line-s)!important;border-radius:var(--c-r)!important;background:var(--c-bg)!important;color:var(--c-ink1)!important;box-shadow:none!important}.trw .ed textarea::placeholder,.trw textarea.ask::placeholder{color:var(--c-ink3)}.trw textarea.ask{flex:1 1 160px;min-height:26px;height:26px;padding:3px 10px!important}.trw .ln.top{align-items:flex-start}.trw .ln.top .q{min-height:26px}.trw .foot{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;padding:8px 0 2px;border-top:0.5px solid var(--c-line)}.trw .sum{flex:1 1 auto;display:inline-flex;align-items:center;gap:8px;font-size:12px;line-height:18px;color:var(--c-ink3);white-space:nowrap}.trw .sum b{font-weight:500;color:var(--c-ink2)}.trw button.lk{font-family:inherit!important;font-size:12px!important;font-weight:400!important;line-height:18px!important;height:auto!important;min-height:0!important;margin:0!important;padding:0 2px!important;border:0!important;border-radius:4px!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;text-decoration:underline;text-decoration-color:var(--c-line-x);text-underline-offset:3px}.trw button.btn{display:inline-flex;align-items:center;gap:6px;font-family:inherit!important;font-size:12px!important;font-weight:500!important;line-height:20px!important;height:28px!important;min-height:0!important;margin:0!important;padding:0 12px!important;border:0.5px solid transparent!important;border-radius:var(--c-r)!important;background:transparent!important;color:var(--c-ink2)!important;box-shadow:none!important;cursor:pointer;white-space:nowrap}.trw button.btn:hover{background:var(--c-hover)!important;color:var(--c-ink1)!important}.trw button.btn.pri{border-color:var(--c-line-x)!important;color:var(--c-ink1)!important}.trw button.btn:disabled,.trw button.gh:disabled{color:var(--c-ink4)!important;background:transparent!important;cursor:default}.trw pre.pv{font:12px/18px var(--font-mono);white-space:pre-wrap;overflow-wrap:anywhere;background:var(--c-sunken);padding:8px 10px;border-radius:var(--c-r);margin:0 0 8px;color:var(--c-ink2)}.trw .fold{display:none;align-items:center;gap:6px;min-height:34px;font-size:12px;color:var(--c-ink2);cursor:pointer;padding:0 40px 0 6px;margin:0 -6px}.trw .fold .fs{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trw .mk-ok{flex:none;width:10px;height:8px;background:var(--c-ok);clip-path:polygon(0 56%,15% 38%,38% 62%,85% 0,100% 19%,38% 100%)}.trw.folded .fold{display:flex}.trw.folded>:not(.fold){display:none!important}.trw.folded:hover{background:var(--c-hover);border-radius:0}.trw button:focus-visible{outline:2px solid var(--c-focus)!important;outline-offset:-2px}.trw .ed textarea:focus,.trw textarea.ask:focus{outline:none!important;border-color:var(--c-focus)!important;box-shadow:inset 0 0 0 1px var(--c-focus)!important}@container (max-width:480px){.trw .grp{display:block}.trw .ln{flex-wrap:wrap;row-gap:2px;padding-bottom:2px}.trw .q{flex:1 1 auto;white-space:normal}.trw .ed{padding-left:0}}.trw .err{padding:6px 0;font-size:12px;line-height:18px;color:var(--c-attn);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.trw.dark{--c-track:color-mix(in srgb,var(--c-ink1,currentColor) 8%,transparent);--c-lift:color-mix(in srgb,var(--c-ink1,currentColor) 26%,transparent);--c-hov:color-mix(in srgb,var(--c-ink1,currentColor) 12%,transparent)}.trw.dark button.sg.rec::before{opacity:.75}.trw.dark button.sg[aria-checked=\"true\"]{font-weight:500!important}.trw .ns{display:none;font-size:12px;line-height:18px;color:var(--c-attn);padding:6px 0 0}.trw.notsent .ns{display:block}";var z=(n,l=1/0,m=1)=>typeof n==="string"&&n.length>=m&&n.length<=l,H=(n,l,m,y)=>{for(let s of Object.keys(n))if(!l.includes(s))y.push(`${m}.${s} 未定义的字段`)};function ot(n){let l=[];if(!n||typeof n!=="object"||Array.isArray(n))return["$ 不是对象"];if(H(n,["v","turn","ticketUrl","decisions","actions","delivery","optional"],"$",l),n.v!==1)l.push("$.v 必须为 1");if(!z(n.turn,40)||!/^[\w.:-]+$/.test(n.turn))l.push("$.turn 缺失或含非法字符");if(n.ticketUrl!==void 0&&!(z(n.ticketUrl)&&n.ticketUrl.startsWith("https://")))l.push("$.ticketUrl 须为 https:// 前缀");let m=new Set;(n.decisions??[]).forEach((s,x)=>{let f=`$.decisions[${x}]`;if(!s||typeof s!=="object")return l.push(`${f} 不是对象`);if(H(s,["n","group","core","q","opts","rec","def","multi","ask","ph"],f,l),!Number.isInteger(s.n)||s.n<1)l.push(`${f}.n 须为正整数`);else if(m.has(s.n))l.push(`${f}.n=${s.n} 重复`);else m.add(s.n);if(!["now","defaulted","pending"].includes(s.group))l.push(`${f}.group 非法`);if(!z(s.q,40))l.push(`${f}.q 须为 1–40 字`);if(s.core!==void 0&&typeof s.core!=="boolean")l.push(`${f}.core 须为布尔`);if(s.def!==void 0&&!z(s.def,60))l.push(`${f}.def 须为 ≤60 字`);if(s.multi!==void 0&&s.multi!==!0)l.push(`${f}.multi 只能为 true`);if(s.ask!==void 0&&s.ask!==!0)l.push(`${f}.ask 只能为 true`);let T=s.opts;if(T!==void 0&&!(Array.isArray(T)&&T.every((D)=>z(D,12))))l.push(`${f}.opts 须为 1–12 字的字符串数组`);let N=Array.isArray(T)?T.length:0,I=s.group==="defaulted"?"defaulted":s.ask?"ask":s.multi?"multi":"one";if(s.ask&&s.multi)l.push(`${f} ask 与 multi 不能同时出现`);if(I==="defaulted"&&(T||s.rec!==void 0||s.ask||s.multi))l.push(`${f} defaulted 条目只有「推翻」,不带 opts/rec/ask/multi`);if(I==="ask"&&(T||s.rec!==void 0))l.push(`${f} ask 条目不带 opts/rec`);if(s.ph!==void 0&&(I!=="ask"||!z(s.ph,40)))l.push(`${f}.ph 只用于 ask,≤40 字`);if(I==="one"&&(N<2||N>4))l.push(`${f} 单选须有 2–4 个 opts(现 ${N})`);if(I==="multi"&&(N<2||N>8))l.push(`${f} 多选须有 2–8 个 opts(现 ${N})`);if(I==="one"&&s.rec!==void 0&&!(Number.isInteger(s.rec)&&s.rec>=0&&s.rec<N))l.push(`${f}.rec 须为 opts 下标`);if(I==="multi"&&s.rec!==void 0&&!(Array.isArray(s.rec)&&s.rec.every((D)=>Number.isInteger(D)&&D>=0&&D<N)&&new Set(s.rec).size===s.rec.length))l.push(`${f}.rec 须为不重复的 opts 下标数组`);if(Array.isArray(T)&&new Set(T).size!==N)l.push(`${f}.opts 有重复`)});let y=[...m].sort((s,x)=>s-x);if(y.length&&y.some((s,x)=>x&&s!==y[x-1]+1))l.push(`$.decisions 编号不连续:${y.join(",")}`);if((n.actions??[]).forEach((s,x)=>{let f=`$.actions[${x}]`;if(!s||typeof s!=="object")return l.push(`${f} 不是对象`);if(H(s,["text","short","cmd"],f,l),!z(s.text,120))l.push(`${f}.text 须为 1–120 字`);if(!z(s.short,24))l.push(`${f}.short 须为 1–24 字`);if(s.cmd!==void 0&&!z(s.cmd))l.push(`${f}.cmd 须为非空字符串`)}),n.delivery!==void 0){let s=n.delivery;if(!s||typeof s!=="object")l.push("$.delivery 不是对象");else{if(H(s,["info","toggles"],"$.delivery",l),s.info!==void 0&&!z(s.info,120))l.push("$.delivery.info 须为 ≤120 字");(s.toggles??[]).forEach((x,f)=>{if(H(x??{},["label","rec"],`$.delivery.toggles[${f}]`,l),!z(x?.label,20))l.push(`$.delivery.toggles[${f}].label 须为 1–20 字`);if(x?.rec!==void 0&&typeof x.rec!=="boolean")l.push(`$.delivery.toggles[${f}].rec 须为布尔`)})}}(n.optional??[]).forEach((s,x)=>{if(H(s??{},["text"],`$.optional[${x}]`,l),!z(s?.text,120))l.push(`$.optional[${x}].text 须为 1–120 字`)});for(let s of["decisions","actions","optional"])if(n[s]!==void 0&&!Array.isArray(n[s]))l.push(`$.${s} 须为数组`);return l}var Nt=(()=>{let n=(p,h={},...E)=>{let w=document.createElement(p);for(let[g,_]of Object.entries(h)){if(_==null||_===!1)continue;if(g==="cls")w.className=_;else w.setAttribute(g,_)}return E.flat().forEach((g)=>{if(g!=null&&g!==!1)w.append(g)}),w},l={copy:'<rect x="5.5" y="5.5" width="7.5" height="7.5" rx="1.2"/><path d="M10.5 3.5V3a.5.5 0 0 0-.5-.5H3a.5.5 0 0 0-.5.5v7a.5.5 0 0 0 .5.5h.5"/>',reset:'<path d="M3.2 8a4.8 4.8 0 1 0 1.4-3.4"/><path d="M3.2 2.6v2.6h2.6"/>'},m=(p)=>{let h=document.createElementNS("http://www.w3.org/2000/svg","svg");return h.setAttribute("viewBox","0 0 16 16"),h.setAttribute("class","i"),h.setAttribute("aria-hidden","true"),h.innerHTML=l[p],h},y=null,s=(p)=>{if(!p)return[];let h=[],E=0;return p.replace(/\b[A-Z]{2,6}-\d+\b|\b[0-9a-f]{7,40}\b/g,(w,g)=>{let _=/^[A-Z]/.test(w);if(!_&&!(/\d/.test(w)&&/[a-f]/.test(w)))return w;if(_&&!y)return w;return h.push(p.slice(E,g),_?n("a",{cls:"tk",href:y+w,target:"_blank",rel:"noopener"},w):n("span",{cls:"mono"},w.slice(0,7))),E=g+w.length,w}),h.push(p.slice(E)),h},x=(p)=>{let h=()=>{let E=n("textarea",{style:"position:absolute;left:-9999px;top:0","aria-hidden":"true"});E.value=p,document.body.append(E),E.select();try{document.execCommand("copy")}catch(w){}E.remove()};try{navigator.clipboard.writeText(p).catch(h)}catch(E){h()}},f=(p,h)=>{if(!p.dataset.l)p.dataset.l=p.textContent;p.textContent=h,clearTimeout(p._t),p._t=setTimeout(()=>{p.textContent=p.dataset.l},1400)},T=(p)=>p?p.split(`
|
|
3
|
+
`).filter(Boolean).length:0,N=(p,h)=>p.length>h?p.slice(0,h)+"…":p,I=(p,h)=>p.size===h.size&&[...p].every((E)=>h.has(E)),D={done:"已做",issue:"有问题",no:"没做",you:"你来做"};function Ot(p,h){p.replaceChildren(),p.classList.remove("folded","kb");{let t=(getComputedStyle(p).color.match(/\d+/g)||[0,0,0]).slice(0,3).map(Number);p.classList.toggle("dark",(t[0]*299+t[1]*587+t[2]*114)/1000>140)}y=h.ticketUrl||null;let E=h.turn?String(h.turn).replace(/[^\w.:-]/g,""):"",w=typeof sendPrompt==="function",g={dec:[],act:[],del:[],opt:[]},_=[],wt=n("div",{cls:"bd"}),st=(t,e)=>{let o=n("div",{cls:"rs"});return wt.append(n("div",{cls:"grp"},n("div",{cls:"gl",title:e||null},t),o)),o},X=(t,e,o)=>{let r=n("div",{cls:"it"},e,...o||[]);return t.append(r),_.push(r),r},Y=(t,e)=>{let o=n("span",{cls:"qt"},...s(e));return{q:n("div",{cls:"q",title:e||null},t&&t.n!=null?n("span",{cls:"n"},t.n+"."):null,t&&t.core?n("span",{cls:"cb",role:"img","aria-label":"核心决策",title:"核心决策"}):null,o),qt:o}},ct=(t)=>{if(!t.value){t.style.removeProperty("height");return}t.style.setProperty("height","auto","important"),t.style.setProperty("height",t.scrollHeight+(t.offsetHeight-t.clientHeight)+"px","important")},kt=(t,e)=>{let o=n("textarea",{rows:"1",cls:e||null,placeholder:t,"aria-label":t});return o.addEventListener("input",()=>ct(o)),o},G=(t,e)=>{let o=n("div",{cls:"ed"}),r=kt(e);return r.addEventListener("input",()=>{t.text=r.value,O()}),o.append(r),o.hidden=!0,t.ed=o,{box:o,i:r,show(a,i){if(o.hidden=!a,a){if(ct(r),i)r.focus()}O()}}},U=(t)=>t.ed&&!t.ed.hidden?(t.text||"").trim():"",yt=(t,e,o={})=>{let r=o.init===void 0?null:o.init,a=n("div",{cls:"seg"+(o.cls?" "+o.cls:""),role:"radiogroup","aria-label":o.label||null,title:o.title||null}),i=t.map((u,b)=>n("button",{type:"button",role:"radio",cls:"sg"+(u.rec?" rec":""),"data-col":String(b),title:u.title||null},u.label,n("span",{cls:"k","aria-hidden":"true"},String(b+1)))),c=()=>{let u=t.findIndex((L)=>L.v===e.pick),b=e.pick!==r;i.forEach((L,M)=>{L.setAttribute("aria-checked",String(M===u)),L.classList.toggle("chg",M===u&&b),L.tabIndex=M===(u<0?0:u)?0:-1})},d=(u,b)=>{if(e.pick=u,c(),o.onChange)o.onChange(b);O()},v=(u,b)=>{if(t[u])d(o.toggle&&e.pick===t[u].v?null:t[u].v,b)};return i.forEach((u,b)=>u.addEventListener("click",()=>v(b,!1))),a.addEventListener("keydown",(u)=>{if(u.key!=="ArrowRight"&&u.key!=="ArrowLeft")return;u.preventDefault();let L=(Math.max(0,i.indexOf(document.activeElement))+(u.key==="ArrowRight"?1:-1)+i.length)%i.length;d(t[L].v,!0),i[L].focus()}),c(),a.append(...i),{w:a,set:d,choose:v,focus:()=>(i.find((u)=>u.tabIndex===0)||i[0]).focus()}},Q=(t,e,o={})=>{let r=n("input",{type:"checkbox"});return r.checked=!!e,{lb:n("label",{cls:"ck"+(o.rec?" rec":""),title:o.title||null},r,n("span",{},t)),i:r}},Mt=(t,e,o,r)=>{let a=n("div",{cls:"ctl",role:"group"}),i=t.map((d,v)=>Q(d,r.has(v),{rec:e.includes(v),title:e.includes(v)?"建议":null})),c=()=>i.forEach((d,v)=>d.lb.classList.toggle("chg",o.sel.has(v)!==r.has(v)));return i.forEach((d,v)=>d.i.addEventListener("change",()=>{d.i.checked?o.sel.add(v):o.sel.delete(v),c(),O()})),a.append(...i.map((d)=>d.lb)),{w:a,set:(d)=>{o.sel=new Set(d),i.forEach((v,u)=>{v.i.checked=o.sel.has(u)}),c(),O()},toggle:(d)=>{if(i[d])i[d].i.click()},focus:()=>i[0]&&i[0].i.focus()}},Et=(t,e={})=>n("button",{type:"button",cls:"gh",title:e.title||null},e.icon?m(e.icon):null,t?n("span",{},t):null);if([["本轮拍板","需要你拍板",(t)=>t.group==="now"],["早前","上轮按默认处理(勾「推翻」才回复)与早前尚未决策(默认不回复)",(t)=>t.group!=="now"]].forEach(([t,e,o])=>{let r=(h.decisions||[]).filter(o).sort((i,c)=>i.n-c.n);if(!r.length)return;let a=st(t,e);r.forEach((i)=>{let c={pick:null,text:""},d={d:i,st:c};g.dec.push(d);let{q:v}=Y(i,i.q),u=[],b,L,M,V=null;if(i.group==="defaulted"){let k=G(c,"改成什么"),A=Q("推翻",!1,{title:"推翻上轮的默认处理"});A.i.addEventListener("change",()=>{c.pick=A.i.checked?"over":null,A.lb.classList.toggle("chg",A.i.checked),k.show(A.i.checked,!0)}),b=n("div",{cls:"ctl"},A.lb),u.push(k.box),L=(S)=>{if(S===0)A.i.click()},M=()=>A.i.focus(),k.i.__esc=()=>A.i.focus(),d.changed=()=>c.pick==="over",d.sum=()=>"推翻"}else if(i.ask){let k=kt(i.ph||"填写;要附文件就留空,到输入框里附","ask");k.addEventListener("input",()=>{c.text=k.value,O()}),b=k,d.top=!0,L=()=>{},M=()=>k.focus(),d.changed=()=>!!(c.text||"").trim(),d.sum=()=>"已填"}else if(i.multi){let k=i.opts||[],A=Array.isArray(i.rec)?i.rec:i.rec!=null?[i.rec]:[],S=new Set(i.group==="now"?A:[]);c.sel=new Set(S),d.init=S;let C=Mt(k,A,c,S),q=G(c,"附一句;不勾选项时即为你的回复");V=()=>{let j=q.box.hidden;if(q.show(j,!0),!j)C.focus()},b=C.w,u.push(q.box),d.reset=()=>C.set(S),L=(j)=>C.toggle(j),M=C.focus,q.i.__esc=()=>{q.show(!1),C.focus()},d.changed=()=>!I(c.sel,S)||!!U(c),d.sum=()=>{let j=[...c.sel].sort((mt,Ft)=>mt-Ft).map((mt)=>k[mt]);return(j.length?j.join("、"):"都不选")+(U(c)?"(附言)":"")}}else{let k=i.opts||[],A=k.map((q,j)=>({v:"o"+j,label:q,rec:j===i.rec,title:j===i.rec?"建议;再点一次即不回复":"再点一次即不回复"}));c.pick=i.group==="now"&&k[i.rec]!=null?"o"+i.rec:null,d.init=c.pick;let S=yt(A,c,{label:i.q,init:d.init,toggle:!0,cls:"dseg",title:i.def||null}),C=G(c,"附一句;不选选项时即为你的回复");V=()=>{let q=C.box.hidden;if(C.show(q,!0),!q)S.focus()},b=n("div",{cls:"ctl"},S.w),u.push(C.box),d.reset=()=>S.set(d.init),L=(q)=>S.choose(q,!1),M=S.focus,C.i.__esc=()=>{C.show(!1),S.focus()},d.changed=()=>c.pick!==d.init||!!U(c),d.sum=()=>(c.pick?k[+c.pick.slice(1)]:"不回复")+(U(c)?"(附言)":"")}let Tt=n("div",{cls:"ln"+(d.top?" top":"")},v,b),Z=X(a,Tt,u);if(V)Tt.addEventListener("click",(k)=>{if(k.target.closest("button,input,textarea,label,a,.ctl"))return;V()}),Z.__note=V;if(i.core)Z.classList.add("core");Z.__pick=L,Z.__focus=M,d.it=Z})}),h.actions&&h.actions.length){let t=st("你要做的","做完后在这里回执");h.actions.forEach((e)=>{let o={pick:null,text:""},r=G(o,"贴结果、报错或原因"),a=[["done","已做"],["issue","有问题"],["no","没做"],["you","你来做"]].map(([u,b])=>({v:u,label:b})),i=yt(a,o,{toggle:!0,init:null,label:e.short||e.text,onChange:(u)=>{let b=o.pick==="issue"||o.pick==="no";if(r.box.hidden=!b,b){if(ct(r.i),!u)r.i.focus()}}}),c=null;if(e.cmd){c=Et("复制命令",{icon:"copy",title:e.cmd});let u=c.querySelector("span");c.addEventListener("click",()=>{x(e.cmd),f(u,"已复制")})}let{q:d}=Y(null,e.text),v=X(t,n("div",{cls:"ln"},d,n("div",{cls:"ctl"},i.w,c)),[r.box]);v.__pick=(u)=>i.choose(u,!1),v.__focus=i.focus,r.i.__esc=i.focus,g.act.push({a:e,st:o,it:v})})}let tt=h.delivery,$t=h.optional||[];if(tt||$t.length){let t=st("收尾");if(tt){let e=[];(tt.toggles||[]).forEach((a)=>{let i={t:a,init:!!a.rec,st:{on:!!a.rec}},c=Q(a.label,i.init,{rec:a.rec,title:a.rec?"建议;取消勾选即不表态":"勾选才表态"});c.i.addEventListener("change",()=>{i.st.on=c.i.checked,c.lb.classList.toggle("chg",i.st.on!==i.init),O()}),g.del.push(i),e.push(c)});let{q:o}=Y(null,tt.info||"");o.classList.add("info");let r=X(t,n("div",{cls:"ln"},o,n("div",{cls:"ctl"},...e.map((a)=>a.lb))));r.__pick=(a)=>{if(e[a])e[a].i.click()},r.__focus=()=>{if(e[0])e[0].i.focus()},g.delIt=r}$t.forEach((e)=>{let o={on:!1},r=Q("也做",!1);r.i.addEventListener("change",()=>{o.on=r.i.checked,r.lb.classList.toggle("chg",o.on),O()});let{q:a}=Y(null,e.text),i=X(t,n("div",{cls:"ln"},a,n("div",{cls:"ctl"},r.lb)));i.__pick=(c)=>{if(c===0)r.i.click()},i.__focus=()=>r.i.focus(),g.opt.push({o:e,st:o,it:i})})}let P=(t,e,o)=>{let r=String(e).split(`
|
|
4
|
+
`).map((i)=>i.replace(/\s+$/,""));while(r.length&&!r[0])r.shift();while(r.length&&!r[r.length-1])r.pop();if(r=r.filter((i,c)=>i||r[c-1]),r.length<=1)return t+(r[0]||"");let a=" ".repeat(o);return t+r[0]+`
|
|
5
|
+
`+r.slice(1).map((i)=>a+i).join(`
|
|
6
|
+
`)},_t=(t)=>{let{d:e,st:o}=t,r=U(o),a=e.n+". ",i=a.length;if(e.ask){let c=(o.text||"").trim();return c?P(a,c,i):null}if(e.group==="defaulted")return o.pick==="over"?P(a+"推翻:",r||"(待补充)",i):null;if(e.multi){let c=[...o.sel].sort((d,v)=>d-v).map((d)=>e.opts[d]);if(c.length)return P(a+c.join("、")+(r?",":""),r,i);if(t.init.size)return P(a+"都不选"+(r?",":""),r,i);return r?P(a,r,i):null}if(o.pick&&o.pick[0]==="o")return P(a+e.opts[+o.pick.slice(1)]+(r?",":""),r,i);return r?P(a,r,i):null},at=(t,e)=>{let o=e.some((r)=>r.includes(`
|
|
7
|
+
`));return e.forEach((r,a)=>{if(o&&a)t.push("");t.push(r)}),o};function lt(){let t=[],e=g.dec.filter((c)=>c.d.group!=="pending").map(_t).filter(Boolean),o=g.dec.filter((c)=>c.d.group==="pending").map(_t).filter(Boolean);if(e.length||o.length){t.push("决策:");let c=at(t,e);if(o.length){if(c||o.some((d)=>d.includes(`
|
|
8
|
+
`)))t.push("");t.push("--早前尚未决策--"),at(t,o)}}let r=g.act.filter((c)=>c.st.pick);if(r.length){if(t.length)t.push("");t.push("反馈:"),at(t,r.map((c)=>{let d=U(c.st);return P("- "+c.a.short+":"+D[c.st.pick]+(d?":":""),d,2)}))}let a=g.del.filter((c)=>c.st.on).map((c)=>c.t.label);if(a.length){if(t.length)t.push("");t.push("交付:"+a.join(","))}let i=g.opt.filter((c)=>c.st.on);if(i.length){if(t.length)t.push("");i.forEach((c)=>t.push("也做:"+c.o.text))}return t.join(`
|
|
9
|
+
`)}let Lt=()=>{let t=[];return g.dec.forEach((e)=>{if(e.changed())t.push(e.d.n+" "+e.sum())}),g.act.forEach((e)=>{if(e.st.pick)t.push(e.a.short+":"+D[e.st.pick])}),g.del.forEach((e)=>{if(e.st.on!==e.init)t.push((e.st.on?"":"不")+e.t.label)}),g.opt.forEach((e)=>{if(e.st.on)t.push("也做 "+N(e.o.text,10))}),t},pt=(t)=>t?"<turn-reply"+(E?' turn="'+E+'"':"")+`>
|
|
10
|
+
`+t+`
|
|
11
|
+
</turn-reply>`:t,dt=g.dec.filter((t)=>t.reset&&t.d.group==="now"),J=null;if(dt.length)J=Et("全部按建议",{icon:"reset",title:"把本轮拍板恢复为建议项"}),J.addEventListener("click",()=>dt.forEach((t)=>t.reset()));let At=n("span",{}),R=n("button",{type:"button",cls:"lk","aria-expanded":"false"},"预览"),Dt=n("span",{cls:"sum","aria-live":"polite"},At,R),B=n("pre",{cls:"pv"});B.hidden=!0;let ut=()=>{B.hidden=!0,R.textContent="预览",R.setAttribute("aria-expanded","false")};R.addEventListener("click",()=>{if(B.hidden)B.hidden=!1,R.textContent="收起",R.setAttribute("aria-expanded","true");else ut()});let St=n("span",{},"复制"),W=n("button",{type:"button",cls:"btn"},St);W.addEventListener("click",()=>{let t=lt();if(t)x(pt(t)),f(St,"已复制")});let K=null;if(w)K=n("button",{type:"button",cls:"btn pri"},"填入输入框"),K.addEventListener("click",(t)=>{t.stopPropagation(),qt()});else W.classList.add("pri");let Pt=n("div",{cls:"foot"},J,Dt,W,K),et=n("span",{cls:"fs"}),Rt=n("button",{type:"button",cls:"lk"},"展开"),Ct=n("div",{cls:"fold"},n("span",{cls:"mk-ok","aria-hidden":"true"}),et,Rt),ft=(t)=>{if(p.classList.toggle("folded",t),t)ut();else if(p.classList.contains("kb")&&_[0])_[0].__focus()};Ct.addEventListener("click",()=>ft(!1));let Ut=n("div",{cls:"ns",role:"status"},"宿主报告没有填进输入框。若输入框里已经有这段文字,可忽略;否则点「复制」后手动粘贴。"),F=null,ht=0,Bt=()=>{if(F)F.disconnect();clearTimeout(ht),p.classList.remove("notsent");let t=document.body;if(!t||typeof MutationObserver!=="function")return;let e=()=>{F.disconnect(),clearTimeout(ht),p.classList.add("notsent"),ft(!1)};F=new MutationObserver(()=>{if(t.hasAttribute("data-not-sent"))e()}),F.observe(t,{attributes:!0,attributeFilter:["data-not-sent"]}),ht=setTimeout(()=>F.disconnect(),5000)};function qt(){let t=lt();if(!t)return;Bt(),sendPrompt(pt(t));let e=Lt();et.textContent="已填入 · "+(e.length?e.join(" · "):"全部按建议"),et.title=et.textContent,ft(!0)}let Wt=()=>{if(w)qt();else W.click()};p.addEventListener("mousedown",()=>p.classList.remove("kb")),p.addEventListener("keydown",(t)=>{if(t.key==="Tab"||t.key.startsWith("Arrow")||/^[0-9]$/.test(t.key))p.classList.add("kb");if(p.classList.contains("folded"))return;let e=t.target;if(e.tagName==="TEXTAREA"){if(t.isComposing||t.keyCode===229)return;if(t.key==="Escape"&&e.__esc)t.preventDefault(),e.__esc();return}if(t.metaKey||t.ctrlKey||t.altKey)return;let o=e.closest&&e.closest(".it");if(!o)return;if(t.key==="/"&&o.__note){t.preventDefault(),o.__note();return}if(/^[1-9]$/.test(t.key)&&o.__pick){t.preventDefault(),o.__pick(+t.key-1);return}if(t.key==="ArrowDown"||t.key==="ArrowUp"){let r=_.indexOf(o),a=_[r+(t.key==="ArrowDown"?1:-1)];if(a)t.preventDefault(),a.__focus()}}),p.append(Ct,wt,Pt,Ut,B);function O(){let t=lt(),e=T(t),o=Lt();if(g.dec.forEach((a)=>a.it.classList.toggle("chg",a.changed())),g.act.forEach((a)=>a.it.classList.toggle("chg",!!a.st.pick)),g.delIt)g.delIt.classList.toggle("chg",g.del.some((a)=>a.st.on!==a.init));g.opt.forEach((a)=>a.it.classList.toggle("chg",a.st.on));let r=[];if(r.push(o.length?"改了 ":"全部按建议",...o.length?[n("b",{},String(o.length))," 项"]:[]),e)r.push(" · 将填入 ",n("b",{},String(e))," 行");else r.push(" · 没有要回复的内容");if(At.replaceChildren(...r),B.textContent=pt(t),R.hidden=!e,!e)ut();if(W.disabled=!e,K)K.disabled=!e;if(J)J.disabled=dt.every((a)=>!a.changed())}O();let gt={},zt=[...p.querySelectorAll(".dseg button.sg")];zt.forEach((t)=>{let e=t.dataset.col;gt[e]=Math.max(gt[e]||0,t.offsetWidth)}),zt.forEach((t)=>{t.style.setProperty("width",Math.ceil(gt[t.dataset.col])+12+"px","important")}),p.classList.add("measuring");let nt=0;if(p.querySelectorAll(".q").forEach((t)=>{nt=Math.max(nt,t.offsetWidth)}),p.classList.remove("measuring"),nt>0)p.style.setProperty("--qw",Math.min(Math.ceil(nt)+2,260)+"px")}return Ot})();var it="0.1.1",jt="trw-style-"+it;function Ht(n){let l=n.ownerDocument||document;if(l.getElementById(jt))return;let m=l.createElement("style");m.id=jt,m.textContent=rt,(l.head||n.parentNode||l.documentElement).appendChild(m)}function xt(n,l){n.replaceChildren(),n.classList.remove("folded","kb");let m=(n.ownerDocument||document).createElement("div");m.className="err",m.setAttribute("role","alert"),m.title=l,m.textContent=l,n.append(m)}var vt=",请直接在输入框回复";function It(n,l){if(!n||typeof n.append!=="function")return console.error("[turn-reply] root 不是元素"),["root 不是元素"];n.classList.add("trw"),Ht(n);let m=l;if(typeof m==="string")try{m=JSON.parse(m)}catch(s){return xt(n,"面板数据不是合法 JSON"+vt),["JSON 解析失败"]}let y=ot(m);if(y.length)return console.error(`[turn-reply] spec 不合法:
|
|
12
|
+
`+y.join(`
|
|
13
|
+
`)),xt(n,"面板数据不合法("+y.length+" 处,首处 "+y[0]+")"+vt),y;try{Nt(n,m)}catch(s){console.error("[turn-reply] 渲染出错",s);let x="面板渲染出错("+(s&&s.message||s)+")"+vt;return xt(n,x),[x]}return[]}var bt=typeof window<"u"?window:globalThis;if(!bt.TR||bt.TR.version!==it)bt.TR={render:It,validate:ot,version:it,css:rt};})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmjsdnt/turn-reply",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "turn-report 回复面板:TR.render(root, spec),供 show_widget 每轮只贴引导代码+spec",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"main": "dist/tr.js",
|
|
7
|
+
"jsdelivr": "dist/tr.min.js",
|
|
8
|
+
"unpkg": "dist/tr.min.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/tr.js",
|
|
11
|
+
"dist/tr.min.js",
|
|
12
|
+
"dist/tr.css"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "bun build.ts",
|
|
16
|
+
"test": "bun build.ts && bun test"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"happy-dom": "^20"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/README.md
DELETED