dsh-shortcut-extensions 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  A [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) plugin that adds a keyboard-driven quick-switching layer to the DSH web/desktop UI: a model palette, a session palette with prompt preview, session cycling, find-next-answered, composer focus, and configurable permission cycling — all manageable from a sidebar control panel.
8
8
 
9
9
  中文说明见 [README.zh.md](README.zh.md)。
10
+ - **Header chips** — the current workspace name and git branch sit right beside the session title (`conversation.session.header.actions`); the branch is re-read whenever you enter a session.
10
11
 
11
12
  ## Features
12
13
 
@@ -62,6 +63,10 @@ dsh-shortcut-extensions/
62
63
  └── icon.svg
63
64
  ```
64
65
 
66
+ ## Icons / 图标
67
+
68
+ - Icons: [Lucide](https://lucide.dev) — `keyboard`, `cpu`, `messages-square`, `arrow-left`, `arrow-right`, `search-check`, `text-cursor-input`, `shield`, `shield-half`, `panel-left`, `message-circle`, `layout-grid`, `message-square-plus` (ISC License).
69
+
65
70
  ## License
66
71
 
67
72
  [MIT](LICENSE)
package/README.zh.md CHANGED
@@ -7,6 +7,7 @@
7
7
  一个 [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 插件,为 DSH 网页/桌面界面增加一层**键盘驱动的快速切换**:模型面板、带提问预览的会话面板、会话循环、查找下一个已回答会话、聚焦输入框、可配置的权限循环——全部可以在侧栏控制面板里管理。
8
8
 
9
9
  English documentation: [README.md](README.md)。
10
+ - **页头标签** — 在会话标题右侧显示当前工作区名与 git 分支(`conversation.session.header.actions`),每次进入会话都会重新读取分支。
10
11
 
11
12
  ## 功能
12
13
 
@@ -62,6 +63,10 @@ dsh-shortcut-extensions/
62
63
  └── icon.svg
63
64
  ```
64
65
 
66
+ ## 图标 / Icons
67
+
68
+ - 图标:[Lucide](https://lucide.dev)(`keyboard`、`cpu`、`messages-square`、`arrow-left`、`arrow-right`、`search-check`、`text-cursor-input`、`shield`、`shield-half`、`panel-left`、`message-circle`、`layout-grid`、`message-square-plus`,ISC 许可证)。
69
+
65
70
  ## 许可证
66
71
 
67
72
  [MIT](LICENSE)
package/client.js CHANGED
@@ -375,8 +375,8 @@ window.__ModuleLoader__.load({
375
375
  labelKey: 'action.newSession',
376
376
  impl: 'uiWorkspace.startSession()',
377
377
  run: (d) => { try { d.ctx.uiWorkspace.startSession(); } catch (error) { console.error('[shortcut-ext] startSession failed', error); } }
378
- }
379
- ];
378
+ },
379
+ ];
380
380
 
381
381
  const ACTION_BY_ID = new Map(ACTIONS.map((action) => [action.id, action]));
382
382
 
@@ -497,7 +497,7 @@ window.__ModuleLoader__.load({
497
497
  },
498
498
  {}
499
499
  ]
500
- }
500
+ },
501
501
  ];
502
502
 
503
503
  const COMMAND_IDS = COMMANDS.map((command) => command.id);
@@ -507,12 +507,13 @@ window.__ModuleLoader__.load({
507
507
  /* ------------------------------------------------------------------ */
508
508
 
509
509
  const CSS = [
510
- /* Palettes: same material as the host's native popup (menu surface, centered) */
510
+ /* Palettes: menu-toned surface over an opaque base (no see-through) */
511
511
  '.dqs-backdrop{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;',
512
512
  'background:rgba(0,0,0,.32);}',
513
513
  '.dqs-palette{width:min(720px,calc(100vw - 48px));max-height:min(70vh,660px);display:flex;flex-direction:column;',
514
- 'overflow:hidden;background:var(--dsw-specific-menu,var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-1)));',
515
- 'backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px));-webkit-backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px));',
514
+ 'overflow:hidden;background-color:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-overlay));',
515
+ 'background-image:linear-gradient(var(--dsw-specific-menu,var(--dsw-alias-bg-overlay)),',
516
+ 'var(--dsw-specific-menu,var(--dsw-alias-bg-overlay)));',
516
517
  'border:.5px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-lg,14px);',
517
518
  'box-shadow:var(--dsw-elevation-prominent,0 12px 32px rgba(0,0,0,.28));color:var(--dsw-alias-label-primary);}',
518
519
  '.dqs-paletteHead{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 18px 10px;}',
@@ -522,18 +523,25 @@ window.__ModuleLoader__.load({
522
523
  '.dqs-closeBtn:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));',
523
524
  'color:var(--dsw-alias-label-primary);}',
524
525
  '.dqs-input{width:100%;box-sizing:border-box;height:40px;padding:0 12px 0 34px;',
525
- 'border:none;border-radius:10px;background:var(--dsw-alias-bg-layer-2);',
526
- 'color:var(--dsw-alias-label-primary);font-size:13px;outline:none;}',
526
+ 'border:1px solid var(--dsw-alias-border-l1);border-radius:8px;background:var(--dsw-alias-bg-layer-2);',
527
+ 'color:var(--dsw-alias-label-primary);font-size:13px;outline:none;transition:border-color .12s;}',
527
528
  '.dqs-input::placeholder{color:var(--dsw-alias-label-secondary);opacity:.8;}',
528
- '.dqs-input:focus{box-shadow:0 0 0 2px var(--dsw-focus-ring-color,var(--dsw-alias-brand-primary));}',
529
+ '.dqs-input:hover:not(:focus){border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));}',
530
+ '.dqs-input:focus{border-color:var(--dsw-alias-state-business-primary);}',
529
531
  '.dqs-searchWrap{position:relative;margin:0 14px 10px;}',
530
532
  '.dqs-searchIcon{position:absolute;left:11px;top:50%;transform:translateY(-50%);',
531
533
  'color:var(--dsw-alias-label-secondary);display:flex;pointer-events:none;}',
532
- '.dqs-list{height:340px;overflow-y:auto;padding:2px 8px 8px;}',
533
- '.dqs-columns{display:flex;height:340px;min-height:0;}',
534
- '.dqs-columns .dqs-list{flex:1 1 auto;height:100%;min-width:0;}',
534
+ '.dqs-list{height:360px;overflow-y:auto;padding:2px 8px 6px;}',
535
+ '.dqs-list::-webkit-scrollbar{width:8px;height:8px;}',
536
+ '.dqs-list::-webkit-scrollbar-track{background:transparent;}',
537
+ '.dqs-list::-webkit-scrollbar-thumb{background:var(--dsw-alias-scrollbar-bg-l2,var(--dsw-alias-border-l2));',
538
+ 'border-radius:999px;}',
539
+ '.dqs-list::-webkit-scrollbar-thumb:hover{background:var(--dsw-alias-scrollbar-hover-l2,var(--dsw-alias-border-l1));}',
540
+ '.dqs-columns{display:flex;height:360px;min-height:0;}',
541
+ '.dqs-columns .dqs-list{flex:1 1 auto;height:100%;min-width:0;border-right:none;}',
535
542
  '.dqs-paletteWide{width:min(880px,calc(100vw - 48px));}',
536
- '.dqs-row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;cursor:pointer;font-size:14px;}',
543
+ '.dqs-row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:8px;cursor:pointer;',
544
+ 'font-size:14px;min-height:36px;box-sizing:border-box;}',
537
545
  '.dqs-row[data-active="true"]{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));}',
538
546
  '.dqs-rowName{flex:0 1 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
539
547
  '.dqs-rowProvider{flex:1 1 auto;min-width:0;color:var(--dsw-alias-label-secondary);font-size:12px;',
@@ -561,20 +569,30 @@ window.__ModuleLoader__.load({
561
569
  '.dqs-pinBtn[data-pinned="true"]{color:var(--dsw-alias-brand-primary);}',
562
570
  '.dqs-status{padding:10px 16px;font-size:12px;color:var(--dsw-alias-label-secondary);}',
563
571
  '.dqs-statusError{color:var(--dsw-alias-state-error-primary);}',
564
- '.dqs-footer{padding:8px 16px;font-size:11px;color:var(--dsw-alias-label-secondary);',
565
- 'border-top:.5px solid var(--dsw-alias-border-l1);display:flex;gap:14px;flex-wrap:wrap;}',
566
- '.dqs-kbd{border:none;background:var(--dsw-alias-bg-layer-2);border-radius:4px;',
567
- 'padding:1px 5px;font-size:10px;margin-right:4px;color:var(--dsw-alias-label-secondary);}',
572
+ '.dqs-footer{padding:12px 18px 13px;font-size:12px;color:var(--dsw-alias-label-secondary);',
573
+ 'border-top:.5px solid var(--dsw-alias-border-l1);display:flex;align-items:center;gap:8px 22px;flex-wrap:wrap;}',
574
+ '.dqs-hintItem{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;}',
575
+ '.dqs-kbd{border:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));',
576
+ 'background:var(--dsw-alias-bg-layer-1);border-radius:5px;',
577
+ 'min-width:22px;height:20px;padding:0 7px;box-sizing:border-box;text-align:center;font-size:11px;',
578
+ 'color:var(--dsw-alias-label-secondary);display:inline-flex;align-items:center;justify-content:center;}',
568
579
  '.dqs-rowDot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-idle-primary);opacity:.45;}',
569
580
  '.dqs-rowDot[data-running="true"]{background:var(--dsw-alias-state-success-primary);opacity:1;}',
570
581
  '.dqs-rowPin{flex:none;color:var(--dsw-alias-brand-primary);display:flex;}',
582
+ '.dqs-hdrChip{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 8px;',
583
+ 'border-radius:5px;font-size:11px;line-height:1;color:var(--dsw-alias-label-secondary);',
584
+ 'background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l1);',
585
+ 'max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:6px;}',
586
+ '.dqs-hdrChip:last-child{margin-right:0;}',
587
+ '.dqs-hdrChipLabel{overflow:hidden;text-overflow:ellipsis;}',
588
+ '.dqs-hdrChipIcon{display:flex;flex:none;}',
571
589
  '.dqs-badge{flex:none;font-size:11px;color:var(--dsw-alias-state-warn-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:0 8px;line-height:16px;}',
572
590
  /* Panel (mirrors the host Plugins page: centered 960px column) */
573
591
  '.dqs-page{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);display:flex;',
574
592
  'flex-direction:column;align-items:center;gap:26px;padding:0 clamp(24px,4vw,48px) 48px;overflow:auto;}',
575
593
  '.dqs-page>*{width:100%;max-width:960px;}',
576
594
  '.dqs-pageHead{padding-top:36px;margin-bottom:-6px;}',
577
- '.dqs-pageTitle{font-size:26px;font-weight:600;margin:0 0 6px;}',
595
+ '.dqs-pageTitle{font-size:20px;font-weight:500;line-height:28px;margin:0 0 6px;}',
578
596
  '.dqs-pageSub{font-size:13px;color:var(--dsw-alias-label-secondary);margin:0;}',
579
597
  '.dqs-toolbar{display:flex;align-items:center;gap:16px;flex-wrap:wrap;}',
580
598
  '.dqs-tabs{display:flex;gap:2px;padding:3px;border-radius:9px;background:var(--dsw-alias-bg-layer-2);}',
@@ -589,9 +607,10 @@ window.__ModuleLoader__.load({
589
607
  '.dqs-linkBtn:hover{text-decoration:underline;}',
590
608
  '.dqs-search{width:220px;height:32px;padding:0 12px;box-sizing:border-box;border-radius:8px;',
591
609
  'border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);',
592
- 'color:var(--dsw-alias-label-primary);font-size:13px;outline:none;}',
610
+ 'color:var(--dsw-alias-label-primary);font-size:13px;outline:none;transition:border-color .12s;}',
593
611
  '.dqs-search::placeholder{color:var(--dsw-alias-label-secondary);opacity:.7;}',
594
- '.dqs-search:focus{border-color:var(--dsw-alias-brand-primary);}',
612
+ '.dqs-search:hover:not(:focus){border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));}',
613
+ '.dqs-search:focus{border-color:var(--dsw-alias-state-business-primary);}',
595
614
  '.dqs-rows{display:flex;flex-direction:column;}',
596
615
  '.dqs-cmdRow{display:flex;align-items:center;gap:16px;padding:14px 2px;',
597
616
  'border-bottom:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));transition:opacity .15s;}',
@@ -599,13 +618,13 @@ window.__ModuleLoader__.load({
599
618
  '.dqs-tile{flex:none;width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;',
600
619
  'background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-brand-primary);border:1px solid var(--dsw-alias-border-l1);}',
601
620
  '.dqs-cmdInfo{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:3px;}',
602
- '.dqs-cmdName{font-size:15px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
603
- '.dqs-cmdImpl{font-size:11.5px;color:var(--dsw-alias-label-secondary);',
621
+ '.dqs-cmdName{font-size:14px;font-weight:400;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
622
+ '.dqs-cmdImpl{font-size:11px;color:var(--dsw-alias-label-secondary);',
604
623
  'font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
605
624
  '.dqs-cmdRight{flex:none;display:flex;align-items:center;gap:14px;}',
606
625
  '.dqs-cmdKeys{display:flex;align-items:center;gap:3px;}',
607
626
  '.dqs-keycap{min-width:20px;height:20px;padding:0 5px;box-sizing:border-box;display:inline-flex;align-items:center;',
608
- 'justify-content:center;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));border-radius:5px;',
627
+ 'justify-content:center;border:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));border-radius:5px;',
609
628
  'font-size:11px;line-height:1;color:var(--dsw-alias-label-secondary);',
610
629
  'background:var(--dsw-alias-bg-layer-2);white-space:nowrap;}',
611
630
  '.dqs-keycapNone{border:none;background:none;font-size:11px;color:var(--dsw-alias-label-secondary);opacity:.7;}',
@@ -664,7 +683,7 @@ window.__ModuleLoader__.load({
664
683
  '.dqs-permCard{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;',
665
684
  'background:var(--dsw-alias-bg-layer-1);padding:16px 18px;}',
666
685
  '.dqs-sectionHead{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:10px;}',
667
- '.dqs-sectionTitle{font-size:13px;font-weight:600;}',
686
+ '.dqs-sectionTitle{font-size:14px;font-weight:500;line-height:20px;}',
668
687
  '.dqs-chips{display:flex;gap:8px;flex-wrap:wrap;}',
669
688
  '.dqs-permChip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));',
670
689
  'border-radius:999px;padding:5px 14px;font-size:12px;cursor:pointer;background:transparent;color:var(--dsw-alias-label-secondary);}',
@@ -679,7 +698,17 @@ window.__ModuleLoader__.load({
679
698
  '.dqs-expand{padding:2px 2px 18px 64px;}',
680
699
  '.dqs-sysRow{display:flex;align-items:center;gap:12px;padding:12px 2px;',
681
700
  'border-bottom:.5px solid var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));}',
682
- '.dqs-sysLabel{flex:1 1 auto;min-width:0;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
701
+ '.dqs-sysLabel{flex:1 1 auto;min-width:0;font-size:14px;font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}',
702
+ '.dqs-tip{position:relative;}',
703
+ '.dqs-tip:hover::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 6px);left:50%;',
704
+ 'transform:translateX(-50%);background:var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-1));',
705
+ 'border:.5px solid var(--dsw-alias-border-l1);border-radius:6px;padding:3px 8px;font-size:11px;',
706
+ 'line-height:16px;color:var(--dsw-alias-label-primary);white-space:nowrap;z-index:20;',
707
+ 'box-shadow:var(--dsw-elevation-prominent,0 6px 16px rgba(0,0,0,.18));pointer-events:none;}',
708
+ '.dqs-iconBtn{flex:none;border:none;background:none;cursor:pointer;display:flex;padding:4px;',
709
+ 'border-radius:6px;color:var(--dsw-alias-label-secondary);}',
710
+ '.dqs-iconBtn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2));}',
711
+ '.dqs-iconBtn[data-hidden="true"]{visibility:hidden;pointer-events:none;}',
683
712
  '.dqs-sysFixed{flex:none;font-size:11px;color:var(--dsw-alias-label-secondary);',
684
713
  'border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:1px 8px;}'
685
714
  ].join('');
@@ -1216,16 +1245,44 @@ window.__ModuleLoader__.load({
1216
1245
  }));
1217
1246
  }
1218
1247
 
1248
+
1249
+ /** Sidebar glyph: Lucide `keyboard` (ISC) - https://lucide.dev/icons/keyboard */
1219
1250
  function PanelIcon({ size, active }) {
1220
1251
  const px = size ?? 16;
1221
- const bar = (y, width) => h('rect', { x: 2, y, width, height: 2.4, rx: 1.2, fill: 'currentColor' });
1252
+ const dot = (d) => h('path', { key: d, d });
1253
+ return h('svg', {
1254
+ width: px,
1255
+ height: px,
1256
+ viewBox: '0 0 24 24',
1257
+ fill: 'none',
1258
+ stroke: 'currentColor',
1259
+ strokeWidth: 2,
1260
+ strokeLinecap: 'round',
1261
+ strokeLinejoin: 'round',
1262
+ 'aria-hidden': true,
1263
+ style: { display: 'block', color: active ? 'var(--dsw-alias-state-business-primary)' : undefined }
1264
+ },
1265
+ h('rect', { key: 'frame', x: 2, y: 4, width: 20, height: 16, rx: 2 }),
1266
+ dot('M6 8h.01'),
1267
+ dot('M10 8h.01'),
1268
+ dot('M14 8h.01'),
1269
+ dot('M18 8h.01'),
1270
+ dot('M8 12h.01'),
1271
+ dot('M12 12h.01'),
1272
+ dot('M16 12h.01'),
1273
+ dot('M7 16h10'));
1274
+ }
1275
+
1276
+
1277
+ /** Lucide `rotate-ccw` (ISC) - restore a command's default binding. */
1278
+ function ResetIcon() {
1222
1279
  return h('svg', {
1223
- width: px, height: px, viewBox: '0 0 16 16', 'aria-hidden': true,
1224
- style: { display: 'block', color: active ? 'var(--dsw-alias-brand-primary)' : undefined }
1280
+ width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
1281
+ strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true,
1282
+ style: { display: 'block' }
1225
1283
  },
1226
- bar(2.6, 12),
1227
- bar(6.8, 8),
1228
- bar(11, 10));
1284
+ h('path', { d: 'M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8' }),
1285
+ h('path', { d: 'M3 3v5h5' }));
1229
1286
  }
1230
1287
 
1231
1288
  /* ------------------------------------------------------------------ */
@@ -1407,11 +1464,11 @@ window.__ModuleLoader__.load({
1407
1464
  ? h('div', { className: 'dqs-status' }, t('palette.providerFailures'))
1408
1465
  : null,
1409
1466
  h('div', { className: 'dqs-footer' },
1410
- h('span', null, h('span', { className: 'dqs-kbd' }, '\u2191\u2193'), t('palette.hint.nav')),
1411
- h('span', null, h('span', { className: 'dqs-kbd' }, 'Enter'), t('palette.hint.confirm')),
1412
- h('span', null, h('span', { className: 'dqs-kbd' }, 'Esc'), t('palette.hint.close')),
1413
- h('span', null, h('span', { className: 'dqs-kbd' }, '\u2605'), t('palette.hint.pin')),
1414
- h('span', null, t('palette.hint.tag')))
1467
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, '\u2191\u2193'), t('palette.hint.nav')),
1468
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, 'Enter'), t('palette.hint.confirm')),
1469
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, 'Esc'), t('palette.hint.close')),
1470
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, '\u2605'), t('palette.hint.pin')),
1471
+ h('span', { className: 'dqs-hintItem' }, t('palette.hint.tag')))
1415
1472
  ));
1416
1473
  }
1417
1474
 
@@ -1618,9 +1675,9 @@ window.__ModuleLoader__.load({
1618
1675
  })))),
1619
1676
  error ? h('div', { className: 'dqs-status dqs-statusError' }, error) : null,
1620
1677
  h('div', { className: 'dqs-footer' },
1621
- h('span', null, h('span', { className: 'dqs-kbd' }, '\u2191\u2193'), t('session.hint.nav')),
1622
- h('span', null, h('span', { className: 'dqs-kbd' }, 'Enter'), t('session.hint.open')),
1623
- h('span', null, h('span', { className: 'dqs-kbd' }, 'Esc'), t('session.hint.close')))));
1678
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, '\u2191\u2193'), t('session.hint.nav')),
1679
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, 'Enter'), t('session.hint.open')),
1680
+ h('span', { className: 'dqs-hintItem' }, h('span', { className: 'dqs-kbd' }, 'Esc'), t('session.hint.close')))));
1624
1681
  }
1625
1682
 
1626
1683
  /* ------------------------------------------------------------------ */
@@ -1638,6 +1695,109 @@ window.__ModuleLoader__.load({
1638
1695
  return h('div', { className: 'dqs-hintbar', role: 'status' }, state.text);
1639
1696
  }
1640
1697
 
1698
+ /* ------------------------------------------------------------------ */
1699
+ /* Conversation header chips: workspace name and git branch */
1700
+ /* ------------------------------------------------------------------ */
1701
+
1702
+ const FOLDER_GLYPH = '<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />';
1703
+ const BRANCH_GLYPH = '<line x1="6" x2="6" y1="3" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" />';
1704
+
1705
+ function basenameOf(path) {
1706
+ return String(path ?? '').split('/').filter(Boolean).pop() ?? '';
1707
+ }
1708
+
1709
+ function workspaceOfSession(workspacesSnap, sessionId) {
1710
+ for (const item of workspacesSnap?.items ?? []) {
1711
+ if ((item?.sessionIds ?? []).includes(sessionId)) {
1712
+ return { title: item.title || basenameOf(item.path), path: item.path };
1713
+ }
1714
+ }
1715
+ return undefined;
1716
+ }
1717
+
1718
+ function HeaderGlyph({ markup }) {
1719
+ return h('svg', {
1720
+ width: 12, height: 12, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
1721
+ strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true,
1722
+ style: { display: 'block' },
1723
+ dangerouslySetInnerHTML: { __html: markup }
1724
+ });
1725
+ }
1726
+
1727
+ function HeaderWorkspaceChip({ ctx }) {
1728
+ const sessionId = useSessionId(ctx);
1729
+ const workspacesSnap = useWorkspacesList(ctx);
1730
+ const sessionsSnap = useSessionsList(ctx);
1731
+ let workspace = sessionId === undefined ? undefined : workspaceOfSession(workspacesSnap, sessionId);
1732
+ if (!workspace && sessionId !== undefined) {
1733
+ const cwd = sessionsSnap?.byId?.[sessionId]?.cwd;
1734
+ const name = basenameOf(cwd);
1735
+ if (name !== '') workspace = { title: name, path: cwd };
1736
+ }
1737
+ if (!workspace) return null;
1738
+ return h('span', { className: 'dqs-hdrChip', title: workspace.path ?? workspace.title },
1739
+ h('span', { className: 'dqs-hdrChipIcon' }, h(HeaderGlyph, { markup: FOLDER_GLYPH })),
1740
+ h('span', { className: 'dqs-hdrChipLabel' }, workspace.title));
1741
+ }
1742
+
1743
+ /** sessionId -> branch name (or null when unavailable). */
1744
+ const branchCache = new Map();
1745
+
1746
+ async function readGitBranch(ctx, sessionId) {
1747
+ try {
1748
+ const response = await ctx.remote.workspaceFiles.read(
1749
+ sessionId, '.git/HEAD', { offset: 1 }, new AbortController().signal
1750
+ );
1751
+ // Remote calls resolve to a { ok, value } result envelope.
1752
+ if (response && response.ok === false) {
1753
+ const detail = response.error ?? {};
1754
+ return { branch: null, error: `${detail.code ?? 'failed'} ${detail.message ?? ''}`.trim() };
1755
+ }
1756
+ const payload = response?.value ?? response;
1757
+ const text = String(payload?.text ?? '');
1758
+ const ref = /ref:\s*refs\/heads\/(.+)/u.exec(text);
1759
+ if (ref) return { branch: ref[1].trim(), error: null };
1760
+ const trimmed = text.trim();
1761
+ if (trimmed === '') return { branch: null, error: 'empty .git/HEAD' };
1762
+ return { branch: trimmed.slice(0, 7), error: null };
1763
+ } catch (error) {
1764
+ return { branch: null, error: String(error?.message ?? error) };
1765
+ }
1766
+ }
1767
+
1768
+ const branchErrors = new Map();
1769
+
1770
+ function HeaderBranchChip({ ctx }) {
1771
+ const sessionId = useSessionId(ctx);
1772
+ const [state, setState] = useState({ branch: null, error: null });
1773
+ useEffect(() => {
1774
+ if (sessionId === undefined) { setState({ branch: null, error: null }); return undefined; }
1775
+ // Show the cached value immediately, then re-read so a branch switch
1776
+ // made between visits is picked up.
1777
+ if (branchCache.has(sessionId)) {
1778
+ setState({ branch: branchCache.get(sessionId), error: branchErrors.get(sessionId) ?? null });
1779
+ } else {
1780
+ setState({ branch: null, error: null });
1781
+ }
1782
+ let cancelled = false;
1783
+ readGitBranch(ctx, sessionId).then((value) => {
1784
+ if (cancelled) return;
1785
+ branchCache.set(sessionId, value.branch);
1786
+ if (value.error) branchErrors.set(sessionId, value.error);
1787
+ else branchErrors.delete(sessionId);
1788
+ setState(value);
1789
+ });
1790
+ return () => { cancelled = true; };
1791
+ }, [ctx, sessionId]);
1792
+ if (state.branch) {
1793
+ return h('span', { className: 'dqs-hdrChip', title: `git: ${state.branch}` },
1794
+ h('span', { className: 'dqs-hdrChipIcon' }, h(HeaderGlyph, { markup: BRANCH_GLYPH })),
1795
+ h('span', { className: 'dqs-hdrChipLabel' }, state.branch));
1796
+ }
1797
+ // Not a git repository, unreadable, or still loading: stay silent.
1798
+ return null;
1799
+ }
1800
+
1641
1801
  /* ------------------------------------------------------------------ */
1642
1802
  /* Sidebar panel: command deck */
1643
1803
  /* ------------------------------------------------------------------ */
@@ -1648,63 +1808,40 @@ window.__ModuleLoader__.load({
1648
1808
  row.keys.map((cap, index) => h('span', { key: index, className: 'dqs-keycap' }, cap)));
1649
1809
  }
1650
1810
 
1811
+ /**
1812
+ * Command tile glyphs: Lucide icons (ISC, https://lucide.dev) rendered as
1813
+ * static trusted markup, stroked with the current text color.
1814
+ */
1815
+ const COMMAND_GLYPHS = {
1816
+ 'open-model-palette': '<rect width="16" height="16" x="4" y="4" rx="2" /> <rect width="6" height="6" x="9" y="9" rx="1" /> <path d="M15 2v2" /> <path d="M15 20v2" /> <path d="M2 15h2" /> <path d="M2 9h2" /> <path d="M20 15h2" /> <path d="M20 9h2" /> <path d="M9 2v2" /> <path d="M9 20v2" />',
1817
+ 'open-session-palette': '<path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z" /> <path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1" />',
1818
+ 'next-session': '<path d="M5 12h14" /> <path d="m12 5 7 7-7 7" />',
1819
+ 'prev-session': '<path d="m12 19-7-7 7-7" /> <path d="M19 12H5" />',
1820
+ 'jump-completed': '<path d="m8 11 2 2 4-4" /> <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" />',
1821
+ 'focus-composer': '<path d="M5 4h1a3 3 0 0 1 3 3 3 3 0 0 1 3-3h1" /> <path d="M13 20h-1a3 3 0 0 1-3-3 3 3 0 0 1-3 3H5" /> <path d="M5 16H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h1" /> <path d="M13 8h7a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-7" /> <path d="M9 7v10" />',
1822
+ 'toggle-permission': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />',
1823
+ 'set-permission': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /> <path d="M12 22V2" />',
1824
+ 'toggle-sidebar': '<rect width="18" height="18" x="3" y="3" rx="2" /> <path d="M9 3v18" />',
1825
+ 'show-conversation': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z" />',
1826
+ 'show-deck': '<rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" />',
1827
+ 'new-session': '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /> <path d="M12 7v6" /> <path d="M9 10h6" />'
1828
+ };
1829
+
1651
1830
  function CommandGlyph({ id }) {
1652
- const common = {
1653
- width: 20, height: 20, viewBox: '0 0 16 16', 'aria-hidden': true, style: { display: 'block' }
1654
- };
1655
- switch (id) {
1656
- case 'open-model-palette':
1657
- return h('svg', common, h('path', {
1658
- d: 'M9.2 1.5 4 9.2h3L6.2 14.5 12 6.6H8.7l.5-5.1z', fill: 'currentColor'
1659
- }));
1660
- case 'open-session-palette':
1661
- return h('svg', common, h('path', {
1662
- d: 'M2.5 4h11M2.5 8h7M2.5 12h9', stroke: 'currentColor', strokeWidth: 1.6,
1663
- strokeLinecap: 'round', fill: 'none'
1664
- }));
1665
- case 'next-session':
1666
- return h('svg', common, h('path', {
1667
- d: 'M3 8h9M8.5 4 12.5 8l-4 4', stroke: 'currentColor', strokeWidth: 1.6,
1668
- strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none'
1669
- }));
1670
- case 'prev-session':
1671
- return h('svg', common, h('path', {
1672
- d: 'M13 8H4M7.5 4 3.5 8l4 4', stroke: 'currentColor', strokeWidth: 1.6,
1673
- strokeLinecap: 'round', strokeLinejoin: 'round', fill: 'none'
1674
- }));
1675
- case 'jump-completed':
1676
- return h('svg', common,
1677
- h('circle', { cx: 8, cy: 8, r: 5.2, fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 }),
1678
- h('circle', { cx: 8, cy: 8, r: 1.8, fill: 'currentColor' }));
1679
- case 'focus-composer':
1680
- return h('svg', common, h('path', {
1681
- d: 'M5 2.5h6M8 2.5v11M5 13.5h6', stroke: 'currentColor', strokeWidth: 1.6,
1682
- strokeLinecap: 'round', fill: 'none'
1683
- }));
1684
- case 'toggle-sidebar':
1685
- return h('svg', common,
1686
- h('rect', { x: 2, y: 2.5, width: 12, height: 11, rx: 2, fill: 'none', stroke: 'currentColor', strokeWidth: 1.4 }),
1687
- h('path', { d: 'M6 2.5v11', stroke: 'currentColor', strokeWidth: 1.4 }));
1688
- case 'show-conversation':
1689
- return h('svg', common, h('path', {
1690
- d: 'M2.5 3.5h11v7h-6l-3 2.5v-2.5h-2z', fill: 'none', stroke: 'currentColor',
1691
- strokeWidth: 1.4, strokeLinejoin: 'round'
1692
- }));
1693
- case 'show-deck':
1694
- return h('svg', common, h('path', {
1695
- d: 'M2.5 4h11M2.5 8h11M2.5 12h11', stroke: 'currentColor', strokeWidth: 1.6,
1696
- strokeLinecap: 'round', fill: 'none'
1697
- }));
1698
- case 'new-session':
1699
- return h('svg', common, h('path', {
1700
- d: 'M8 3v10M3 8h10', stroke: 'currentColor', strokeWidth: 1.7, strokeLinecap: 'round', fill: 'none'
1701
- }));
1702
- default:
1703
- return h('svg', common, h('path', {
1704
- d: 'M8 1.6 12.6 3.4v4.2c0 3.2-2.2 5.2-4.6 6.8-2.4-1.6-4.6-3.6-4.6-6.8V3.4z',
1705
- fill: 'none', stroke: 'currentColor', strokeWidth: 1.5, strokeLinejoin: 'round'
1706
- }));
1707
- }
1831
+ const markup = COMMAND_GLYPHS[id] ?? COMMAND_GLYPHS['toggle-permission'];
1832
+ return h('svg', {
1833
+ width: 20,
1834
+ height: 20,
1835
+ viewBox: '0 0 24 24',
1836
+ fill: 'none',
1837
+ stroke: 'currentColor',
1838
+ strokeWidth: 2,
1839
+ strokeLinecap: 'round',
1840
+ strokeLinejoin: 'round',
1841
+ 'aria-hidden': true,
1842
+ style: { display: 'block' },
1843
+ dangerouslySetInnerHTML: { __html: markup }
1844
+ });
1708
1845
  }
1709
1846
 
1710
1847
  function SearchIcon() {
@@ -1917,6 +2054,16 @@ window.__ModuleLoader__.load({
1917
2054
  sysRows.map((row) => {
1918
2055
  return h('div', { key: row.id, className: 'dqs-sysRow' },
1919
2056
  h('span', { className: 'dqs-sysLabel' }, row.label),
2057
+ h('button', {
2058
+ type: 'button',
2059
+ className: 'dqs-iconBtn dqs-tip',
2060
+ 'data-tip': t('system.reset'),
2061
+ 'aria-label': t('system.reset'),
2062
+ 'data-hidden': overrides[row.id] === undefined ? 'true' : 'false',
2063
+ onClick: async () => {
2064
+ markResult(row.id, await applyEdit({ type: 'reset', id: row.id }));
2065
+ }
2066
+ }, h(ResetIcon)),
1920
2067
  h(KeyRecorder, {
1921
2068
  ctx,
1922
2069
  t,
@@ -1925,15 +2072,6 @@ window.__ModuleLoader__.load({
1925
2072
  markResult(row.id, await applyEdit({ type: 'set', id: row.id, binding }));
1926
2073
  }
1927
2074
  }),
1928
- overrides[row.id] !== undefined
1929
- ? h('button', {
1930
- type: 'button',
1931
- className: 'dqs-linkBtn',
1932
- onClick: async () => {
1933
- markResult(row.id, await applyEdit({ type: 'reset', id: row.id }));
1934
- }
1935
- }, t('system.reset'))
1936
- : null,
1937
2075
  sysIssues[row.id]
1938
2076
  ? h('span', { className: 'dqs-editorError' }, sysIssues[row.id])
1939
2077
  : null);
@@ -2261,6 +2399,7 @@ window.__ModuleLoader__.load({
2261
2399
  'shortcuts',
2262
2400
  'remote',
2263
2401
  'remote.permissionPresets',
2402
+ 'remote.workspaceFiles',
2264
2403
  'modelDirectories'
2265
2404
  ],
2266
2405
  apply(ctx) {
@@ -2278,14 +2417,28 @@ window.__ModuleLoader__.load({
2278
2417
 
2279
2418
  ctx.effect(() => {
2280
2419
  if (typeof document === 'undefined') return undefined;
2281
- if (document.querySelector('style[data-dqs="1"]')) return undefined;
2282
- const style = document.createElement('style');
2420
+ // Upgrade an existing tag in place: after a hot reload the old tag is
2421
+ // still attached, and skipping it would keep stale CSS alive.
2422
+ const existing = document.querySelector('style[data-dqs="1"]');
2423
+ const style = existing ?? document.createElement('style');
2283
2424
  style.setAttribute('data-dqs', '1');
2284
2425
  style.textContent = CSS;
2285
- document.head.appendChild(style);
2286
- return () => { style.remove(); };
2426
+ if (!existing) document.head.appendChild(style);
2427
+ return () => { if (style.isConnected) style.remove(); };
2287
2428
  }, 'shortcut-ext: styles');
2288
2429
 
2430
+ ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register({
2431
+ name: 'conversation.session.header.actions',
2432
+ id: 'shortcut-ext.header-workspace',
2433
+ order: 10
2434
+ }, () => h(HeaderWorkspaceChip, { ctx })));
2435
+
2436
+ ctx.slots.inject('conversation.session.header.actions', () => ctx.slots.register({
2437
+ name: 'conversation.session.header.actions',
2438
+ id: 'shortcut-ext.header-branch',
2439
+ order: 11
2440
+ }, () => h(HeaderBranchChip, { ctx })));
2441
+
2289
2442
  ctx.slots.inject('shell.overlay', () => ctx.slots.register({
2290
2443
  name: 'shell.overlay',
2291
2444
  id: 'shortcut-ext-palette',
package/icon.svg CHANGED
@@ -1,6 +1,9 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
- <rect x="4" y="4" width="56" height="56" rx="14" fill="#3559e0"/>
3
- <rect x="16" y="19" width="32" height="5" rx="2.5" fill="#ffffff"/>
4
- <rect x="16" y="29.5" width="21" height="5" rx="2.5" fill="#ffffff" opacity="0.85"/>
5
- <rect x="16" y="40" width="27" height="5" rx="2.5" fill="#ffffff" opacity="0.7"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="7" fill="#3559e0"/>
3
+ <g transform="translate(4 4)" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
4
+ <rect x="2" y="4" width="20" height="16" rx="2"/>
5
+ <path d="M6 8h.01"/><path d="M10 8h.01"/><path d="M14 8h.01"/><path d="M18 8h.01"/>
6
+ <path d="M8 12h.01"/><path d="M12 12h.01"/><path d="M16 12h.01"/>
7
+ <path d="M7 16h10"/>
8
+ </g>
6
9
  </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-shortcut-extensions",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Shortcut Extensions: a toggleable registry of quick-switch shortcut commands (model palette with pins, session palette with prompt preview and archived restore, recency-ordered session cycling, find-next-answered jump, focus composer, configurable permission cycling) with a sidebar control panel and a custom command editor.",
6
6
  "keywords": [