xterm-input-panel 1.1.0 → 1.2.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/.storybook/vitest.setup.ts +2 -2
- package/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/package.json +3 -3
- package/src/input-method-tab.stories.ts +12 -8
- package/src/input-method-tab.ts +20 -8
- package/src/input-panel.stories.ts +14 -0
- package/src/input-panel.ts +3 -12
- package/src/pixi-theme.test.ts +2 -2
- package/src/platform.ts +3 -3
- package/src/shortcut-pages.ts +431 -61
- package/src/shortcut-tab.ts +45 -24
- package/src/virtual-keyboard-tab.stories.ts +10 -8
- package/src/virtual-trackpad-tab.stories.ts +51 -40
- package/src/xterm-addon.stories.ts +195 -6
- package/src/xterm-addon.ts +246 -19
- package/vitest.storybook.config.ts +1 -1
package/src/shortcut-pages.ts
CHANGED
|
@@ -96,23 +96,135 @@ function terminalPage(): ShortcutPage {
|
|
|
96
96
|
cols: GRID_COLS,
|
|
97
97
|
rows: GRID_ROWS,
|
|
98
98
|
items: [
|
|
99
|
-
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
99
|
+
{
|
|
100
|
+
id: 'ctrl-w',
|
|
101
|
+
label: 'Kill Word',
|
|
102
|
+
subLabel: 'Ctrl+W',
|
|
103
|
+
col: 3,
|
|
104
|
+
row: ROW_Q,
|
|
105
|
+
cols: 2,
|
|
106
|
+
action: { type: 'send', data: '\x17' },
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'ctrl-r',
|
|
110
|
+
label: 'History Search',
|
|
111
|
+
subLabel: 'Ctrl+R',
|
|
112
|
+
col: 5,
|
|
113
|
+
row: ROW_Q,
|
|
114
|
+
cols: 2,
|
|
115
|
+
action: { type: 'send', data: '\x12' },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'ctrl-u',
|
|
119
|
+
label: 'Kill Left',
|
|
120
|
+
subLabel: 'Ctrl+U',
|
|
121
|
+
col: 8,
|
|
122
|
+
row: ROW_Q,
|
|
123
|
+
cols: 2,
|
|
124
|
+
action: { type: 'send', data: '\x15' },
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
{
|
|
128
|
+
id: 'ctrl-a',
|
|
129
|
+
label: 'Line Start',
|
|
130
|
+
subLabel: 'Ctrl+A',
|
|
131
|
+
col: 2,
|
|
132
|
+
row: ROW_A,
|
|
133
|
+
cols: 2,
|
|
134
|
+
action: { type: 'send', data: '\x01' },
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'ctrl-d',
|
|
138
|
+
label: 'EOF',
|
|
139
|
+
subLabel: 'Ctrl+D',
|
|
140
|
+
col: 4,
|
|
141
|
+
row: ROW_A,
|
|
142
|
+
cols: 2,
|
|
143
|
+
action: { type: 'send', data: '\x04' },
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'ctrl-f',
|
|
147
|
+
label: 'Char Right',
|
|
148
|
+
subLabel: 'Ctrl+F',
|
|
149
|
+
col: 6,
|
|
150
|
+
row: ROW_A,
|
|
151
|
+
cols: 2,
|
|
152
|
+
action: { type: 'send', data: '\x06' },
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'ctrl-k',
|
|
156
|
+
label: 'Kill Right',
|
|
157
|
+
subLabel: 'Ctrl+K',
|
|
158
|
+
col: 9,
|
|
159
|
+
row: ROW_A,
|
|
160
|
+
cols: 2,
|
|
161
|
+
action: { type: 'send', data: '\x0b' },
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'ctrl-l',
|
|
165
|
+
label: 'Clear',
|
|
166
|
+
subLabel: 'Ctrl+L',
|
|
167
|
+
col: 11,
|
|
168
|
+
row: ROW_A,
|
|
169
|
+
cols: 2,
|
|
170
|
+
action: { type: 'send', data: '\x0c' },
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
{
|
|
174
|
+
id: 'ctrl-z',
|
|
175
|
+
label: 'Suspend',
|
|
176
|
+
subLabel: 'Ctrl+Z',
|
|
177
|
+
col: 3,
|
|
178
|
+
row: ROW_Z,
|
|
179
|
+
cols: 2,
|
|
180
|
+
action: { type: 'send', data: '\x1a' },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'ctrl-c',
|
|
184
|
+
label: 'Interrupt',
|
|
185
|
+
subLabel: 'Ctrl+C',
|
|
186
|
+
col: 5,
|
|
187
|
+
row: ROW_Z,
|
|
188
|
+
cols: 2,
|
|
189
|
+
action: { type: 'send', data: '\x03' },
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'ctrl-v',
|
|
193
|
+
label: 'Literal',
|
|
194
|
+
subLabel: 'Ctrl+V',
|
|
195
|
+
col: 7,
|
|
196
|
+
row: ROW_Z,
|
|
197
|
+
cols: 2,
|
|
198
|
+
action: { type: 'send', data: '\x16' },
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'ctrl-y',
|
|
202
|
+
label: 'Yank',
|
|
203
|
+
subLabel: 'Ctrl+Y',
|
|
204
|
+
col: 9,
|
|
205
|
+
row: ROW_Z,
|
|
206
|
+
cols: 2,
|
|
207
|
+
action: { type: 'send', data: '\x19' },
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
id: 'alt-b',
|
|
212
|
+
label: 'Prev Word',
|
|
213
|
+
subLabel: 'Alt+B',
|
|
214
|
+
col: 6,
|
|
215
|
+
row: ROW_BOTTOM,
|
|
216
|
+
cols: 2,
|
|
217
|
+
action: { type: 'send', data: '\x1bb' },
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: 'alt-f',
|
|
221
|
+
label: 'Next Word',
|
|
222
|
+
subLabel: 'Alt+F',
|
|
223
|
+
col: 8,
|
|
224
|
+
row: ROW_BOTTOM,
|
|
225
|
+
cols: 2,
|
|
226
|
+
action: { type: 'send', data: '\x1bf' },
|
|
227
|
+
},
|
|
116
228
|
],
|
|
117
229
|
}
|
|
118
230
|
}
|
|
@@ -125,20 +237,116 @@ function claudePage(): ShortcutPage {
|
|
|
125
237
|
cols: GRID_COLS,
|
|
126
238
|
rows: GRID_ROWS,
|
|
127
239
|
items: [
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
240
|
+
{
|
|
241
|
+
id: 'claude-commands',
|
|
242
|
+
label: '/ Commands',
|
|
243
|
+
subLabel: '/',
|
|
244
|
+
col: 1,
|
|
245
|
+
row: ROW_TOP,
|
|
246
|
+
cols: 2,
|
|
247
|
+
action: { type: 'text', text: '/' },
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'claude-shell',
|
|
251
|
+
label: '! Shell',
|
|
252
|
+
subLabel: '!',
|
|
253
|
+
col: 3,
|
|
254
|
+
row: ROW_TOP,
|
|
255
|
+
cols: 2,
|
|
256
|
+
action: { type: 'text', text: '!' },
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
id: 'claude-files',
|
|
260
|
+
label: '@ Paths',
|
|
261
|
+
subLabel: '@',
|
|
262
|
+
col: 5,
|
|
263
|
+
row: ROW_TOP,
|
|
264
|
+
cols: 2,
|
|
265
|
+
action: { type: 'text', text: '@' },
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
id: 'claude-newline',
|
|
269
|
+
label: 'Newline',
|
|
270
|
+
subLabel: '\\ + Enter',
|
|
271
|
+
col: 8,
|
|
272
|
+
row: ROW_TOP,
|
|
273
|
+
cols: 3,
|
|
274
|
+
action: { type: 'text', text: '\\\n' },
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: 'claude-edit-last',
|
|
278
|
+
label: 'Edit Previous',
|
|
279
|
+
subLabel: 'Esc Esc',
|
|
280
|
+
col: 11,
|
|
281
|
+
row: ROW_TOP,
|
|
282
|
+
cols: 3,
|
|
283
|
+
action: { type: 'send', data: '\x1b\x1b' },
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
{
|
|
287
|
+
id: 'claude-editor',
|
|
288
|
+
label: 'External Editor',
|
|
289
|
+
subLabel: 'Ctrl+G',
|
|
290
|
+
col: 2,
|
|
291
|
+
row: ROW_A,
|
|
292
|
+
cols: 3,
|
|
293
|
+
action: { type: 'send', data: '\x07' },
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'claude-reverse-search',
|
|
297
|
+
label: 'History Search',
|
|
298
|
+
subLabel: 'Ctrl+R',
|
|
299
|
+
col: 5,
|
|
300
|
+
row: ROW_A,
|
|
301
|
+
cols: 3,
|
|
302
|
+
action: { type: 'send', data: '\x12' },
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: 'claude-bg',
|
|
306
|
+
label: 'Background Task',
|
|
307
|
+
subLabel: 'Ctrl+B',
|
|
308
|
+
col: 8,
|
|
309
|
+
row: ROW_A,
|
|
310
|
+
cols: 3,
|
|
311
|
+
action: { type: 'send', data: '\x02' },
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 'claude-task-list',
|
|
315
|
+
label: 'Task List',
|
|
316
|
+
subLabel: 'Ctrl+T',
|
|
317
|
+
col: 11,
|
|
318
|
+
row: ROW_A,
|
|
319
|
+
cols: 3,
|
|
320
|
+
action: { type: 'send', data: '\x14' },
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
{
|
|
324
|
+
id: 'claude-change-mode',
|
|
325
|
+
label: 'Change Mode',
|
|
326
|
+
subLabel: 'Shift+Tab',
|
|
327
|
+
col: 1,
|
|
328
|
+
row: ROW_BOTTOM,
|
|
329
|
+
cols: 3,
|
|
330
|
+
action: { type: 'send', data: '\x1b[Z' },
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: 'claude-cancel',
|
|
334
|
+
label: 'Cancel',
|
|
335
|
+
subLabel: 'Ctrl+C',
|
|
336
|
+
col: 4,
|
|
337
|
+
row: ROW_BOTTOM,
|
|
338
|
+
cols: 3,
|
|
339
|
+
action: { type: 'send', data: '\x03' },
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: 'claude-exit',
|
|
343
|
+
label: 'Exit',
|
|
344
|
+
subLabel: 'Ctrl+D',
|
|
345
|
+
col: 7,
|
|
346
|
+
row: ROW_BOTTOM,
|
|
347
|
+
cols: 3,
|
|
348
|
+
action: { type: 'send', data: '\x04' },
|
|
349
|
+
},
|
|
142
350
|
],
|
|
143
351
|
}
|
|
144
352
|
}
|
|
@@ -151,20 +359,108 @@ function codexPage(platform: HostPlatform): ShortcutPage {
|
|
|
151
359
|
cols: GRID_COLS,
|
|
152
360
|
rows: GRID_ROWS,
|
|
153
361
|
items: [
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
362
|
+
{
|
|
363
|
+
id: 'codex-commands',
|
|
364
|
+
label: '/ Commands',
|
|
365
|
+
subLabel: '/',
|
|
366
|
+
col: 1,
|
|
367
|
+
row: ROW_TOP,
|
|
368
|
+
cols: 2,
|
|
369
|
+
action: { type: 'text', text: '/' },
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: 'codex-shell',
|
|
373
|
+
label: '! Shell',
|
|
374
|
+
subLabel: '!',
|
|
375
|
+
col: 3,
|
|
376
|
+
row: ROW_TOP,
|
|
377
|
+
cols: 2,
|
|
378
|
+
action: { type: 'text', text: '!' },
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: 'codex-files',
|
|
382
|
+
label: '@ Paths',
|
|
383
|
+
subLabel: '@',
|
|
384
|
+
col: 5,
|
|
385
|
+
row: ROW_TOP,
|
|
386
|
+
cols: 2,
|
|
387
|
+
action: { type: 'text', text: '@' },
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: 'codex-newline',
|
|
391
|
+
label: 'Newline',
|
|
392
|
+
subLabel: 'Shift+Enter',
|
|
393
|
+
col: 8,
|
|
394
|
+
row: ROW_TOP,
|
|
395
|
+
cols: 3,
|
|
396
|
+
action: { type: 'send', data: '\n' },
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: 'codex-queue',
|
|
400
|
+
label: 'Queue Message',
|
|
401
|
+
subLabel: 'Tab',
|
|
402
|
+
col: 11,
|
|
403
|
+
row: ROW_TOP,
|
|
404
|
+
cols: 3,
|
|
405
|
+
action: { type: 'send', data: '\t' },
|
|
406
|
+
},
|
|
159
407
|
|
|
160
|
-
{
|
|
161
|
-
|
|
162
|
-
|
|
408
|
+
{
|
|
409
|
+
id: 'codex-editor',
|
|
410
|
+
label: 'External Editor',
|
|
411
|
+
subLabel: 'Ctrl+G',
|
|
412
|
+
col: 4,
|
|
413
|
+
row: ROW_A,
|
|
414
|
+
cols: 3,
|
|
415
|
+
action: { type: 'send', data: '\x07' },
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: 'codex-edit-last',
|
|
419
|
+
label: 'Edit Previous',
|
|
420
|
+
subLabel: 'Esc Esc',
|
|
421
|
+
col: 7,
|
|
422
|
+
row: ROW_A,
|
|
423
|
+
cols: 3,
|
|
424
|
+
action: { type: 'send', data: '\x1b\x1b' },
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
id: 'codex-transcript',
|
|
428
|
+
label: 'Transcript',
|
|
429
|
+
subLabel: 'Ctrl+T',
|
|
430
|
+
col: 10,
|
|
431
|
+
row: ROW_A,
|
|
432
|
+
cols: 3,
|
|
433
|
+
action: { type: 'send', data: '\x14' },
|
|
434
|
+
},
|
|
163
435
|
|
|
164
|
-
{
|
|
436
|
+
{
|
|
437
|
+
id: 'codex-paste-image',
|
|
438
|
+
label: 'Paste Image',
|
|
439
|
+
subLabel: codexPasteImageLabel(platform),
|
|
440
|
+
col: 6,
|
|
441
|
+
row: ROW_Z,
|
|
442
|
+
cols: 3,
|
|
443
|
+
action: { type: 'send', data: '\x16' },
|
|
444
|
+
},
|
|
165
445
|
|
|
166
|
-
{
|
|
167
|
-
|
|
446
|
+
{
|
|
447
|
+
id: 'codex-change-mode',
|
|
448
|
+
label: 'Change Mode',
|
|
449
|
+
subLabel: 'Shift+Tab',
|
|
450
|
+
col: 1,
|
|
451
|
+
row: ROW_BOTTOM,
|
|
452
|
+
cols: 3,
|
|
453
|
+
action: { type: 'send', data: '\x1b[Z' },
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
id: 'codex-exit',
|
|
457
|
+
label: 'Exit',
|
|
458
|
+
subLabel: 'Ctrl+C',
|
|
459
|
+
col: 4,
|
|
460
|
+
row: ROW_BOTTOM,
|
|
461
|
+
cols: 3,
|
|
462
|
+
action: { type: 'send', data: '\x03' },
|
|
463
|
+
},
|
|
168
464
|
],
|
|
169
465
|
}
|
|
170
466
|
}
|
|
@@ -177,28 +473,102 @@ function geminiPage(): ShortcutPage {
|
|
|
177
473
|
cols: GRID_COLS,
|
|
178
474
|
rows: GRID_ROWS,
|
|
179
475
|
items: [
|
|
180
|
-
{
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
476
|
+
{
|
|
477
|
+
id: 'gemini-shell-mode',
|
|
478
|
+
label: 'Shell Mode',
|
|
479
|
+
subLabel: '!',
|
|
480
|
+
col: 1,
|
|
481
|
+
row: ROW_TOP,
|
|
482
|
+
cols: 2,
|
|
483
|
+
action: { type: 'text', text: '!' },
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
id: 'gemini-shortcuts-panel',
|
|
487
|
+
label: 'Shortcuts',
|
|
488
|
+
subLabel: '?',
|
|
489
|
+
col: 3,
|
|
490
|
+
row: ROW_TOP,
|
|
491
|
+
cols: 2,
|
|
492
|
+
action: { type: 'text', text: '?' },
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
id: 'gemini-editor',
|
|
496
|
+
label: 'External Editor',
|
|
497
|
+
subLabel: 'Ctrl+X',
|
|
498
|
+
col: 5,
|
|
499
|
+
row: ROW_TOP,
|
|
500
|
+
cols: 3,
|
|
501
|
+
action: { type: 'send', data: '\x18' },
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
id: 'gemini-reverse-search',
|
|
505
|
+
label: 'Reverse Search',
|
|
506
|
+
subLabel: 'Ctrl+R',
|
|
507
|
+
col: 8,
|
|
508
|
+
row: ROW_TOP,
|
|
509
|
+
cols: 3,
|
|
510
|
+
action: { type: 'send', data: '\x12' },
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
id: 'gemini-edit-last',
|
|
514
|
+
label: 'Edit Previous',
|
|
515
|
+
subLabel: 'Esc Esc',
|
|
516
|
+
col: 11,
|
|
517
|
+
row: ROW_TOP,
|
|
518
|
+
cols: 3,
|
|
519
|
+
action: { type: 'send', data: '\x1b\x1b' },
|
|
520
|
+
},
|
|
521
|
+
|
|
522
|
+
{
|
|
523
|
+
id: 'gemini-ide-context',
|
|
524
|
+
label: 'IDE Context',
|
|
525
|
+
subLabel: 'Ctrl+G',
|
|
526
|
+
col: 4,
|
|
527
|
+
row: ROW_A,
|
|
528
|
+
cols: 3,
|
|
529
|
+
action: { type: 'send', data: '\x07' },
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
id: 'gemini-todo-list',
|
|
533
|
+
label: 'TODO List',
|
|
534
|
+
subLabel: 'Ctrl+T',
|
|
535
|
+
col: 7,
|
|
536
|
+
row: ROW_A,
|
|
537
|
+
cols: 3,
|
|
538
|
+
action: { type: 'send', data: '\x14' },
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
id: 'gemini-change-mode',
|
|
542
|
+
label: 'Change Mode',
|
|
543
|
+
subLabel: 'Shift+Tab',
|
|
544
|
+
col: 10,
|
|
545
|
+
row: ROW_A,
|
|
546
|
+
cols: 3,
|
|
547
|
+
action: { type: 'send', data: '\x1b[Z' },
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
{
|
|
551
|
+
id: 'gemini-cancel',
|
|
552
|
+
label: 'Cancel',
|
|
553
|
+
subLabel: 'Ctrl+C',
|
|
554
|
+
col: 4,
|
|
555
|
+
row: ROW_BOTTOM,
|
|
556
|
+
cols: 3,
|
|
557
|
+
action: { type: 'send', data: '\x03' },
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
id: 'gemini-exit',
|
|
561
|
+
label: 'Exit Empty',
|
|
562
|
+
subLabel: 'Ctrl+D',
|
|
563
|
+
col: 7,
|
|
564
|
+
row: ROW_BOTTOM,
|
|
565
|
+
cols: 3,
|
|
566
|
+
action: { type: 'send', data: '\x04' },
|
|
567
|
+
},
|
|
192
568
|
],
|
|
193
569
|
}
|
|
194
570
|
}
|
|
195
571
|
|
|
196
572
|
export function buildShortcutPages(platform: HostPlatform): ShortcutPage[] {
|
|
197
|
-
return [
|
|
198
|
-
systemPage(platform),
|
|
199
|
-
terminalPage(),
|
|
200
|
-
claudePage(),
|
|
201
|
-
codexPage(platform),
|
|
202
|
-
geminiPage(),
|
|
203
|
-
]
|
|
573
|
+
return [systemPage(platform), terminalPage(), claudePage(), codexPage(platform), geminiPage()]
|
|
204
574
|
}
|
package/src/shortcut-tab.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Application, CanvasTextMetrics, Container, FederatedPointerEvent, Graphics, Text, TextStyle } from 'pixi.js'
|
|
1
|
+
import { css, html, LitElement } from 'lit'
|
|
3
2
|
import { createElement, SquareSlash, SquareTerminal } from 'lucide'
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
Application,
|
|
5
|
+
CanvasTextMetrics,
|
|
6
|
+
Container,
|
|
7
|
+
FederatedPointerEvent,
|
|
8
|
+
Graphics,
|
|
9
|
+
Text,
|
|
10
|
+
TextStyle,
|
|
11
|
+
} from 'pixi.js'
|
|
12
|
+
import { onThemeChange, resolvePixiTheme, type PixiTheme } from './pixi-theme.js'
|
|
6
13
|
import { detectHostPlatform, type PlatformMode } from './platform.js'
|
|
14
|
+
import {
|
|
15
|
+
buildShortcutPages,
|
|
16
|
+
type ShortcutAction,
|
|
17
|
+
type ShortcutItem,
|
|
18
|
+
type ShortcutPage,
|
|
19
|
+
} from './shortcut-pages.js'
|
|
7
20
|
|
|
8
21
|
const GRID_GAP = 8
|
|
9
22
|
const GRID_PADDING = 10
|
|
@@ -66,7 +79,10 @@ export class ShortcutTab extends LitElement {
|
|
|
66
79
|
justify-content: center;
|
|
67
80
|
padding: 0;
|
|
68
81
|
cursor: pointer;
|
|
69
|
-
transition:
|
|
82
|
+
transition:
|
|
83
|
+
border-color 0.15s,
|
|
84
|
+
color 0.15s,
|
|
85
|
+
background 0.15s;
|
|
70
86
|
}
|
|
71
87
|
|
|
72
88
|
.page-btn:hover {
|
|
@@ -247,9 +263,7 @@ export class ShortcutTab extends LitElement {
|
|
|
247
263
|
gfx.clear()
|
|
248
264
|
gfx.roundRect(0, 0, width, height, CARD_RADIUS)
|
|
249
265
|
gfx.fill({
|
|
250
|
-
color: pressed
|
|
251
|
-
? this._theme.keyPressed
|
|
252
|
-
: this._theme.keyNormal,
|
|
266
|
+
color: pressed ? this._theme.keyPressed : this._theme.keyNormal,
|
|
253
267
|
})
|
|
254
268
|
gfx.stroke({
|
|
255
269
|
color: pressed ? this._theme.accent : this._theme.surfaceBorder,
|
|
@@ -409,7 +423,7 @@ export class ShortcutTab extends LitElement {
|
|
|
409
423
|
detail: { data },
|
|
410
424
|
bubbles: true,
|
|
411
425
|
composed: true,
|
|
412
|
-
})
|
|
426
|
+
})
|
|
413
427
|
)
|
|
414
428
|
}
|
|
415
429
|
|
|
@@ -443,7 +457,12 @@ export class ShortcutTab extends LitElement {
|
|
|
443
457
|
document.execCommand('selectAll')
|
|
444
458
|
}
|
|
445
459
|
|
|
446
|
-
private _dpadData(
|
|
460
|
+
private _dpadData(
|
|
461
|
+
event: FederatedPointerEvent,
|
|
462
|
+
width: number,
|
|
463
|
+
height: number,
|
|
464
|
+
container: Container
|
|
465
|
+
): string {
|
|
447
466
|
const local = container.toLocal(event.global)
|
|
448
467
|
const dx = local.x - width / 2
|
|
449
468
|
const dy = local.y - height / 2
|
|
@@ -463,7 +482,7 @@ export class ShortcutTab extends LitElement {
|
|
|
463
482
|
event: FederatedPointerEvent,
|
|
464
483
|
width: number,
|
|
465
484
|
height: number,
|
|
466
|
-
container: Container
|
|
485
|
+
container: Container
|
|
467
486
|
) {
|
|
468
487
|
if (item.kind === 'dpad') {
|
|
469
488
|
this._send(this._dpadData(event, width, height, container))
|
|
@@ -518,19 +537,21 @@ export class ShortcutTab extends LitElement {
|
|
|
518
537
|
return html`
|
|
519
538
|
<div class="layout">
|
|
520
539
|
<div class="pages">
|
|
521
|
-
${pages.map(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
540
|
+
${pages.map(
|
|
541
|
+
(page) => html`
|
|
542
|
+
<button
|
|
543
|
+
type="button"
|
|
544
|
+
class="page-btn"
|
|
545
|
+
title=${page.title}
|
|
546
|
+
aria-label=${page.title}
|
|
547
|
+
?data-active=${page.id === activePage.id}
|
|
548
|
+
@click=${() => this._setActivePage(page.id)}
|
|
549
|
+
>
|
|
550
|
+
${this._renderPageIcon(page.id)}
|
|
551
|
+
<span class="sr-only">${page.title}</span>
|
|
552
|
+
</button>
|
|
553
|
+
`
|
|
554
|
+
)}
|
|
534
555
|
</div>
|
|
535
556
|
<div class="canvas-wrap">
|
|
536
557
|
<div class="pixi-host"></div>
|
|
@@ -153,17 +153,16 @@ export const KeyRepeatOnLongPress: StoryObj = {
|
|
|
153
153
|
emitDown(key!.container)
|
|
154
154
|
|
|
155
155
|
// Wait long enough for initial delay (400ms) + several repeats (80ms each)
|
|
156
|
-
// 400 + 80*3 = 640ms,
|
|
157
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
156
|
+
// 400 + 80*3 = 640ms, add wider CI margin
|
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, 850))
|
|
158
158
|
|
|
159
159
|
emitUp(key!.container)
|
|
160
160
|
|
|
161
161
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
162
162
|
|
|
163
|
-
// 1 send from
|
|
164
|
-
// Total should be > 2 (repeats happen at 80ms intervals after 400ms delay)
|
|
163
|
+
// 1 send from key up + at least 1 repeat event.
|
|
165
164
|
const callCount = handler.mock.calls.length
|
|
166
|
-
expect(callCount).toBeGreaterThanOrEqual(
|
|
165
|
+
expect(callCount).toBeGreaterThanOrEqual(2)
|
|
167
166
|
|
|
168
167
|
// All sends should have data 'a'
|
|
169
168
|
for (const call of handler.mock.calls) {
|
|
@@ -252,8 +251,11 @@ export const PointerLeaveDuringRepeatKeepsRepeating: StoryObj = {
|
|
|
252
251
|
el.addEventListener('input-panel:send', handler)
|
|
253
252
|
|
|
254
253
|
emitDown(key!.container)
|
|
255
|
-
// Wait for repeat to start
|
|
256
|
-
|
|
254
|
+
// Wait for repeat to start (polling is more stable than fixed sleeps in CI)
|
|
255
|
+
const repeatStart = Date.now()
|
|
256
|
+
while (handler.mock.calls.length === 0 && Date.now() - repeatStart < 1600) {
|
|
257
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
258
|
+
}
|
|
257
259
|
|
|
258
260
|
const countBefore = handler.mock.calls.length
|
|
259
261
|
expect(countBefore).toBeGreaterThan(0) // At least one repeat fired
|
|
@@ -262,7 +264,7 @@ export const PointerLeaveDuringRepeatKeepsRepeating: StoryObj = {
|
|
|
262
264
|
emitLeave(key!.container)
|
|
263
265
|
|
|
264
266
|
// Wait — repeats should keep firing until release
|
|
265
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
267
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
266
268
|
|
|
267
269
|
const countAfter = handler.mock.calls.length
|
|
268
270
|
expect(countAfter).toBeGreaterThan(countBefore)
|