dsh-openai-subscription 0.1.6 → 0.1.7

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.
Files changed (2) hide show
  1. package/dist/client.js +214 -31
  2. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -24,7 +24,12 @@ const ZH = {
24
24
  'context.model': '模型',
25
25
  'context.window': '上下文窗口',
26
26
  'context.default': '跟随模型默认',
27
- 'context.million': '1M · 1,000,000 tokens',
27
+ 'context.million': '1M 大上下文',
28
+ 'context.default.detail': '使用同步的模型配置',
29
+ 'context.custom.detail': '手动设置 tokens 数',
30
+ 'context.pending': '有未保存的更改',
31
+ 'context.applied': '已保存',
32
+ 'manage.help': '授权维护与本机连接',
28
33
  'context.custom': '自定义',
29
34
  'context.tokens': '自定义 tokens 数',
30
35
  'context.current': '当前:{count} tokens',
@@ -155,7 +160,12 @@ const EN = {
155
160
  'context.model': 'Model',
156
161
  'context.window': 'Context window',
157
162
  'context.default': 'Use model default',
158
- 'context.million': '1M · 1,000,000 tokens',
163
+ 'context.million': '1M context',
164
+ 'context.default.detail': 'Use the synced model setting',
165
+ 'context.custom.detail': 'Set a token count manually',
166
+ 'context.pending': 'Unsaved changes',
167
+ 'context.applied': 'Saved',
168
+ 'manage.help': 'Authorization and local connection',
159
169
  'context.custom': 'Custom',
160
170
  'context.tokens': 'Custom token count',
161
171
  'context.current': 'Current: {count} tokens',
@@ -363,16 +373,64 @@ window.__ModuleLoader__.load({
363
373
  .oasub-model-title { font-size: 13px; font-weight: 500; line-height: 19px; }
364
374
  .oasub-model-detail { color: var(--dsw-alias-label-tertiary, #81858c); font-size: 11px; line-height: 17px; }
365
375
  .oasub-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
366
- .oasub-management { display: flex; flex-direction: column; gap: 16px; border-top: 1px solid var(--dsw-alias-border-l2, rgba(15, 17, 21, .14)); padding-top: 16px; }
367
- .oasub-manage-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
368
- .oasub-manage-row > .oasub-button { flex: 0 0 auto; }
369
- .oasub-button.disclosure { border-color: transparent; border-radius: 8px; color: var(--dsw-alias-label-secondary, #61666b); padding: 4px 0; min-height: 32px; align-self: flex-start; }
370
- .oasub-context { display: flex; flex-direction: column; gap: 10px; }
371
- .oasub-field { display: flex; flex-direction: column; gap: 6px; flex: 1 1 180px; min-width: 0; font-size: 12px; }
376
+ .oasub-wrap { container-type: inline-size; }
377
+ .oasub-ui-icon { display: block; flex: none; }
378
+ .oasub-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
379
+ .oasub-section-copy { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
380
+ .oasub-context { display: flex; flex-direction: column; gap: 16px; }
381
+ .oasub-context-grid { display: grid; grid-template-columns: 1.1fr 1fr; align-items: start; gap: 12px; }
382
+ .oasub-field { display: flex; flex-direction: column; gap: 8px; min-width: 0; font-size: 12px; font-weight: 500; }
372
383
  .oasub-context > .oasub-field { flex: none; }
373
- .oasub-select { width: 100%; min-height: 36px; border: 1px solid var(--oasub-control-border); border-radius: 8px; padding: 7px 10px; font: inherit; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); }
374
- .oasub-select:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #4176e6); outline-offset: 2px; }
375
- .oasub-select:disabled { opacity: .55; }
384
+ .oasub-picker { position: relative; min-width: 0; }
385
+ .oasub-picker-trigger, .oasub-input-shell {
386
+ width: 100%; min-height: 56px; padding: 10px 12px;
387
+ border: 1px solid var(--dsw-alias-border-l1, rgba(15, 17, 21, .2)); border-radius: 12px;
388
+ color: var(--dsw-alias-label-primary, #0f1115); background: var(--dsw-alias-bg-layer-2, #f5f6f7);
389
+ }
390
+ .oasub-picker-trigger { appearance: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; font: inherit; text-align: start; cursor: pointer; transition: border-color .16s, background .16s; }
391
+ .oasub-picker-trigger:hover:not(:disabled), .oasub-picker-trigger[aria-expanded="true"] { border-color: var(--oasub-control-border); background: var(--dsw-alias-bg-layer-1, #fff); }
392
+ .oasub-picker-trigger:focus-visible, .oasub-input-shell:focus-within, .oasub-management-toggle:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #4176e6); outline-offset: 3px; }
393
+ .oasub-picker-trigger:disabled { opacity: .5; cursor: default; }
394
+ .oasub-picker-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
395
+ .oasub-picker-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 500; line-height: 19px; }
396
+ .oasub-picker-detail { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-secondary, #61666b); font-size: 11px; font-weight: 400; line-height: 16px; font-variant-numeric: tabular-nums; }
397
+ .oasub-chevron { color: var(--dsw-alias-label-secondary, #61666b); transition: transform .16s; }
398
+ [aria-expanded="true"] > .oasub-chevron { transform: rotate(180deg); }
399
+ .oasub-picker-menu { position: absolute; inset: calc(100% + 6px) 0 auto; z-index: 20; margin: 0; padding: 5px; max-height: 248px; overflow-y: auto; overscroll-behavior: contain; list-style: none; border: 1px solid var(--dsw-alias-border-l1, rgba(15, 17, 21, .2)); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 8px 28px rgba(0, 0, 0, .14); }
400
+ .oasub-picker-option { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 48px; padding: 8px 10px; border-radius: 9px; cursor: pointer; }
401
+ .oasub-picker-option[data-active="true"] { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 17, 21, .06)); }
402
+ .oasub-picker-option[aria-selected="true"] .oasub-picker-label { font-weight: 600; }
403
+ .oasub-picker-option[aria-disabled="true"] { opacity: .4; cursor: not-allowed; }
404
+ .oasub-input-shell { display: flex; align-items: center; gap: 12px; min-height: 44px; }
405
+ .oasub-input-shell:has(input:disabled) { opacity: .5; }
406
+ .oasub-input-shell:has([aria-invalid="true"]) { border-color: var(--dsw-alias-state-error-primary, #dc2626); }
407
+ .oasub-token-input { appearance: none; width: 100%; min-width: 0; padding: 0; border: 0; outline: none; background: transparent; color: inherit; font: inherit; font-size: 14px; line-height: 22px; font-variant-numeric: tabular-nums; }
408
+ .oasub-token-unit { color: var(--dsw-alias-label-tertiary, #81858c); font-size: 12px; font-weight: 400; }
409
+ .oasub-context-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; color: var(--dsw-alias-label-secondary, #61666b); font-size: 12px; line-height: 18px; font-variant-numeric: tabular-nums; }
410
+ .oasub-context-badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f5f6f7); font-size: 10px; }
411
+ .oasub-context-note { display: flex; align-items: flex-start; gap: 7px; color: var(--dsw-alias-label-tertiary, #81858c); font-size: 11px; line-height: 17px; }
412
+ .oasub-context-note > svg { margin-top: 2px; }
413
+ .oasub-context-error { color: var(--dsw-alias-state-error-primary, #b91c1c); font-size: 12px; line-height: 18px; }
414
+ .oasub-context-footer { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; padding-top: 14px; border-top: 1px solid var(--dsw-alias-border-l2, rgba(15, 17, 21, .14)); }
415
+ .oasub-management-card { padding: 0; gap: 0; }
416
+ .oasub-management-heading { margin: 0; font: inherit; }
417
+ .oasub-management-toggle { appearance: none; display: flex; align-items: center; gap: 12px; width: 100%; padding: var(--oasub-section-inset); border: 0; border-radius: 16px; color: inherit; background: transparent; font: inherit; text-align: start; cursor: pointer; transition: background .16s; }
418
+ .oasub-management-toggle:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 17, 21, .04)); }
419
+ .oasub-management-toggle > .oasub-section-copy { flex: 1; }
420
+ .oasub-management-symbol { display: grid; place-items: center; width: 34px; height: 34px; flex: none; border-radius: 10px; color: var(--dsw-alias-label-secondary, #61666b); background: var(--dsw-alias-bg-layer-2, #f5f6f7); }
421
+ .oasub-management { display: flex; flex-direction: column; margin-inline: var(--oasub-section-inset); }
422
+ .oasub-management[hidden] { display: none; }
423
+ .oasub-manage-row { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-block: 16px; border-top: 1px solid var(--dsw-alias-border-l2, rgba(15, 17, 21, .14)); }
424
+ .oasub-manage-row .oasub-status-detail { font-size: 11px; line-height: 17px; }
425
+ .oasub-manage-title { font-size: 13px; font-weight: 500; line-height: 20px; }
426
+ .oasub-manage-row > .oasub-button { flex: 0 0 auto; min-height: 32px; padding: 5px 12px; border-color: var(--dsw-alias-border-l1, rgba(15, 17, 21, .2)); font-size: 12px; }
427
+ .oasub-manage-row > .oasub-button.danger-quiet { color: var(--dsw-alias-state-error-primary, #b91c1c); border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #dc2626) 25%, transparent); }
428
+ .oasub-manage-row > .oasub-button.danger-quiet:hover:not(:disabled) { background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #dc2626) 7%, transparent); border-color: var(--dsw-alias-state-error-primary, #dc2626); }
429
+ @container (max-width: 460px) {
430
+ .oasub-context-grid { grid-template-columns: minmax(0, 1fr); }
431
+ .oasub-manage-row { align-items: flex-start; flex-direction: column; gap: 10px; }
432
+ .oasub-manage-row > .oasub-button { align-self: flex-start; }
433
+ }
376
434
  .oasub-button {
377
435
  appearance: none;
378
436
  min-height: 36px;
@@ -448,6 +506,9 @@ window.__ModuleLoader__.load({
448
506
  .oasub-manage-row { align-items: flex-start; flex-direction: column; gap: 8px; }
449
507
  }
450
508
  @media (forced-colors: active) {
509
+ .oasub-picker-trigger, .oasub-input-shell, .oasub-picker-menu { border-color: ButtonText; }
510
+ .oasub-picker-option[data-active="true"] { outline: 1px solid Highlight; outline-offset: -1px; }
511
+ .oasub-picker-option[aria-disabled="true"], .oasub-picker-trigger:disabled { color: GrayText; opacity: 1; }
451
512
  .oasub-button { border-color: ButtonText; }
452
513
  .oasub-button.primary, .oasub-button.danger { border-color: ButtonText; color: ButtonText; background: ButtonFace; }
453
514
  .oasub-button:not(.primary):not(.danger):hover:not(:disabled),
@@ -456,7 +517,7 @@ window.__ModuleLoader__.load({
456
517
  .oasub-button:disabled { border-color: GrayText; color: GrayText; opacity: 1; }
457
518
  }
458
519
  @media (prefers-reduced-motion: reduce) {
459
- .oasub-button { transition: none; }
520
+ .oasub-button, .oasub-picker-trigger, .oasub-chevron, .oasub-management-toggle { transition: none; }
460
521
  .oasub-skeleton::after { animation: none; display: none; }
461
522
  }
462
523
  `;
@@ -661,6 +722,17 @@ window.__ModuleLoader__.load({
661
722
  return null;
662
723
  }
663
724
  }
725
+ function uiIcon(name, size = 16) {
726
+ const paths = {
727
+ chevron: 'm6 9 6 6 6-6', check: 'm5 12 4 4L19 6',
728
+ info: 'M12 11v6m0-10v.01M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0',
729
+ settings: 'M4 7h9m4 0h3M4 17h3m4 0h9M13 4v6M7 14v6',
730
+ };
731
+ return React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
732
+ stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round',
733
+ className: 'oasub-ui-icon' + (name === 'chevron' ? ' oasub-chevron' : ''), 'aria-hidden': true, focusable: false,
734
+ }, React.createElement('path', { d: paths[name] }));
735
+ }
664
736
  function Section(props) {
665
737
  const el = React.createElement;
666
738
  const { connection, timer, t } = props;
@@ -686,6 +758,10 @@ window.__ModuleLoader__.load({
686
758
  // null means no pending edit; '' selects the synchronized model default.
687
759
  const [contextDraft, setContextDraft] = React.useState(null);
688
760
  const [contextChoice, setContextChoice] = React.useState(null);
761
+ const [picker, setPicker] = React.useState(null);
762
+ const pickerRoot = React.useRef(null);
763
+ const activeOption = React.useRef(null);
764
+ const pickerSearch = React.useRef({ text: '', at: 0 });
689
765
  const actionLock = React.useRef(false);
690
766
  const cancelAcknowledged = React.useRef(false);
691
767
  const flowObserved = React.useRef(false);
@@ -770,6 +846,28 @@ window.__ModuleLoader__.load({
770
846
  setContextLoading(false); });
771
847
  return () => controller.abort();
772
848
  }, [connection, configured, statusRevision]);
849
+ const contextDisabled = busy || contextLoading || statusLoading;
850
+ React.useEffect(() => {
851
+ if (contextDisabled || !configured)
852
+ setPicker(null);
853
+ }, [contextDisabled, configured, contexts]);
854
+ React.useEffect(() => {
855
+ if (!picker || typeof document === 'undefined')
856
+ return;
857
+ const dismissOutside = (event) => {
858
+ if (!pickerRoot.current?.contains(event.target))
859
+ setPicker(null);
860
+ };
861
+ document.addEventListener('pointerdown', dismissOutside);
862
+ document.addEventListener('focusin', dismissOutside);
863
+ return () => {
864
+ document.removeEventListener('pointerdown', dismissOutside);
865
+ document.removeEventListener('focusin', dismissOutside);
866
+ };
867
+ }, [picker?.id]);
868
+ React.useEffect(() => {
869
+ activeOption.current?.scrollIntoView?.({ block: 'nearest' });
870
+ }, [picker?.id, picker?.active]);
773
871
  React.useEffect(() => {
774
872
  if (props.subscribeReset === undefined)
775
873
  return;
@@ -1123,6 +1221,81 @@ window.__ModuleLoader__.load({
1123
1221
  }
1124
1222
  });
1125
1223
  };
1224
+ // Select-only combobox: focus stays on the trigger, and navigation never saves a value.
1225
+ const renderPicker = (id, label, value, options, choose, describedBy) => {
1226
+ const open = picker?.id === id && !contextDisabled;
1227
+ const selected = options.find((option) => option.value === value);
1228
+ const enabled = options.map((option, index) => option.disabled ? -1 : index).filter((index) => index >= 0);
1229
+ const selectedIndex = Math.max(0, options.findIndex((option) => option.value === value && !option.disabled));
1230
+ const active = open ? picker.active : selectedIndex;
1231
+ const optionId = (index) => id + '-option-' + index;
1232
+ const commit = (index) => {
1233
+ const option = options[index];
1234
+ if (!option || option.disabled || contextDisabled)
1235
+ return;
1236
+ choose(option.value);
1237
+ setPicker(null);
1238
+ };
1239
+ const onKeyDown = (event) => {
1240
+ if (contextDisabled)
1241
+ return;
1242
+ if (event.key === 'Escape' && open) {
1243
+ event.preventDefault();
1244
+ event.stopPropagation();
1245
+ setPicker(null);
1246
+ return;
1247
+ }
1248
+ if (event.key === 'Tab') {
1249
+ setPicker(null);
1250
+ return;
1251
+ }
1252
+ if (event.key === 'Enter' || event.key === ' ') {
1253
+ event.preventDefault();
1254
+ if (open)
1255
+ commit(active);
1256
+ else {
1257
+ pickerSearch.current.text = '';
1258
+ setPicker({ id, active: selectedIndex });
1259
+ }
1260
+ return;
1261
+ }
1262
+ const direction = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : 0;
1263
+ if (direction || event.key === 'Home' || event.key === 'End') {
1264
+ event.preventDefault();
1265
+ const position = enabled.indexOf(active);
1266
+ const next = event.key === 'Home' ? enabled[0] : event.key === 'End' ? enabled.at(-1)
1267
+ : !open ? selectedIndex : enabled[(position + direction + enabled.length) % enabled.length];
1268
+ setPicker({ id, active: next ?? 0 });
1269
+ return;
1270
+ }
1271
+ if (event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey) {
1272
+ event.preventDefault();
1273
+ const search = pickerSearch.current;
1274
+ search.text = (Date.now() - search.at > 700 ? '' : search.text) + event.key.toLocaleLowerCase();
1275
+ search.at = Date.now();
1276
+ const text = [...search.text].every((char) => char === search.text[0]) ? search.text[0] : search.text;
1277
+ const order = [...enabled.filter((index) => index > active), ...enabled.filter((index) => index <= active)];
1278
+ const match = order.find((index) => options[index].label.toLocaleLowerCase().startsWith(text) || options[index].value.toLocaleLowerCase().startsWith(text));
1279
+ if (match !== undefined)
1280
+ setPicker({ id, active: match });
1281
+ }
1282
+ };
1283
+ const copy = (option) => el('span', { className: 'oasub-picker-copy' }, el('span', { className: 'oasub-picker-label' }, option?.label ?? value), option?.detail ? el('span', { className: 'oasub-picker-detail' }, option.detail) : null);
1284
+ return el('div', { className: 'oasub-field' }, el('label', { id: id + '-label', htmlFor: id }, label), el('div', { className: 'oasub-picker', ref: open ? pickerRoot : undefined }, el('button', { id, type: 'button', className: 'oasub-picker-trigger', role: 'combobox',
1285
+ 'aria-label': label, 'aria-expanded': open, 'aria-haspopup': 'listbox',
1286
+ 'aria-controls': open ? id + '-list' : undefined, 'aria-activedescendant': open ? optionId(active) : undefined,
1287
+ 'aria-describedby': describedBy, disabled: contextDisabled, 'data-value': value,
1288
+ onKeyDown, onClick: () => { pickerSearch.current.text = ''; setPicker(open ? null : { id, active: selectedIndex }); },
1289
+ }, copy(selected), uiIcon('chevron')), open ? el('ul', { id: id + '-list', className: 'oasub-picker-menu', role: 'listbox', 'aria-labelledby': id + '-label',
1290
+ onMouseDown: (event) => event.preventDefault(),
1291
+ }, options.map((option, index) => el('li', { key: option.value, id: optionId(index), role: 'option',
1292
+ className: 'oasub-picker-option', 'aria-selected': option.value === value, 'aria-disabled': !!option.disabled,
1293
+ 'data-value': option.value, 'data-active': index === active, ref: index === active ? activeOption : undefined,
1294
+ onMouseMove: () => { if (!option.disabled && index !== active)
1295
+ setPicker({ id, active: index }); },
1296
+ onClick: () => commit(index),
1297
+ }, copy(option), option.value === value ? uiIcon('check', 15) : null))) : null));
1298
+ };
1126
1299
  const selectCode = () => {
1127
1300
  codeRef.current?.focus();
1128
1301
  codeRef.current?.select();
@@ -1185,25 +1358,35 @@ window.__ModuleLoader__.load({
1185
1358
  onClick: () => startAuthorization(info.refreshable ? 'refresh' : 'device_code'),
1186
1359
  }, phase === 'starting' || phase === 'authorizing' ? t('action.refreshing') : t(info.refreshable ? 'action.refresh' : 'action.reconnect'))
1187
1360
  : null), configured ? el('div', { id: 'oasub-sync-help', className: 'oasub-status-detail' }, t('model.sync.help')) : null);
1188
- return el('div', { className: 'oasub-wrap' }, el('header', { className: 'oasub-header' }, el('div', { className: 'oasub-mark', 'aria-hidden': true }, el(OpenAIIcon, { size: 24 })), el('div', { className: 'oasub-heading' }, el('h2', { className: 'oasub-title' }, t('title')), el('p', { className: 'oasub-subtitle' }, t('subtitle')))), statusCard, configured ? el('section', { className: 'oasub-card oasub-context', 'aria-labelledby': 'oasub-context-title' }, el('div', { id: 'oasub-context-title', className: 'oasub-status-title' }, t('context.title')), el('div', { className: 'oasub-status-detail' }, t('context.help')), contextLoading ? el('div', { role: 'status', className: 'oasub-status-detail' }, t('context.loading')) : null, contextError ? el('div', { role: 'alert', className: 'oasub-notice error' }, t(contextError), el('button', { type: 'button', className: 'oasub-button quiet', disabled: contextLoading, onClick: reloadStatus }, t('action.retry'))) : null, !contextLoading && contexts?.models.length === 0 ? el('div', { className: 'oasub-status-detail' }, t('context.empty')) : null, selectedModel ? el('div', { className: 'oasub-context' }, el('div', { className: 'oasub-actions' }, el('label', { className: 'oasub-field' }, t('context.model'), el('select', { className: 'oasub-select', 'aria-label': t('context.model'), value: modelId, disabled: busy || contextLoading || statusLoading,
1189
- onChange: (event) => { setModelId(event.currentTarget.value); setContextDraft(null); setContextChoice(null); } }, contexts?.models.map((model) => el('option', { key: model.id, value: model.id }, model.name && model.name !== model.id ? model.name + ' (' + model.id + ')' : model.id)))), el('label', { className: 'oasub-field' }, t('context.window'), el('select', { className: 'oasub-select', 'aria-label': t('context.window'), value: contextMode, disabled: busy || contextLoading || statusLoading,
1190
- 'aria-describedby': 'oasub-context-limit',
1191
- onChange: (event) => {
1192
- const choice = event.currentTarget.value === 'default' ? 'default' : event.currentTarget.value === 'million' ? 'million' : 'custom';
1193
- setContextChoice(choice);
1194
- setContextDraft(choice === 'default' ? '' : choice === 'million' ? '1000000' : '-');
1195
- } }, el('option', { value: 'default' }, t('context.default') + (selectedModel.defaultContextWindow ? ' · ' + selectedModel.defaultContextWindow.toLocaleString() : '')), el('option', { value: 'million', disabled: selectedModel.maxContextWindow !== undefined && selectedModel.maxContextWindow < 1_000_000 }, t('context.million')), el('option', { value: 'custom' }, t('context.custom'))))), contextMode === 'custom' ? el('label', { className: 'oasub-field' }, t('context.tokens'), el('input', { className: 'oasub-select', type: 'text', inputMode: 'numeric', 'aria-label': t('context.tokens'),
1196
- value: draft === '-' ? '' : draft, disabled: busy || contextLoading || statusLoading,
1197
- 'aria-invalid': contextValidation !== null, 'aria-describedby': 'oasub-context-limit',
1198
- onChange: (event) => setContextDraft(event.currentTarget.value || '-') })) : null, selectedModel.contextWindow ? el('div', { className: 'oasub-status-detail' }, t('context.current', { count: selectedModel.contextWindow.toLocaleString() })) : null, el('div', { id: 'oasub-context-limit', className: 'oasub-status-detail' }, selectedModel.maxContextWindow ? t('context.maximum', { count: selectedModel.maxContextWindow.toLocaleString() }) + ' · ' + t('context.limit.help') : t('context.unknown')), contextDirty && contextValidation ? el('div', { role: 'alert', className: 'oasub-status-detail' }, t(contextValidation)) : null, contextDirty ? el('div', { className: 'oasub-actions' }, el('button', { type: 'button', className: 'oasub-button primary', disabled: busy || contextLoading || statusLoading || contextValidation !== null,
1199
- onClick: saveContext }, t(phase === 'saving-context' ? 'context.saving' : 'context.save'))) : null) : null) : null, statusError !== null
1200
- ? el('div', { className: 'oasub-notice error', role: 'alert' }, t(statusError), info !== null ? ' ' + t('status.stale') : null, el('button', { type: 'button', className: 'oasub-button quiet', disabled: statusLoading, onClick: reloadStatus }, t('action.retry'))) : null, el('button', { ref: reloadRef, type: 'button', className: 'oasub-button disclosure',
1201
- 'aria-expanded': managementOpen, 'aria-controls': managementOpen ? 'oasub-management' : undefined,
1202
- onClick: () => setManagementOpen((open) => !open) }, t(managementOpen ? 'action.less' : 'action.more')), managementOpen ? el('section', { id: 'oasub-management', className: 'oasub-management', 'aria-label': t('action.more') }, configured && info?.credentialState === 'valid' ? el('div', { className: 'oasub-manage-row' }, el('div', { className: 'oasub-status-detail', id: 'oasub-refresh-help' }, t(info.refreshable ? 'manage.refresh.help' : 'manage.reconnect.help')), el('button', { type: 'button', className: 'oasub-button', disabled: busy || !ready,
1203
- 'aria-describedby': 'oasub-refresh-help',
1204
- onClick: () => startAuthorization(info.refreshable ? 'refresh' : 'device_code') }, phase === 'starting' || phase === 'authorizing' ? t('action.refreshing') : t(info.refreshable ? 'action.refresh' : 'action.reconnect'))) : null, el('div', { className: 'oasub-manage-row' }, el('div', { className: 'oasub-status-detail', id: 'oasub-reload-help' }, t('manage.reload.help')), el('button', { type: 'button', className: 'oasub-button', disabled: statusLoading,
1205
- 'aria-describedby': 'oasub-reload-help', onClick: reloadStatus }, t(statusLoading ? 'status.reloading' : 'action.reload'))), info?.cleanupAvailable ? el('div', { className: 'oasub-manage-row' }, el('div', { className: 'oasub-status-detail', id: 'oasub-disconnect-help' }, t('manage.disconnect.help')), el('button', { type: 'button', className: 'oasub-button', disabled: busy,
1206
- 'aria-describedby': 'oasub-disconnect-help', onClick: () => openConfirm('disconnect') }, t(phase === 'disconnecting' ? 'action.disconnecting' : 'action.disconnect'))) : null) : null, device !== null
1361
+ return el('div', { className: 'oasub-wrap' }, el('header', { className: 'oasub-header' }, el('div', { className: 'oasub-mark', 'aria-hidden': true }, el(OpenAIIcon, { size: 24 })), el('div', { className: 'oasub-heading' }, el('h2', { className: 'oasub-title' }, t('title')), el('p', { className: 'oasub-subtitle' }, t('subtitle')))), statusCard, configured ? el('section', { className: 'oasub-card oasub-context', 'aria-labelledby': 'oasub-context-title' }, el('div', { className: 'oasub-section-heading' }, el('div', { className: 'oasub-section-copy' }, el('div', { id: 'oasub-context-title', className: 'oasub-status-title' }, t('context.title')), el('div', { className: 'oasub-status-detail' }, t('context.help')))), contextLoading ? el('div', { role: 'status', className: 'oasub-status-detail' }, t('context.loading')) : null, contextError ? el('div', { role: 'alert', className: 'oasub-notice error' }, t(contextError), el('button', { type: 'button', className: 'oasub-button quiet', disabled: contextLoading, onClick: reloadStatus }, t('action.retry'))) : null, !contextLoading && contexts?.models.length === 0 ? el('div', { className: 'oasub-status-detail' }, t('context.empty')) : null, selectedModel ? el('div', { className: 'oasub-context' }, el('div', { className: 'oasub-context-grid' }, renderPicker('oasub-model', t('context.model'), modelId, (contexts?.models ?? []).map((model) => ({
1362
+ value: model.id, label: model.name || model.id,
1363
+ detail: model.name && model.name !== model.id ? model.id : undefined,
1364
+ })), (value) => { setModelId(value); setContextDraft(null); setContextChoice(null); }), renderPicker('oasub-window', t('context.window'), contextMode, [
1365
+ { value: 'default', label: t('context.default'), detail: selectedModel.defaultContextWindow ? selectedModel.defaultContextWindow.toLocaleString() + ' tokens' : t('context.default.detail') },
1366
+ { value: 'million', label: t('context.million'), detail: '1,000,000 tokens', disabled: selectedModel.maxContextWindow !== undefined && selectedModel.maxContextWindow < 1_000_000 },
1367
+ { value: 'custom', label: t('context.custom'), detail: t('context.custom.detail') },
1368
+ ], (value) => {
1369
+ const choice = value === 'default' ? 'default' : value === 'million' ? 'million' : 'custom';
1370
+ setContextChoice(choice);
1371
+ setContextDraft(choice === 'default' ? '' : choice === 'million' ? '1000000' : '-');
1372
+ }, 'oasub-context-limit')), contextMode === 'custom' ? el('label', { className: 'oasub-field' }, t('context.tokens'), el('span', { className: 'oasub-input-shell' }, el('input', { className: 'oasub-token-input', type: 'text', inputMode: 'numeric', 'aria-label': t('context.tokens'),
1373
+ value: draft === '-' ? '' : draft, disabled: contextDisabled, placeholder: '1000000',
1374
+ 'aria-invalid': contextValidation !== null, 'aria-describedby': 'oasub-context-limit' + (contextDirty && contextValidation ? ' oasub-context-error' : ''),
1375
+ onChange: (event) => setContextDraft(event.currentTarget.value || '-') }), el('span', { className: 'oasub-token-unit', 'aria-hidden': true }, 'tokens'))) : null, el('div', { id: 'oasub-context-limit', className: 'oasub-context-note' }, uiIcon('info', 13), el('span', null, selectedModel.maxContextWindow ? t('context.maximum', { count: selectedModel.maxContextWindow.toLocaleString() }) + ' · ' + t('context.limit.help') : t('context.unknown'))), contextDirty && contextValidation ? el('div', { id: 'oasub-context-error', role: 'alert', className: 'oasub-context-error' }, t(contextValidation)) : null, el('div', { className: 'oasub-context-footer' }, el('div', { className: 'oasub-context-meta' }, selectedModel.contextWindow ? el('span', null, t('context.current', { count: selectedModel.contextWindow.toLocaleString() })) : null, contextDirty ? el('span', { className: 'oasub-context-badge' }, t('context.pending'))
1376
+ : selectedModel.customized ? el('span', { className: 'oasub-context-badge' }, uiIcon('check', 12), t('context.applied')) : null), contextDirty ? el('button', { type: 'button', className: 'oasub-button primary', disabled: contextDisabled || contextValidation !== null,
1377
+ onClick: saveContext }, t(phase === 'saving-context' ? 'context.saving' : 'context.save')) : null)) : null) : null, statusError !== null
1378
+ ? el('div', { className: 'oasub-notice error', role: 'alert' }, t(statusError), info !== null ? ' ' + t('status.stale') : null, el('button', { type: 'button', className: 'oasub-button quiet', disabled: statusLoading, onClick: reloadStatus }, t('action.retry'))) : null, el('section', { className: 'oasub-card oasub-management-card', 'aria-label': t('action.more') }, el('h3', { className: 'oasub-management-heading' }, el('button', { ref: reloadRef, type: 'button', className: 'oasub-management-toggle',
1379
+ 'aria-label': t(managementOpen ? 'action.less' : 'action.more'),
1380
+ 'aria-expanded': managementOpen, 'aria-controls': 'oasub-management',
1381
+ onClick: () => setManagementOpen((open) => !open) }, el('span', { className: 'oasub-management-symbol' }, uiIcon('settings', 18)), el('span', { className: 'oasub-section-copy' }, el('span', { className: 'oasub-status-title' }, t('action.more')), el('span', { className: 'oasub-status-detail' }, t('manage.help'))), uiIcon('chevron'))), el('div', { id: 'oasub-management', className: 'oasub-management', hidden: !managementOpen }, managementOpen ? [
1382
+ configured && info?.credentialState === 'valid' ? el('div', { key: 'refresh', className: 'oasub-manage-row' }, el('div', { className: 'oasub-section-copy' }, el('div', { className: 'oasub-manage-title' }, t(info.refreshable ? 'action.refresh' : 'action.reconnect')), el('div', { className: 'oasub-status-detail', id: 'oasub-refresh-help' }, t(info.refreshable ? 'manage.refresh.help' : 'manage.reconnect.help'))), el('button', { type: 'button', className: 'oasub-button', disabled: busy || !ready,
1383
+ 'aria-describedby': 'oasub-refresh-help',
1384
+ onClick: () => startAuthorization(info.refreshable ? 'refresh' : 'device_code') }, phase === 'starting' || phase === 'authorizing' ? t('action.refreshing') : t(info.refreshable ? 'action.refresh' : 'action.reconnect'))) : null,
1385
+ el('div', { key: 'reload', className: 'oasub-manage-row' }, el('div', { className: 'oasub-section-copy' }, el('div', { className: 'oasub-manage-title' }, t('action.reload')), el('div', { className: 'oasub-status-detail', id: 'oasub-reload-help' }, t('manage.reload.help'))), el('button', { type: 'button', className: 'oasub-button', disabled: statusLoading,
1386
+ 'aria-describedby': 'oasub-reload-help', onClick: reloadStatus }, t(statusLoading ? 'status.reloading' : 'action.reload'))),
1387
+ info?.cleanupAvailable ? el('div', { key: 'disconnect', className: 'oasub-manage-row' }, el('div', { className: 'oasub-section-copy' }, el('div', { className: 'oasub-manage-title' }, t('action.disconnect')), el('div', { className: 'oasub-status-detail', id: 'oasub-disconnect-help' }, t('manage.disconnect.help'))), el('button', { type: 'button', className: 'oasub-button danger-quiet', disabled: busy,
1388
+ 'aria-describedby': 'oasub-disconnect-help', onClick: () => openConfirm('disconnect') }, t(phase === 'disconnecting' ? 'action.disconnecting' : 'action.disconnect'))) : null,
1389
+ ] : null)), device !== null
1207
1390
  ? el('section', { className: 'oasub-card oasub-device', 'aria-label': t('device.title') }, el('div', { className: 'oasub-device-head' }, el('div', { className: 'oasub-status-title' }, t('device.title')), el('div', { className: 'oasub-status-detail' }, t('device.detail'))), el('input', { ref: codeRef, className: 'oasub-code', type: 'text', readOnly: true, value: device.code,
1208
1391
  'aria-label': t('device.code'), autoComplete: 'off', spellCheck: false, onFocus: (event) => event.currentTarget.select() }), el('div', { className: 'oasub-actions' }, device.url !== null
1209
1392
  ? el('a', { className: 'oasub-button primary', href: device.url, target: '_blank', rel: 'noopener noreferrer',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-openai-subscription",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "ChatGPT subscription sign-in for DeepSeek Harness via OpenAI device authorization.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.6.3",