@oharness/server 0.11.0 → 0.12.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.
@@ -1 +1 @@
1
- {"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../src/page/client/composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA45EjC,CAAC"}
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../src/page/client/composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAs4FjC,CAAC"}
@@ -2357,6 +2357,25 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
2357
2357
  #market{width:min(860px,94vw);max-width:min(860px,94vw);max-height:76vh}
2358
2358
  #market[open]{display:flex;flex-direction:column}
2359
2359
  .mk-head{padding:18px 20px 10px;position:relative}
2360
+ /* The list and the import beside it, as tabs: a row of two under the title,
2361
+ the chosen one on a raised chip that slides over when the other is picked. */
2362
+ .mk-tabs{display:flex;gap:4px;margin-top:12px;padding:3px;border-radius:10px;background:var(--field);width:max-content;max-width:100%}
2363
+ .mk-tabs[hidden]{display:none}
2364
+ .mk-tab{
2365
+ display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 12px;border:0;border-radius:8px;
2366
+ background:transparent;color:var(--dim);font:inherit;font-size:12.5px;font-weight:500;cursor:pointer;
2367
+ transition:background-color .16s ease,color .16s ease,box-shadow .16s ease;
2368
+ }
2369
+ .mk-tab:hover{color:var(--fg)}
2370
+ .mk-tab.on{background:var(--bg);color:var(--fg);box-shadow:0 1px 2px #00000026}
2371
+ .mk-tab .vendor-mark{width:14px;height:14px}
2372
+ .mk-tab .vendor-mark + .vendor-mark{margin-left:-2px}
2373
+ .mk-tab-count{
2374
+ min-width:18px;height:18px;padding:0 5px;border-radius:999px;display:inline-grid;place-items:center;
2375
+ background:var(--accent);color:var(--bg);font-size:10.5px;font-weight:600;
2376
+ }
2377
+ /* A tab switch repaints the list; it fades in rather than snapping. */
2378
+ #mk-list.swap{animation:row-in .18s cubic-bezier(.2,.8,.2,1)}
2360
2379
  #mk-list{overflow-y:auto;padding:2px 12px 14px;flex:1}
2361
2380
  /* Where every other window puts it. A "Done" under the list meant scrolling
2362
2381
  past eighty rows to leave, for a dialog that changes nothing on the way out. */
@@ -2375,6 +2394,8 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
2375
2394
  .mk-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:0 10px}
2376
2395
  .mk-item{display:flex;align-items:flex-start;gap:11px;padding:10px 8px;border-radius:9px}
2377
2396
  .mk-item:hover{background:var(--hover)}
2397
+ /* A skill switched off: still listed, so it can be switched back on. */
2398
+ .mk-item.off .mk-naming,.mk-item.off .ico-sm{opacity:.5}
2378
2399
  .mk-item .ico-sm{
2379
2400
  width:28px;height:28px;margin-top:1px;padding:6px;box-sizing:border-box;
2380
2401
  border-radius:7px;background:var(--sel);opacity:.75;
@@ -2408,6 +2429,10 @@ button.pw-plan:disabled{opacity:.4;cursor:default}
2408
2429
  .mk-naming{flex:1;min-width:0}
2409
2430
  .mk-name{font-size:13.5px;font-weight:500}
2410
2431
  .mk-desc{font-size:12px;color:var(--dim);margin-top:2px;line-height:1.45}
2432
+ /* Three lines at most: a skill written for another tool can carry a whole
2433
+ paragraph of triggers, and one row should not fill the dialog. The full
2434
+ text is on the row's tooltip. */
2435
+ .mk-naming .mk-desc{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
2411
2436
  .mk-desc.warn{color:var(--warn)}
2412
2437
  .mk-acts{display:flex;align-items:center;gap:6px;flex:0 0 auto;margin-top:1px}
2413
2438
  .mk-state{font-size:11.5px;color:var(--dim)}
@@ -3400,8 +3425,16 @@ textarea.boxed:focus{outline:none;background:var(--field-focus)}
3400
3425
  At rest the marks take the section labels' grey; a row in play — under the
3401
3426
  pointer, under the keyboard cursor, or switched on — lights its mark to the
3402
3427
  label's own colour. */
3403
- #attach-picker .menu-item .ico-sm{opacity:1;color:var(--dim)}
3404
- #attach-picker .menu-item:hover .ico-sm,#attach-picker .menu-item.cursor .ico-sm{color:var(--fg)}
3428
+ /* Every mark in the label's own colour, at rest as much as under the pointer:
3429
+ grey glyphs beside white words read as rows that are switched off. The
3430
+ count and the chevron stay a step quieter, as solid mixes of the two theme
3431
+ colours rather than translucency, so they hold their tone in both themes. */
3432
+ #attach-picker .menu-item .ico-sm{opacity:1;color:var(--fg)}
3433
+ #attach-picker .menu-item .sub{color:color-mix(in srgb,var(--fg) 70%,var(--bg))}
3434
+ #attach-picker .menu-item .chev{opacity:1;color:color-mix(in srgb,var(--fg) 60%,var(--bg))}
3435
+ /* Two vendor marks side by side, for the row that imports from both. */
3436
+ #attach-picker .menu-item .mark-pair{display:flex;align-items:center;gap:3px;flex:0 0 auto}
3437
+ #attach-picker .menu-item .mark-pair .vendor-mark{width:16px;height:16px}
3405
3438
  /* The stacked rows carry a name over a sentence, so they are already two lines
3406
3439
  tall; the mark beside them is a label for the kind, not an illustration, and
3407
3440
  at 16px it was the widest thing in a row whose job is the words. */
@@ -3818,5 +3851,62 @@ dialog::backdrop{background:rgba(0,0,0,.55)}
3818
3851
  panel's width already selects, without the built-in browser. The person's
3819
3852
  own browser is the rest of the window. */
3820
3853
  .embed #browser-pane,.embed #browser-scrim,.embed .tab-browser{display:none!important}
3854
+
3855
+ /* Motion for the + menu, the pickers beside it and the dialogs they open.
3856
+ A menu grows out of the button it hangs from, a screen inside it slides the
3857
+ way it was entered, and a dialog settles in and fades out. All of it short
3858
+ enough that nothing waits on it, and none of it for someone who asked the
3859
+ system for less motion. */
3860
+ .menu:not([hidden]){animation:menu-in .16s cubic-bezier(.2,.8,.2,1);transform-origin:bottom left}
3861
+ @keyframes menu-in{from{opacity:0;transform:translateY(6px) scale(.97)}to{opacity:1;transform:none}}
3862
+ .menu-body.slide-in{animation:slide-in .2s cubic-bezier(.2,.8,.2,1)}
3863
+ .menu-body.slide-back{animation:slide-back .2s cubic-bezier(.2,.8,.2,1)}
3864
+ @keyframes slide-in{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:none}}
3865
+ @keyframes slide-back{from{opacity:0;transform:translateX(-16px)}to{opacity:1;transform:none}}
3866
+ /* While the height glides the rows stay put against the edge that does not
3867
+ move — the bottom, where the menu hangs from the + — and the top edge
3868
+ uncovers or covers them, rather than the whole list sliding with it. */
3869
+ .menu.resizing{overflow:hidden;justify-content:flex-end}
3870
+ .menu-item{transition:background-color .12s ease}
3871
+ .menu-item:active{transform:scale(.985)}
3872
+
3873
+ #attach-picker .menu-item:hover .chev,#attach-picker .menu-item.cursor .chev{transform:translateX(2px)}
3874
+ dialog[open]{animation:dialog-in .22s cubic-bezier(.2,.8,.2,1)}
3875
+ dialog[open]::backdrop{animation:backdrop-in .22s ease-out}
3876
+ dialog.closing{animation:dialog-out .14s ease-in forwards}
3877
+ dialog.closing::backdrop{animation:backdrop-out .14s ease-in forwards}
3878
+ @keyframes dialog-in{from{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:none}}
3879
+ @keyframes dialog-out{to{opacity:0;transform:translateY(6px) scale(.985)}}
3880
+ @keyframes backdrop-in{from{opacity:0}}
3881
+ @keyframes backdrop-out{to{opacity:0}}
3882
+ .mk-item{transition:background-color .12s ease,opacity .2s ease}
3883
+ .mk-btn,.mk-icon-btn{transition:background-color .12s ease,transform .1s ease,opacity .15s ease}
3884
+ .mk-btn:not(:disabled):active,.mk-icon-btn:active{transform:scale(.96)}
3885
+ /* Rows arrive with the list, a little after one another, the first time a
3886
+ dialog opens; a repaint for a keystroke in the search box does not replay it. */
3887
+ #market.fresh .mk-grid>.mk-item{animation:row-in .22s cubic-bezier(.2,.8,.2,1) both}
3888
+ #market.fresh .mk-grid>.mk-item:nth-child(2){animation-delay:.02s}
3889
+ #market.fresh .mk-grid>.mk-item:nth-child(3){animation-delay:.04s}
3890
+ #market.fresh .mk-grid>.mk-item:nth-child(4){animation-delay:.06s}
3891
+ #market.fresh .mk-grid>.mk-item:nth-child(n+5){animation-delay:.08s}
3892
+ @keyframes row-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
3893
+ /* Claude Code's and Codex's marks: a glyph beside the name it belongs to. */
3894
+ .vendor-mark{width:14px;height:14px;flex:0 0 auto;display:block;position:relative}
3895
+ .vendor-mark img{width:100%;height:100%;display:block;border-radius:24%}
3896
+
3897
+ .vendor-chip{
3898
+ display:inline-flex;align-items:center;gap:5px;margin-left:8px;padding:1px 7px 1px 4px;
3899
+ border-radius:999px;background:var(--sel);color:var(--fg);
3900
+ font-size:11px;font-weight:500;vertical-align:1px;line-height:18px;white-space:nowrap;
3901
+ }
3902
+ .vendor-chip .vendor-mark{width:12px;height:12px}
3903
+ .mk-empty{padding:18px 8px}
3904
+ @media (prefers-reduced-motion:reduce){
3905
+ .menu:not([hidden]),.menu-body.slide-in,.menu-body.slide-back,dialog[open],dialog[open]::backdrop,
3906
+ dialog.closing,dialog.closing::backdrop,#market.fresh .mk-grid>.mk-item,#mk-list.swap{animation:none}
3907
+ .mk-tab{transition:none}
3908
+ .menu-item,.chev,.mk-item,.mk-btn,.mk-icon-btn{transition:none}
3909
+ .menu-item:active,.mk-btn:not(:disabled):active,.mk-icon-btn:active{transform:none}
3910
+ }
3821
3911
  `;
3822
3912
  //# sourceMappingURL=style.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;EAC7B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyuHV,CAAC"}
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;EAC7B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAm0HV,CAAC"}
package/dist/page.js CHANGED
@@ -401,6 +401,7 @@ ${ANALYTICS_STUB}
401
401
  <div class="mk-head">
402
402
  <div id="mk-title" class="cn-title"></div>
403
403
  <div id="mk-note" class="row-desc"></div>
404
+ <div id="mk-tabs" class="mk-tabs" role="tablist" hidden></div>
404
405
  <input id="mk-search" class="mk-search" type="text" autocomplete="off" spellcheck="false">
405
406
  <button id="mk-close" class="mk-close" type="button"></button>
406
407
  </div>
package/dist/page.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;;;;;;GAWG;AAGH;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;;;;;yCAKyB,CAAC;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;4CAOwB,kBAAkB,CAAC,OAAO,CAAC;SAC9D,KAAK;EACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwfK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;EAC1C,aAAa;EACb,UAAU;EACV,gBAAgB;;;QAGV,CAAC"}
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;;;;;;GAWG;AAGH;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;;;;;yCAKyB,CAAC;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;4CAOwB,kBAAkB,CAAC,OAAO,CAAC;SAC9D,KAAK;EACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAyfK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;EAC1C,aAAa;EACb,UAAU;EACV,gBAAgB;;;QAGV,CAAC"}
package/dist/server.js CHANGED
@@ -7,8 +7,11 @@ import { homedir } from "node:os";
7
7
  import { Harness } from "@oharness/engine/harness.ts";
8
8
  import { updateUserMcpConfig } from "@oharness/engine/config-files.ts";
9
9
  import { connectorConfig, officialConnectors } from "@oharness/engine/mcp/registry.ts";
10
+ import { findForeignServer, foreignMcpServers } from "@oharness/engine/foreign.ts";
11
+ import { installMarketPlugin, marketPlugins, removeMarketPlugin } from "@oharness/engine/plugin-market.ts";
10
12
  import { missingSecrets } from "@oharness/engine/mcp/secrets.ts";
11
- import { connectorIcon, providerSites, siteIcon } from "./icons.js";
13
+ import { connectorIcon, providerSites, sameSite, siteIcon, vendorSites } from "./icons.js";
14
+ import { TOOL_ICONS } from "./tool-icons.js";
12
15
  import { checkAnswer } from "@oharness/engine/tools/questions.ts";
13
16
  import { isFree } from "@oharness/engine/providers/types.ts";
14
17
  import { PERMISSION_MODES } from "@oharness/engine/agent/permissions.ts";
@@ -1713,13 +1716,19 @@ export async function startWebServer(opts) {
1713
1716
  case "GET /registry": {
1714
1717
  const installed = new Set(contextOffer().connectors.map((one) => one.name));
1715
1718
  const entries = await officialConnectors();
1719
+ const found = await foreignMcpServers(current.session.cwd, current.harness.config.imports ?? {}, current.harness.plugins);
1716
1720
  return send(res, 200, {
1721
+ // What Claude Code and Codex already have, offered rather than
1722
+ // started: each one is a process, and often a token, that the user
1723
+ // set up for another program. See `foreign.ts`.
1724
+ imports: found.map((one) => foreignRow(one, installed)),
1717
1725
  connectors: entries.map((entry) => ({
1718
1726
  id: entry.id,
1719
1727
  label: entry.label,
1720
1728
  ...(entry.description ? { description: entry.description } : {}),
1721
1729
  ...(entry.publisher ? { publisher: entry.publisher } : {}),
1722
1730
  ...(entry.docsUrl ? { docsUrl: entry.docsUrl } : {}),
1731
+ ...(entry.category ? { category: entry.category } : {}),
1723
1732
  installed: installed.has(entry.id),
1724
1733
  // Said before the click, not after it. An entry that needs a key
1725
1734
  // fails at connect with a perfectly clear message, but a menu that
@@ -1737,10 +1746,35 @@ export async function startWebServer(opts) {
1737
1746
  * otherwise be twelve third-party requests, each one telling a vendor
1738
1747
  * that this user opened the menu before connecting anything.
1739
1748
  */
1749
+ // Claude Code's and Codex's own marks; see tool-icons.ts.
1750
+ case "GET /tool-icon": {
1751
+ const data = TOOL_ICONS[url.searchParams.get("id") ?? ""];
1752
+ if (!data)
1753
+ return send(res, 404, { error: "no icon" });
1754
+ const bytes = Buffer.from(data, "base64");
1755
+ res.writeHead(200, {
1756
+ "content-type": "image/png",
1757
+ "content-length": bytes.length,
1758
+ "cache-control": "private, max-age=604800",
1759
+ });
1760
+ res.end(bytes);
1761
+ return;
1762
+ }
1740
1763
  case "GET /connector-icon": {
1741
1764
  const id = url.searchParams.get("id") ?? "";
1742
1765
  const entry = (await officialConnectors()).find((one) => one.id === id);
1743
- const icon = entry?.siteUrl ? await connectorIcon(entry.id, entry.siteUrl) : undefined;
1766
+ let icon = entry?.siteUrl ? await connectorIcon(entry.id, entry.siteUrl) : undefined;
1767
+ // One brought over from Claude Code or Codex, or added from them: its
1768
+ // vendor's site, from the host it talks to — mcp.zernio.com is
1769
+ // zernio.com. Looked up by name here, never taken from the request.
1770
+ if (!icon && !entry) {
1771
+ const site = foreignSite((current.harness.mcpStatus?.() ?? []).some((one) => one.name === id)
1772
+ ? current.harness.mcp.configFor(id)
1773
+ : (await foreignMcpServers(current.session.cwd, current.harness.config.imports ?? {}, current.harness.plugins))
1774
+ .find((one) => one.name === id && one.config?.type === "http")?.config);
1775
+ if (site)
1776
+ icon = await siteIcon(`site-${new URL(site).hostname}`, vendorSites(site));
1777
+ }
1744
1778
  // A row without a picture draws a monogram, so this is a normal answer
1745
1779
  // rather than something worth reporting.
1746
1780
  if (!icon)
@@ -1794,6 +1828,42 @@ export async function startWebServer(opts) {
1794
1828
  */
1795
1829
  case "POST /connector": {
1796
1830
  const body = await readJson(req);
1831
+ /**
1832
+ * One found in Claude Code's or Codex's config, named by where it was
1833
+ * found rather than sent whole. The declaration is read again here,
1834
+ * so what starts is what is in that file — not whatever a page that
1835
+ * can reach this port chose to put in a request body.
1836
+ */
1837
+ if (body.import && typeof body.import === "object") {
1838
+ const key = body.import;
1839
+ const found = await findForeignServer(current.session.cwd, {
1840
+ source: String(key.source ?? ""),
1841
+ scope: String(key.scope ?? ""),
1842
+ name: String(key.name ?? ""),
1843
+ ...(typeof key.plugin === "string" ? { plugin: key.plugin } : {}),
1844
+ }, current.harness.config.imports ?? {}, current.harness.plugins);
1845
+ if (!found)
1846
+ return send(res, 404, { error: "that server is no longer in its config file" });
1847
+ if (!found.config)
1848
+ return send(res, 400, { error: `${found.name} cannot be used here` });
1849
+ const name = found.name;
1850
+ if (current.harness.mcpStatus().some((one) => one.name === name)) {
1851
+ return send(res, 409, { error: `a connector named "${name}" is already here` });
1852
+ }
1853
+ try {
1854
+ const status = await current.harness.addMcpServer(name, found.config);
1855
+ if (!status.connected && !status.needsAuth) {
1856
+ await current.harness.removeMcpServer(name);
1857
+ return send(res, 400, { error: status.error ?? `${name} did not start` });
1858
+ }
1859
+ await updateUserMcpConfig((existing) => ({ ...existing, [name]: found.config }));
1860
+ return send(res, 200, { status, ...contextOffer() });
1861
+ }
1862
+ catch (err) {
1863
+ await current.harness.removeMcpServer(name).catch(() => { });
1864
+ return send(res, 400, { error: errorMessage(err) });
1865
+ }
1866
+ }
1797
1867
  const id = typeof body.id === "string" ? body.id.trim() : "";
1798
1868
  if (!id)
1799
1869
  return send(res, 400, { error: "id is required" });
@@ -1834,6 +1904,103 @@ export async function startWebServer(opts) {
1834
1904
  return send(res, 400, { error: errorMessage(err) });
1835
1905
  }
1836
1906
  }
1907
+ /**
1908
+ * The plugins on offer, as the connectors are: a file, plus what is here.
1909
+ *
1910
+ * A plugin counts as here when one of that name is loaded from anywhere —
1911
+ * Claude Code's cache included. Offering a second copy of something the
1912
+ * Installed list already shows is how a menu ends up with two PostHogs.
1913
+ */
1914
+ case "GET /plugin-market": {
1915
+ const loaded = current.harness.plugins ?? [];
1916
+ const entries = await marketPlugins();
1917
+ return send(res, 200, {
1918
+ plugins: entries.map((entry) => ({
1919
+ id: entry.id,
1920
+ label: entry.label,
1921
+ publisher: entry.publisher,
1922
+ category: entry.category,
1923
+ description: entry.description,
1924
+ ...(entry.homepage ? { homepage: entry.homepage } : {}),
1925
+ provides: entry.provides,
1926
+ installed: loaded.some((one) => one.market?.id === entry.id || one.name === entry.id),
1927
+ })),
1928
+ });
1929
+ }
1930
+ /**
1931
+ * Install a plugin from the list, or remove one it installed.
1932
+ *
1933
+ * Every open project's harness takes the change, not just the focused
1934
+ * one: the plugin directory is the user's, so it is in every project,
1935
+ * and a tab in another tree that kept the old set would disagree with
1936
+ * the menu about what is installed.
1937
+ */
1938
+ case "POST /plugin-market": {
1939
+ const body = await readJson(req);
1940
+ const id = typeof body.id === "string" ? body.id.trim() : "";
1941
+ if (!id)
1942
+ return send(res, 400, { error: "id is required" });
1943
+ const open = new Set([current.harness, ...harnesses.values()]);
1944
+ if (body.remove === true) {
1945
+ const installed = (current.harness.plugins ?? []).find((one) => one.market?.id === id);
1946
+ if (!installed)
1947
+ return send(res, 404, { error: `${id} was not installed from the plugin list` });
1948
+ for (const harness of open)
1949
+ await harness.dropPlugin(installed.directory);
1950
+ await removeMarketPlugin(id);
1951
+ return send(res, 200, { removed: id, ...contextOffer() });
1952
+ }
1953
+ const entry = (await marketPlugins()).find((one) => one.id === id);
1954
+ if (!entry)
1955
+ return send(res, 404, { error: `no plugin named "${id}"` });
1956
+ try {
1957
+ const directory = await installMarketPlugin(entry);
1958
+ for (const harness of open)
1959
+ await harness.adoptPlugin(directory);
1960
+ return send(res, 200, { installed: id, ...contextOffer() });
1961
+ }
1962
+ catch (err) {
1963
+ return send(res, 400, { error: errorMessage(err) });
1964
+ }
1965
+ }
1966
+ /**
1967
+ * A plugin's vendor icon, on the same terms as a connector's.
1968
+ *
1969
+ * The marketplace's site for the plugin first, because a manifest's
1970
+ * homepage is as often a GitHub repository as the vendor's own page, and
1971
+ * every one of those would come back wearing GitHub's mark.
1972
+ */
1973
+ case "GET /plugin-icon": {
1974
+ const id = url.searchParams.get("id") ?? "";
1975
+ const loaded = (current.harness.plugins ?? []).find((one) => one.name === id || one.market?.id === id);
1976
+ // A Codex plugin ships its own mark, which beats any guess from its site.
1977
+ if (loaded?.logo) {
1978
+ const type = { ".png": "image/png", ".svg": "image/svg+xml", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp" }[path.extname(loaded.logo).toLowerCase()];
1979
+ const bytes = type ? await readFile(loaded.logo).catch(() => undefined) : undefined;
1980
+ if (bytes && type) {
1981
+ res.writeHead(200, { "content-type": type, "content-length": bytes.length, "cache-control": "private, max-age=86400" });
1982
+ res.end(bytes);
1983
+ return;
1984
+ }
1985
+ }
1986
+ const listed = (await marketPlugins()).find((one) => one.id === id || one.id === loaded?.market?.id);
1987
+ const site = listed?.siteUrl ?? loaded?.siteUrl;
1988
+ // A vendor with a connector too wears the connector's mark: the two
1989
+ // dialogs then show one vendor one way, and a mark the connector list
1990
+ // already fetched is not lost to this one's failed attempt.
1991
+ const sibling = site ? (await officialConnectors()).find((one) => sameSite(one.siteUrl, site)) : undefined;
1992
+ const icon = (sibling?.siteUrl ? await connectorIcon(sibling.id, sibling.siteUrl) : undefined) ??
1993
+ (site ? await siteIcon(`plugin-${listed?.id ?? id}`, vendorSites(site)) : undefined);
1994
+ if (!icon)
1995
+ return send(res, 404, { error: "no icon" });
1996
+ res.writeHead(200, {
1997
+ "content-type": icon.type,
1998
+ "content-length": icon.bytes.length,
1999
+ "cache-control": "private, max-age=86400",
2000
+ });
2001
+ res.end(icon.bytes);
2002
+ return;
2003
+ }
1837
2004
  /**
1838
2005
  * Log in to a connector that asked for it.
1839
2006
  *
@@ -2391,6 +2558,23 @@ export async function startWebServer(opts) {
2391
2558
  broadcast({ type: "connectors_changed", ...contextOffer() });
2392
2559
  return send(res, 200, await current.harness.claudeAgentStatus());
2393
2560
  }
2561
+ /**
2562
+ * Switch one skill off or on. Every open project's harness, as with
2563
+ * Claude Agent: it is a setting of this machine, not of one project.
2564
+ */
2565
+ case "POST /skill": {
2566
+ const body = await readJson(req);
2567
+ const name = typeof body.name === "string" ? body.name.trim() : "";
2568
+ if (!name)
2569
+ return send(res, 400, { error: "name is required" });
2570
+ if (typeof body.enabled !== "boolean")
2571
+ return send(res, 400, { error: "enabled must be a boolean" });
2572
+ for (const harness of new Set([current.harness, ...harnesses.values()])) {
2573
+ await harness.setSkillEnabled(name, body.enabled);
2574
+ }
2575
+ broadcast({ type: "connectors_changed", ...contextOffer() });
2576
+ return send(res, 200, contextOffer());
2577
+ }
2394
2578
  case "POST /settings/browser-mode": {
2395
2579
  const body = await readJson(req);
2396
2580
  if (body.mode !== "auto" && body.mode !== "builtin" && body.mode !== "extension") {
@@ -4139,6 +4323,56 @@ export async function startWebServer(opts) {
4139
4323
  * is declared but failed to start is worth showing as exactly that, and a
4140
4324
  * menu built from the config file would claim it was there.
4141
4325
  */
4326
+ /**
4327
+ * One server found in Claude Code's or Codex's config, as the connector
4328
+ * list shows it. What it runs is summarised — a host, a command's name —
4329
+ * rather than echoed: an argument or a URL can carry a key, and the row
4330
+ * only needs to say which server this is.
4331
+ */
4332
+ /** The vendor's site for an HTTP server, from its host; none for a local one. */
4333
+ function foreignSite(config) {
4334
+ if (config?.type !== "http")
4335
+ return undefined;
4336
+ let host;
4337
+ try {
4338
+ host = new URL(config.url).hostname.toLowerCase();
4339
+ }
4340
+ catch {
4341
+ return undefined;
4342
+ }
4343
+ if (host === "localhost" || /^[\d.]+$/.test(host) || host.endsWith(".local") || !host.includes("."))
4344
+ return undefined;
4345
+ const bare = host.replace(/^(mcp|api|connect|server)\./, "");
4346
+ return "https://" + bare + "/";
4347
+ }
4348
+ function foreignRow(one, installed) {
4349
+ const config = one.config;
4350
+ let target = "";
4351
+ if (config?.type === "http") {
4352
+ try {
4353
+ target = new URL(config.url).host;
4354
+ }
4355
+ catch {
4356
+ target = "";
4357
+ }
4358
+ }
4359
+ else if (config) {
4360
+ target = path.basename(config.command);
4361
+ }
4362
+ return {
4363
+ source: one.source,
4364
+ scope: one.scope,
4365
+ name: one.name,
4366
+ ...(one.plugin ? { plugin: one.plugin } : {}),
4367
+ ...(one.pluginTitle ? { pluginTitle: one.pluginTitle } : {}),
4368
+ file: one.file.startsWith(homedir()) ? "~" + one.file.slice(homedir().length) : one.file,
4369
+ ...(config ? { transport: config.type, target } : {}),
4370
+ ...(one.reason ? { reason: one.reason } : {}),
4371
+ ...(one.inlineSecret ? { inlineSecret: true } : {}),
4372
+ ...(config && missingSecrets(config).length ? { missingEnv: missingSecrets(config) } : {}),
4373
+ installed: installed.has(one.name),
4374
+ };
4375
+ }
4142
4376
  function contextOffer() {
4143
4377
  const harness = current.harness;
4144
4378
  const connectors = (harness.mcpStatus?.() ?? []).map((status) => ({
@@ -4151,10 +4385,14 @@ export async function startWebServer(opts) {
4151
4385
  }));
4152
4386
  const plugins = (harness.plugins ?? []).map((plugin) => ({
4153
4387
  name: plugin.name,
4388
+ ...(plugin.title ? { title: plugin.title } : {}),
4154
4389
  ...(plugin.description ? { description: plugin.description } : {}),
4155
4390
  source: plugin.source,
4156
4391
  servers: Object.keys(plugin.mcpServers ?? {}),
4157
4392
  subagents: plugin.subagents?.length ?? 0,
4393
+ // The marketplace id, for the row's remove button. Only a directory the
4394
+ // marketplace put there carries one; nothing else is removed from here.
4395
+ ...(plugin.market ? { market: plugin.market.id } : {}),
4158
4396
  }));
4159
4397
  const runtime = (harness.runtimePluginStatus?.() ?? []).map((plugin) => ({
4160
4398
  name: plugin.id,
@@ -4175,13 +4413,17 @@ export async function startWebServer(opts) {
4175
4413
  * in the model's own system prompt. Nobody driving the composer could see
4176
4414
  * what was there, let alone point at one.
4177
4415
  */
4178
- const skills = (harness.skills?.list?.() ?? []).map((skill) => ({
4416
+ // Switched-off ones included, so the list that switched one off still
4417
+ // has the row to switch it back on. Everything that only wants what the
4418
+ // model can load filters on `disabled`.
4419
+ const skills = (harness.skills?.listAll?.() ?? harness.skills?.list?.() ?? []).map((skill) => ({
4179
4420
  name: skill.name,
4180
4421
  description: skill.description,
4181
4422
  source: skill.source,
4182
4423
  // "builtin" is the registry's own word for one that ships with the
4183
4424
  // harness; the rest name where on disk they were found.
4184
4425
  builtin: skill.source === "builtin",
4426
+ ...(harness.skills?.isDisabled?.(skill.name) ? { disabled: true } : {}),
4185
4427
  }));
4186
4428
  /**
4187
4429
  * What shipped here first, then what this machine added.