@preprocess-cn/nano-code-web 0.1.2

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/dialog.js ADDED
@@ -0,0 +1,273 @@
1
+ // ── Question dialog state machine ──
2
+ const qd = {
3
+ questions: [],
4
+ currentIdx: 0,
5
+ focusIdx: 0,
6
+ screen: 'selecting',
7
+ selections: {},
8
+ customInputs: {},
9
+ customText: '',
10
+ dialogId: '',
11
+ multiSelect: false,
12
+ };
13
+
14
+ function qdOpen(data) {
15
+ setInputEnabled(false, false);
16
+ qd.questions = data.questions || [];
17
+ qd.currentIdx = 0;
18
+ qd.focusIdx = 0;
19
+ qd.screen = 'selecting';
20
+ qd.selections = {};
21
+ qd.customInputs = {};
22
+ qd.customText = '';
23
+ qd.dialogId = data.id || '';
24
+ qd.multiSelect = false;
25
+ for (const q of qd.questions) qd.selections[q.question] = [];
26
+ $('qd-overlay').classList.add('show');
27
+ qdRender();
28
+ window._qd = qd;
29
+ }
30
+
31
+ function qdClose() {
32
+ $('qd-overlay').classList.remove('show');
33
+ }
34
+
35
+ function qdRender() {
36
+ const overlay = $('qd-overlay');
37
+ if (!overlay.classList.contains('show')) return;
38
+ $('qd-options').style.display = 'none';
39
+ $('qd-desc').style.display = 'none';
40
+ $('qd-hint').style.display = 'none';
41
+ $('qd-custom-area').classList.remove('show');
42
+ $('qd-custom-hint').classList.remove('show');
43
+ $('qd-confirm-area').classList.remove('show');
44
+ $('qd-back').style.display = 'none';
45
+ $('qd-next').style.display = 'none';
46
+ $('qd-submit').style.display = 'none';
47
+ $('qd-cancel').style.display = '';
48
+
49
+ if (qd.screen === 'selecting') {
50
+ qdRenderSelecting();
51
+ } else if (qd.screen === 'customInput') {
52
+ qdRenderCustomInput();
53
+ } else if (qd.screen === 'confirming') {
54
+ qdRenderConfirming();
55
+ }
56
+ }
57
+
58
+ function qdRenderSelecting() {
59
+ const q = qd.questions[qd.currentIdx];
60
+ if (!q) return;
61
+ const allOptions = [...q.options, { label: '其它 (自定义输入)', description: '输入自定义文本' }];
62
+ $('qd-header-title').textContent = '❓ ' + q.header;
63
+ $('qd-progress').textContent = '(' + (qd.currentIdx + 1) + '/' + qd.questions.length + ')';
64
+ $('qd-question').textContent = q.question;
65
+ const sel = qd.selections[q.question] || [];
66
+
67
+ const optsEl = $('qd-options');
68
+ optsEl.style.display = '';
69
+ optsEl.innerHTML = '';
70
+ for (let i = 0; i < allOptions.length; i++) {
71
+ const opt = allOptions[i];
72
+ const isFocused = i === qd.focusIdx;
73
+ const isSelected = sel.includes(opt.label);
74
+ const isOther = opt.label === '其它 (自定义输入)';
75
+ const div = document.createElement('div');
76
+ div.className = 'qd-opt' + (isFocused ? ' focused' : '') + (isSelected ? ' selected' : '') + (isOther ? ' other' : '');
77
+ div.dataset.idx = i;
78
+ div.innerHTML = '<span class="indicator">' + (isOther ? (isSelected ? '✎' : '✎') : q.multiSelect ? (isSelected ? '☑' : '☐') : isFocused ? '◉' : '○') + '</span><span class="label">' + escapeHtml(opt.label) + '</span>';
79
+ div.onclick = () => { qd.focusIdx = i; qdSelectOption(); };
80
+ div.onmouseenter = () => { qd.focusIdx = i; qdRender(); };
81
+ optsEl.appendChild(div);
82
+ }
83
+
84
+ $('qd-desc').style.display = '';
85
+ $('qd-desc').textContent = allOptions[qd.focusIdx]?.description || '';
86
+
87
+ $('qd-hint').style.display = '';
88
+ $('qd-hint').textContent = q.multiSelect
89
+ ? '↑↓ 选择 Space 切换 ←→ 切换问题 Enter 确认 Esc 取消'
90
+ : '↑↓ 选择 Enter 确认 ←→ 切换问题 Esc 取消';
91
+
92
+ $('qd-cancel').style.display = '';
93
+ if (qd.currentIdx < qd.questions.length - 1) {
94
+ const selCur = qd.selections[qd.questions[qd.currentIdx].question] || [];
95
+ if (selCur.length > 0) {
96
+ $('qd-next').style.display = '';
97
+ $('qd-next').textContent = 'Next →';
98
+ }
99
+ } else {
100
+ const allAnswered = qd.questions.every(q2 => (qd.selections[q2.question] || []).length > 0);
101
+ if (allAnswered) {
102
+ $('qd-next').style.display = '';
103
+ $('qd-next').textContent = 'Review';
104
+ }
105
+ }
106
+ }
107
+
108
+ function qdRenderCustomInput() {
109
+ const q = qd.questions[qd.currentIdx];
110
+ $('qd-header-title').textContent = '✎ ' + q.header + ' - 自定义输入';
111
+ $('qd-progress').textContent = '';
112
+ $('qd-question').textContent = q.question;
113
+
114
+ $('qd-custom-area').classList.add('show');
115
+ $('qd-custom-hint').classList.add('show');
116
+ const input = $('qd-custom-input');
117
+ input.value = qd.customText;
118
+ setTimeout(() => input.focus(), 50);
119
+
120
+ $('qd-cancel').textContent = 'Back';
121
+ }
122
+
123
+ function qdRenderConfirming() {
124
+ $('qd-header-title').innerHTML = '✓ 确认你的回答';
125
+ $('qd-progress').textContent = '';
126
+ $('qd-question').style.display = 'none';
127
+
128
+ const area = $('qd-confirm-area');
129
+ area.classList.add('show');
130
+ area.innerHTML = '';
131
+ for (const q of qd.questions) {
132
+ const sel = qd.selections[q.question] || [];
133
+ const hasOther = sel.includes('其它 (自定义输入)');
134
+ const custom = qd.customInputs[q.question] || '';
135
+ const normalSel = sel.filter(s => s !== '其它 (自定义输入)');
136
+ const display = hasOther && custom ? escapeHtml(custom) : escapeHtml(normalSel.join(', ') || (hasOther ? '(空)' : '(未选择)'));
137
+ const item = document.createElement('div');
138
+ item.className = 'qd-confirm-item';
139
+ item.innerHTML = '<div class="q">[' + escapeHtml(q.header) + '] ' + escapeHtml(q.question) + '</div><div class="a">→ ' + display + '</div>';
140
+ area.appendChild(item);
141
+ }
142
+
143
+ $('qd-cancel').style.display = '';
144
+ $('qd-back').style.display = '';
145
+ $('qd-submit').style.display = '';
146
+ }
147
+
148
+ function qdSelectOption() {
149
+ const q = qd.questions[qd.currentIdx];
150
+ const allOptions = [...q.options, { label: '其它 (自定义输入)', description: '' }];
151
+ const opt = allOptions[qd.focusIdx];
152
+ const isOther = opt.label === '其它 (自定义输入)';
153
+
154
+ if (q.multiSelect) {
155
+ const prev = [...(qd.selections[q.question] || [])];
156
+ const idx = prev.indexOf(opt.label);
157
+ if (idx >= 0) prev.splice(idx, 1);
158
+ else prev.push(opt.label);
159
+ qd.selections[q.question] = prev;
160
+ qdRender();
161
+ return;
162
+ }
163
+
164
+ if (isOther) {
165
+ qd.selections[q.question] = ['其它 (自定义输入)'];
166
+ qd.customText = qd.customInputs[q.question] || '';
167
+ qd.screen = 'customInput';
168
+ qdRender();
169
+ return;
170
+ }
171
+ qd.selections[q.question] = [opt.label];
172
+ qdAdvance();
173
+ }
174
+
175
+ function qdAdvance() {
176
+ if (qd.currentIdx < qd.questions.length - 1) {
177
+ const sel = qd.selections[qd.questions[qd.currentIdx].question] || [];
178
+ if (sel.includes('其它 (自定义输入)')) {
179
+ qd.customText = qd.customInputs[qd.questions[qd.currentIdx].question] || '';
180
+ qd.screen = 'customInput';
181
+ qdRender();
182
+ return;
183
+ }
184
+ qd.currentIdx++;
185
+ qd.focusIdx = 0;
186
+ qdRender();
187
+ } else {
188
+ const allAnswered = qd.questions.every(q2 => (qd.selections[q2.question] || []).length > 0);
189
+ if (allAnswered) {
190
+ qd.screen = 'confirming';
191
+ qdRender();
192
+ }
193
+ }
194
+ }
195
+
196
+ function qdNext() {
197
+ if (qd.screen === 'confirming') { qdSubmit(); return; }
198
+ const q = qd.questions[qd.currentIdx];
199
+ const sel = qd.selections[q.question] || [];
200
+ if (sel.includes('其它 (自定义输入)')) {
201
+ qd.customText = qd.customInputs[q.question] || '';
202
+ qd.screen = 'customInput';
203
+ qdRender();
204
+ return;
205
+ }
206
+ qdAdvance();
207
+ }
208
+
209
+ function qdBack() {
210
+ if (qd.screen === 'confirming') {
211
+ qd.screen = 'selecting';
212
+ qd.currentIdx = qd.questions.length - 1;
213
+ qd.focusIdx = 0;
214
+ qdRender();
215
+ }
216
+ }
217
+
218
+ function qdCancel() {
219
+ if (qd.screen === 'customInput') {
220
+ qd.screen = 'selecting';
221
+ qdRender();
222
+ return;
223
+ }
224
+ if (qd.dialogId) {
225
+ fetch('/question-answer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: qd.dialogId, answers: {} }) }).catch(() => {});
226
+ }
227
+ qdClose();
228
+ }
229
+
230
+ function qdSubmit() {
231
+ const answers = {};
232
+ for (const q of qd.questions) {
233
+ const sel = qd.selections[q.question] || [];
234
+ if (sel.includes('其它 (自定义输入)')) {
235
+ answers[q.question] = qd.customInputs[q.question] || '';
236
+ } else {
237
+ answers[q.question] = sel.join(', ');
238
+ }
239
+ }
240
+ if (qd.dialogId) {
241
+ fetch('/question-answer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: qd.dialogId, answers }) }).catch(() => {});
242
+ }
243
+ qdClose();
244
+ }
245
+
246
+ document.addEventListener('keydown', (e) => {
247
+ const overlay = $('qd-overlay');
248
+ if (!overlay.classList.contains('show')) return;
249
+ if (qd.screen === 'customInput' && document.activeElement === $('qd-custom-input')) {
250
+ if (e.key === 'Escape') { e.preventDefault(); qdCancel(); return; }
251
+ if (e.key === 'Enter' && !e.shiftKey) {
252
+ e.preventDefault();
253
+ const q = qd.questions[qd.currentIdx];
254
+ qd.customInputs[q.question] = $('qd-custom-input').value;
255
+ qd.customText = '';
256
+ qd.screen = 'selecting';
257
+ const allAnswered = qd.questions.every(q2 => (qd.selections[q2.question] || []).length > 0);
258
+ if (allAnswered && qd.currentIdx === qd.questions.length - 1) {
259
+ qd.screen = 'confirming';
260
+ }
261
+ qdRender();
262
+ return;
263
+ }
264
+ return;
265
+ }
266
+ if (e.key === 'Escape') { e.preventDefault(); qdCancel(); return; }
267
+ if (e.key === 'ArrowDown') { e.preventDefault(); qd.focusIdx = Math.min(qd.questions[qd.currentIdx].options.length, qd.focusIdx + 1); qdRender(); return; }
268
+ if (e.key === 'ArrowUp') { e.preventDefault(); qd.focusIdx = Math.max(0, qd.focusIdx - 1); qdRender(); return; }
269
+ if (e.key === 'ArrowLeft') { e.preventDefault(); if (qd.currentIdx > 0) { qd.currentIdx--; qd.focusIdx = 0; qdRender(); } return; }
270
+ if (e.key === 'ArrowRight') { e.preventDefault(); if (qd.currentIdx < qd.questions.length - 1) { const q = qd.questions[qd.currentIdx]; const sel = qd.selections[q.question] || []; if (sel.length > 0) { qd.currentIdx++; qd.focusIdx = 0; qdRender(); } } return; }
271
+ if (e.key === 'Enter') { e.preventDefault(); qdSelectOption(); return; }
272
+ if (e.key === ' ') { e.preventDefault(); qdSelectOption(); return; }
273
+ });