dsh-client-auto-continue 0.10.2 → 0.11.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.
@@ -154,6 +154,67 @@ export class AutoContinueSettingsCardController {
154
154
  export type AutoContinueSettingsCardProps =
155
155
  PropsRuntime<'settings.plugin.item'> & PropsLocale<'auto-continue'> & InjectFace<AutoContinueSettingsCardFace>;
156
156
 
157
+ const REPOSITORY_URL = 'https://github.com/HsiangNianian/dsh-auto-continue';
158
+ const REPOSITORY_SLUG = 'HsiangNianian/dsh-auto-continue';
159
+
160
+ function RelayMark() {
161
+ return (
162
+ <svg viewBox="0 0 48 48" aria-hidden="true" focusable="false">
163
+ <path className="dshAcRelayArc" d="M9 30c4-12 10-18 19-18 5 0 9 2 12 6" />
164
+ <path className="dshAcRelayArc dshAcRelayArcEcho" d="M8 35c6 3 12 3 17 0 5-3 8-8 15-9" />
165
+ <circle className="dshAcRelayNode dshAcRelayNodeStart" cx="9" cy="30" r="3" />
166
+ <circle className="dshAcRelayNode dshAcRelayNodeEnd" cx="40" cy="18" r="3" />
167
+ <circle className="dshAcRelayPulse" cx="28" cy="12" r="2.5" />
168
+ </svg>
169
+ );
170
+ }
171
+
172
+ function ChevronMark() {
173
+ return (
174
+ <svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
175
+ <path d="m3.5 6 4.5 4 4.5-4" />
176
+ </svg>
177
+ );
178
+ }
179
+
180
+ function GitHubMark() {
181
+ return (
182
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
183
+ <path d="M12 2.4a9.8 9.8 0 0 0-3.1 19.1c.5.1.7-.2.7-.5v-1.9c-2.8.6-3.4-1.2-3.4-1.2-.5-1.1-1.1-1.4-1.1-1.4-.9-.6.1-.6.1-.6 1 0 1.5 1 1.5 1 .9 1.5 2.3 1.1 2.9.8.1-.6.4-1.1.6-1.3-2.2-.3-4.6-1.1-4.6-4.9 0-1.1.4-2 1-2.7-.1-.3-.4-1.3.1-2.7 0 0 .8-.3 2.8 1a9.5 9.5 0 0 1 5 0c1.9-1.3 2.8-1 2.8-1 .5 1.4.2 2.4.1 2.7.6.7 1 1.6 1 2.7 0 3.8-2.3 4.6-4.6 4.9.4.3.7.9.7 1.8V21c0 .3.2.6.7.5A9.8 9.8 0 0 0 12 2.4Z" />
184
+ </svg>
185
+ );
186
+ }
187
+
188
+ function ExternalMark() {
189
+ return (
190
+ <svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
191
+ <path d="M6 3h7v7M13 3 7 9" />
192
+ <path d="M11 9v3a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h3" />
193
+ </svg>
194
+ );
195
+ }
196
+
197
+ function RelayJourney(props: { t: (key: SettingsCardKey) => string }) {
198
+ return (
199
+ <span className="dshAcJourney">
200
+ <span className="dshAcJourneyStep dshAcJourneyInterrupted">
201
+ <span className="dshAcJourneyDot" aria-hidden="true" />
202
+ {props.t('flow.interrupted')}
203
+ </span>
204
+ <span className="dshAcJourneyLine" aria-hidden="true" />
205
+ <span className="dshAcJourneyStep dshAcJourneyGrace">
206
+ <span className="dshAcJourneyDot" aria-hidden="true" />
207
+ {props.t('flow.grace')}
208
+ </span>
209
+ <span className="dshAcJourneyLine" aria-hidden="true" />
210
+ <span className="dshAcJourneyStep dshAcJourneyContinue">
211
+ <span className="dshAcJourneyDot" aria-hidden="true" />
212
+ {props.t('flow.continue')}
213
+ </span>
214
+ </span>
215
+ );
216
+ }
217
+
157
218
  /** Card chrome: a disclosure header naming the plugin and what its settings govern, the controls, and the save that writes them. */
158
219
  function SettingsCard(props: {
159
220
  t: (key: SettingsCardKey) => string;
@@ -171,25 +232,46 @@ function SettingsCard(props: {
171
232
  const blocked = !state.dirty || state.invalid || state.saving;
172
233
  return (
173
234
  <li className={open ? 'dshAcCard dshAcCardOpen' : 'dshAcCard'}>
174
- <button
175
- type="button"
176
- className="dshAcHeader"
177
- aria-expanded={open}
178
- aria-label={`${props.t(open ? 'chrome.collapse' : 'chrome.expand')}: ${title}`}
179
- title={props.t(props.descriptionKey)}
180
- onClick={() => setOpen(!open)}
181
- >
182
- <span className="dshAcHeadText">
183
- <span className="dshAcName">{title}</span>
184
- <span className="dshAcDescription">{props.t(props.descriptionKey)}</span>
185
- </span>
186
- {state.dirty ? (
187
- <span className="dshAcPending" title={props.t('chrome.unsaved')}>
188
- {props.t('chrome.unsaved')}
235
+ <div className="dshAcHeaderFrame">
236
+ <button
237
+ type="button"
238
+ className="dshAcHeader"
239
+ aria-expanded={open}
240
+ aria-label={`${props.t(open ? 'chrome.collapse' : 'chrome.expand')}: ${title}`}
241
+ title={props.t(props.descriptionKey)}
242
+ onClick={() => setOpen(!open)}
243
+ >
244
+ <span className="dshAcRelayMark"><RelayMark /></span>
245
+ <span className="dshAcHeadText">
246
+ <span className="dshAcEyebrow">{props.t('card.eyebrow')}</span>
247
+ <span className="dshAcName">{title}</span>
248
+ <span className="dshAcDescription">{props.t(props.descriptionKey)}</span>
249
+ <RelayJourney t={props.t} />
189
250
  </span>
190
- ) : null}
191
- <span className={open ? 'dshAcChevron dshAcChevronOpen' : 'dshAcChevron'}>▾</span>
192
- </button>
251
+ {state.dirty ? (
252
+ <span className="dshAcPending" title={props.t('chrome.unsaved')}>
253
+ {props.t('chrome.unsaved')}
254
+ </span>
255
+ ) : null}
256
+ <span className={open ? 'dshAcChevron dshAcChevronOpen' : 'dshAcChevron'}>
257
+ <ChevronMark />
258
+ </span>
259
+ </button>
260
+ <a
261
+ className="dshAcGithub"
262
+ href={REPOSITORY_URL}
263
+ target="_blank"
264
+ rel="noreferrer"
265
+ aria-label={props.t('repo.aria')}
266
+ >
267
+ <span className="dshAcGithubIcon"><GitHubMark /></span>
268
+ <span className="dshAcGithubText">
269
+ <span className="dshAcGithubAction">{props.t('repo.star')}</span>
270
+ <span className="dshAcGithubSlug">{REPOSITORY_SLUG}</span>
271
+ </span>
272
+ <span className="dshAcExternal"><ExternalMark /></span>
273
+ </a>
274
+ </div>
193
275
  {open ? (
194
276
  <div className="dshAcBody">
195
277
  {!state.writable ? (
@@ -223,6 +305,7 @@ interface FieldProps {
223
305
  id: string;
224
306
  label: string;
225
307
  hint: string;
308
+ wide?: boolean;
226
309
  text: string;
227
310
  overridden: boolean;
228
311
  invalid: boolean;
@@ -236,7 +319,7 @@ interface FieldProps {
236
319
  function ValueField(props: FieldProps & { numeric?: boolean; multiline?: boolean; placeholder?: string }) {
237
320
  const className = props.invalid ? 'dshAcInput dshAcInputInvalid' : 'dshAcInput';
238
321
  return (
239
- <div className="dshAcField">
322
+ <div className={props.wide === true ? 'dshAcField dshAcFieldWide' : 'dshAcField'}>
240
323
  <div className="dshAcHead">
241
324
  <label className="dshAcLabel" htmlFor={props.id}>{props.label}</label>
242
325
  {props.overridden ? (
@@ -282,7 +365,7 @@ function ValueField(props: FieldProps & { numeric?: boolean; multiline?: boolean
282
365
  /** A staged boolean field: inherit / on / off. */
283
366
  function BooleanField(props: FieldProps) {
284
367
  return (
285
- <div className="dshAcField">
368
+ <div className={props.wide === true ? 'dshAcField dshAcFieldWide' : 'dshAcField'}>
286
369
  <div className="dshAcHead">
287
370
  <label className="dshAcLabel" htmlFor={props.id}>{props.label}</label>
288
371
  {props.overridden ? (
@@ -310,6 +393,30 @@ function BooleanField(props: FieldProps) {
310
393
  );
311
394
  }
312
395
 
396
+ type SettingsSectionTone = 'handoff' | 'safety' | 'recovery' | 'loop' | 'live';
397
+
398
+ /** A real behavior group, not a decorative divider: each section maps to one phase of the relay. */
399
+ function SettingsSection(props: {
400
+ t: (key: SettingsCardKey) => string;
401
+ titleKey: SettingsCardKey;
402
+ descriptionKey: SettingsCardKey;
403
+ tone: SettingsSectionTone;
404
+ children: ReactNode;
405
+ }) {
406
+ return (
407
+ <section className={`dshAcFormSection dshAcFormSection-${props.tone}`}>
408
+ <header className="dshAcSectionHead">
409
+ <span className="dshAcSectionSignal" aria-hidden="true" />
410
+ <span className="dshAcSectionCopy">
411
+ <span className="dshAcSectionTitle">{props.t(props.titleKey)}</span>
412
+ <span className="dshAcSectionDescription">{props.t(props.descriptionKey)}</span>
413
+ </span>
414
+ </header>
415
+ <div className="dshAcSectionGrid">{props.children}</div>
416
+ </section>
417
+ );
418
+ }
419
+
313
420
  /** 实时面板: 今日统计 + 已暂停会话。浏览器本地状态, 每 5 秒刷新一次。 */
314
421
  function LivePanels(props: { t: (key: SettingsCardKey) => string }) {
315
422
  const { t } = props;
@@ -436,251 +543,299 @@ export function AutoContinueSettingsCard(props: AutoContinueSettingsCardProps) {
436
543
  onSave={props.save}
437
544
  onDiscard={props.discard}
438
545
  >
439
- <BooleanField
440
- id="auto-continue-paused"
441
- label={t('field.paused')}
442
- hint={t('field.pausedHint')}
443
- {...shared}
444
- {...state.paused}
445
- onEdit={(text) => props.edit('paused', text)}
446
- onReset={() => props.resetField('paused')}
447
- />
448
- <ValueField
449
- id="auto-continue-continue-text"
450
- label={t('field.continueText')}
451
- hint={t('field.continueTextHint')}
452
- {...shared}
453
- {...state.continueText}
454
- onEdit={(text) => props.edit('continueText', text)}
455
- placeholder={t('default.continueText')}
456
- onReset={() => props.resetField('continueText')}
457
- />
458
- <ValueField
459
- id="auto-continue-continue-text-max-tokens"
460
- label={t('field.continueTextMaxTokens')}
461
- hint={t('field.continueTextMaxTokensHint')}
462
- {...shared}
463
- {...state.continueTextMaxTokens}
464
- onEdit={(text) => props.edit('continueTextMaxTokens', text)}
465
- placeholder={t('default.continueTextMaxTokens')}
466
- onReset={() => props.resetField('continueTextMaxTokens')}
467
- />
468
- <BooleanField
469
- id="auto-continue-guard-tools"
470
- label={t('field.guardTools')}
471
- hint={t('field.guardToolsHint')}
472
- {...shared}
473
- {...state.guardTools}
474
- onEdit={(text) => props.edit('guardTools', text)}
475
- onReset={() => props.resetField('guardTools')}
476
- />
477
- <ValueField
478
- id="auto-continue-guard-pending-text"
479
- label={t('field.guardPendingText')}
480
- hint={t('field.guardPendingTextHint')}
481
- {...shared}
482
- {...state.guardPendingText}
483
- onEdit={(text) => props.edit('guardPendingText', text)}
484
- placeholder={t('default.guardPendingText')}
485
- onReset={() => props.resetField('guardPendingText')}
486
- />
487
- <ValueField
488
- id="auto-continue-guard-done-text"
489
- label={t('field.guardDoneText')}
490
- hint={t('field.guardDoneTextHint')}
491
- {...shared}
492
- {...state.guardDoneText}
493
- onEdit={(text) => props.edit('guardDoneText', text)}
494
- placeholder={t('default.guardDoneText')}
495
- onReset={() => props.resetField('guardDoneText')}
496
- />
497
- <ValueField
498
- id="auto-continue-grace-ms"
499
- label={t('field.graceMs')}
500
- hint={t('field.graceMsHint')}
501
- numeric
502
- {...shared}
503
- {...state.graceMs}
504
- onEdit={(text) => props.edit('graceMs', text)}
505
- onReset={() => props.resetField('graceMs')}
506
- />
507
- <ValueField
508
- id="auto-continue-cooldown-ms"
509
- label={t('field.cooldownMs')}
510
- hint={t('field.cooldownMsHint')}
511
- numeric
512
- {...shared}
513
- {...state.cooldownMs}
514
- onEdit={(text) => props.edit('cooldownMs', text)}
515
- onReset={() => props.resetField('cooldownMs')}
516
- />
517
- <ValueField
518
- id="auto-continue-max-consecutive"
519
- label={t('field.maxConsecutive')}
520
- hint={t('field.maxConsecutiveHint')}
521
- numeric
522
- {...shared}
523
- {...state.maxConsecutive}
524
- onEdit={(text) => props.edit('maxConsecutive', text)}
525
- onReset={() => props.resetField('maxConsecutive')}
526
- />
527
- <BooleanField
528
- id="auto-continue-scan-on-boot"
529
- label={t('field.scanOnBoot')}
530
- hint={t('field.scanOnBootHint')}
531
- {...shared}
532
- {...state.scanOnBoot}
533
- onEdit={(text) => props.edit('scanOnBoot', text)}
534
- onReset={() => props.resetField('scanOnBoot')}
535
- />
536
- <ValueField
537
- id="auto-continue-scan-limit"
538
- label={t('field.scanLimit')}
539
- hint={t('field.scanLimitHint')}
540
- numeric
541
- {...shared}
542
- {...state.scanLimit}
543
- onEdit={(text) => props.edit('scanLimit', text)}
544
- onReset={() => props.resetField('scanLimit')}
545
- />
546
- <ValueField
547
- id="auto-continue-fresh-ms"
548
- label={t('field.freshMs')}
549
- hint={t('field.freshMsHint')}
550
- numeric
551
- {...shared}
552
- {...state.freshMs}
553
- onEdit={(text) => props.edit('freshMs', text)}
554
- onReset={() => props.resetField('freshMs')}
555
- />
556
- <BooleanField
557
- id="auto-continue-verbose"
558
- label={t('field.verbose')}
559
- hint={t('field.verboseHint')}
560
- {...shared}
561
- {...state.verbose}
562
- onEdit={(text) => props.edit('verbose', text)}
563
- onReset={() => props.resetField('verbose')}
564
- />
565
- <BooleanField
566
- id="auto-continue-classify"
567
- label={t('field.classify')}
568
- hint={t('field.classifyHint')}
569
- {...shared}
570
- {...state.classify}
571
- onEdit={(text) => props.edit('classify', text)}
572
- onReset={() => props.resetField('classify')}
573
- />
574
- <ValueField
575
- id="auto-continue-retryable-error-patterns"
576
- label={t('field.retryableErrorPatterns')}
577
- hint={t('field.retryableErrorPatternsHint')}
578
- multiline
579
- {...shared}
580
- {...state.retryableErrorPatterns}
581
- onEdit={(text) => props.edit('retryableErrorPatterns', text)}
582
- placeholder="Upstream rejected the request as invalid"
583
- onReset={() => props.resetField('retryableErrorPatterns')}
584
- />
585
- <ValueField
586
- id="auto-continue-backoff-factor"
587
- label={t('field.backoffFactor')}
588
- hint={t('field.backoffFactorHint')}
589
- numeric
590
- {...shared}
591
- {...state.backoffFactor}
592
- onEdit={(text) => props.edit('backoffFactor', text)}
593
- onReset={() => props.resetField('backoffFactor')}
594
- />
595
- <ValueField
596
- id="auto-continue-backoff-max"
597
- label={t('field.backoffMaxMs')}
598
- hint={t('field.backoffMaxMsHint')}
599
- numeric
600
- {...shared}
601
- {...state.backoffMaxMs}
602
- onEdit={(text) => props.edit('backoffMaxMs', text)}
603
- onReset={() => props.resetField('backoffMaxMs')}
604
- />
605
- <BooleanField
606
- id="auto-continue-notify"
607
- label={t('field.notify')}
608
- hint={t('field.notifyHint')}
609
- {...shared}
610
- {...state.notify}
611
- onEdit={(text) => props.edit('notify', text)}
612
- onReset={() => props.resetField('notify')}
613
- />
614
- <BooleanField
615
- id="auto-continue-loop-guard"
616
- label={t('field.loopGuard')}
617
- hint={t('field.loopGuardHint')}
618
- {...shared}
619
- {...state.loopGuard}
620
- onEdit={(text) => props.edit('loopGuard', text)}
621
- onReset={() => props.resetField('loopGuard')}
622
- />
623
- <ValueField
624
- id="auto-continue-loop-short-chars"
625
- label={t('field.loopShortChars')}
626
- hint={t('field.loopShortCharsHint')}
627
- numeric
628
- {...shared}
629
- {...state.loopShortChars}
630
- onEdit={(text) => props.edit('loopShortChars', text)}
631
- onReset={() => props.resetField('loopShortChars')}
632
- />
633
- <ValueField
634
- id="auto-continue-loop-window-ms"
635
- label={t('field.loopWindowMs')}
636
- hint={t('field.loopWindowMsHint')}
637
- numeric
638
- {...shared}
639
- {...state.loopWindowMs}
640
- onEdit={(text) => props.edit('loopWindowMs', text)}
641
- onReset={() => props.resetField('loopWindowMs')}
642
- />
643
- <ValueField
644
- id="auto-continue-loop-short-count"
645
- label={t('field.loopShortCount')}
646
- hint={t('field.loopShortCountHint')}
647
- numeric
648
- {...shared}
649
- {...state.loopShortCount}
650
- onEdit={(text) => props.edit('loopShortCount', text)}
651
- onReset={() => props.resetField('loopShortCount')}
652
- />
653
- <ValueField
654
- id="auto-continue-loop-tool-repeat"
655
- label={t('field.loopToolRepeat')}
656
- hint={t('field.loopToolRepeatHint')}
657
- numeric
658
- {...shared}
659
- {...state.loopToolRepeat}
660
- onEdit={(text) => props.edit('loopToolRepeat', text)}
661
- onReset={() => props.resetField('loopToolRepeat')}
662
- />
663
- <ValueField
664
- id="auto-continue-loop-repeat-text"
665
- label={t('field.loopRepeatText')}
666
- hint={t('field.loopRepeatTextHint')}
667
- numeric
668
- {...shared}
669
- {...state.loopRepeatText}
670
- onEdit={(text) => props.edit('loopRepeatText', text)}
671
- onReset={() => props.resetField('loopRepeatText')}
672
- />
673
- <ValueField
674
- id="auto-continue-loop-text"
675
- label={t('field.loopText')}
676
- hint={t('field.loopTextHint')}
677
- {...shared}
678
- {...state.loopText}
679
- onEdit={(text) => props.edit('loopText', text)}
680
- placeholder={t('default.loopText')}
681
- onReset={() => props.resetField('loopText')}
682
- />
683
- <LivePanels t={t} />
546
+ <div className="dshAcFormCanvas">
547
+ <SettingsSection
548
+ t={t}
549
+ titleKey="section.handoff.title"
550
+ descriptionKey="section.handoff.description"
551
+ tone="handoff"
552
+ >
553
+ <BooleanField
554
+ wide
555
+ id="auto-continue-paused"
556
+ label={t('field.paused')}
557
+ hint={t('field.pausedHint')}
558
+ {...shared}
559
+ {...state.paused}
560
+ onEdit={(text) => props.edit('paused', text)}
561
+ onReset={() => props.resetField('paused')}
562
+ />
563
+ <ValueField
564
+ id="auto-continue-continue-text"
565
+ label={t('field.continueText')}
566
+ hint={t('field.continueTextHint')}
567
+ {...shared}
568
+ {...state.continueText}
569
+ onEdit={(text) => props.edit('continueText', text)}
570
+ placeholder={t('default.continueText')}
571
+ onReset={() => props.resetField('continueText')}
572
+ />
573
+ <ValueField
574
+ id="auto-continue-continue-text-max-tokens"
575
+ label={t('field.continueTextMaxTokens')}
576
+ hint={t('field.continueTextMaxTokensHint')}
577
+ {...shared}
578
+ {...state.continueTextMaxTokens}
579
+ onEdit={(text) => props.edit('continueTextMaxTokens', text)}
580
+ placeholder={t('default.continueTextMaxTokens')}
581
+ onReset={() => props.resetField('continueTextMaxTokens')}
582
+ />
583
+ </SettingsSection>
584
+
585
+ <SettingsSection
586
+ t={t}
587
+ titleKey="section.safety.title"
588
+ descriptionKey="section.safety.description"
589
+ tone="safety"
590
+ >
591
+ <BooleanField
592
+ wide
593
+ id="auto-continue-guard-tools"
594
+ label={t('field.guardTools')}
595
+ hint={t('field.guardToolsHint')}
596
+ {...shared}
597
+ {...state.guardTools}
598
+ onEdit={(text) => props.edit('guardTools', text)}
599
+ onReset={() => props.resetField('guardTools')}
600
+ />
601
+ <ValueField
602
+ wide
603
+ id="auto-continue-guard-pending-text"
604
+ label={t('field.guardPendingText')}
605
+ hint={t('field.guardPendingTextHint')}
606
+ {...shared}
607
+ {...state.guardPendingText}
608
+ onEdit={(text) => props.edit('guardPendingText', text)}
609
+ placeholder={t('default.guardPendingText')}
610
+ onReset={() => props.resetField('guardPendingText')}
611
+ />
612
+ <ValueField
613
+ wide
614
+ id="auto-continue-guard-done-text"
615
+ label={t('field.guardDoneText')}
616
+ hint={t('field.guardDoneTextHint')}
617
+ {...shared}
618
+ {...state.guardDoneText}
619
+ onEdit={(text) => props.edit('guardDoneText', text)}
620
+ placeholder={t('default.guardDoneText')}
621
+ onReset={() => props.resetField('guardDoneText')}
622
+ />
623
+ <ValueField
624
+ id="auto-continue-grace-ms"
625
+ label={t('field.graceMs')}
626
+ hint={t('field.graceMsHint')}
627
+ numeric
628
+ {...shared}
629
+ {...state.graceMs}
630
+ onEdit={(text) => props.edit('graceMs', text)}
631
+ onReset={() => props.resetField('graceMs')}
632
+ />
633
+ <ValueField
634
+ id="auto-continue-cooldown-ms"
635
+ label={t('field.cooldownMs')}
636
+ hint={t('field.cooldownMsHint')}
637
+ numeric
638
+ {...shared}
639
+ {...state.cooldownMs}
640
+ onEdit={(text) => props.edit('cooldownMs', text)}
641
+ onReset={() => props.resetField('cooldownMs')}
642
+ />
643
+ <ValueField
644
+ id="auto-continue-max-consecutive"
645
+ label={t('field.maxConsecutive')}
646
+ hint={t('field.maxConsecutiveHint')}
647
+ numeric
648
+ {...shared}
649
+ {...state.maxConsecutive}
650
+ onEdit={(text) => props.edit('maxConsecutive', text)}
651
+ onReset={() => props.resetField('maxConsecutive')}
652
+ />
653
+ </SettingsSection>
654
+
655
+ <SettingsSection
656
+ t={t}
657
+ titleKey="section.recovery.title"
658
+ descriptionKey="section.recovery.description"
659
+ tone="recovery"
660
+ >
661
+ <BooleanField
662
+ id="auto-continue-scan-on-boot"
663
+ label={t('field.scanOnBoot')}
664
+ hint={t('field.scanOnBootHint')}
665
+ {...shared}
666
+ {...state.scanOnBoot}
667
+ onEdit={(text) => props.edit('scanOnBoot', text)}
668
+ onReset={() => props.resetField('scanOnBoot')}
669
+ />
670
+ <BooleanField
671
+ id="auto-continue-classify"
672
+ label={t('field.classify')}
673
+ hint={t('field.classifyHint')}
674
+ {...shared}
675
+ {...state.classify}
676
+ onEdit={(text) => props.edit('classify', text)}
677
+ onReset={() => props.resetField('classify')}
678
+ />
679
+ <ValueField
680
+ id="auto-continue-scan-limit"
681
+ label={t('field.scanLimit')}
682
+ hint={t('field.scanLimitHint')}
683
+ numeric
684
+ {...shared}
685
+ {...state.scanLimit}
686
+ onEdit={(text) => props.edit('scanLimit', text)}
687
+ onReset={() => props.resetField('scanLimit')}
688
+ />
689
+ <ValueField
690
+ id="auto-continue-fresh-ms"
691
+ label={t('field.freshMs')}
692
+ hint={t('field.freshMsHint')}
693
+ numeric
694
+ {...shared}
695
+ {...state.freshMs}
696
+ onEdit={(text) => props.edit('freshMs', text)}
697
+ onReset={() => props.resetField('freshMs')}
698
+ />
699
+ <ValueField
700
+ wide
701
+ id="auto-continue-retryable-error-patterns"
702
+ label={t('field.retryableErrorPatterns')}
703
+ hint={t('field.retryableErrorPatternsHint')}
704
+ multiline
705
+ {...shared}
706
+ {...state.retryableErrorPatterns}
707
+ onEdit={(text) => props.edit('retryableErrorPatterns', text)}
708
+ placeholder={t('field.retryableErrorPatternsPlaceholder')}
709
+ onReset={() => props.resetField('retryableErrorPatterns')}
710
+ />
711
+ <ValueField
712
+ id="auto-continue-backoff-factor"
713
+ label={t('field.backoffFactor')}
714
+ hint={t('field.backoffFactorHint')}
715
+ numeric
716
+ {...shared}
717
+ {...state.backoffFactor}
718
+ onEdit={(text) => props.edit('backoffFactor', text)}
719
+ onReset={() => props.resetField('backoffFactor')}
720
+ />
721
+ <ValueField
722
+ id="auto-continue-backoff-max"
723
+ label={t('field.backoffMaxMs')}
724
+ hint={t('field.backoffMaxMsHint')}
725
+ numeric
726
+ {...shared}
727
+ {...state.backoffMaxMs}
728
+ onEdit={(text) => props.edit('backoffMaxMs', text)}
729
+ onReset={() => props.resetField('backoffMaxMs')}
730
+ />
731
+ <BooleanField
732
+ id="auto-continue-notify"
733
+ label={t('field.notify')}
734
+ hint={t('field.notifyHint')}
735
+ {...shared}
736
+ {...state.notify}
737
+ onEdit={(text) => props.edit('notify', text)}
738
+ onReset={() => props.resetField('notify')}
739
+ />
740
+ <BooleanField
741
+ id="auto-continue-verbose"
742
+ label={t('field.verbose')}
743
+ hint={t('field.verboseHint')}
744
+ {...shared}
745
+ {...state.verbose}
746
+ onEdit={(text) => props.edit('verbose', text)}
747
+ onReset={() => props.resetField('verbose')}
748
+ />
749
+ </SettingsSection>
750
+
751
+ <SettingsSection
752
+ t={t}
753
+ titleKey="section.loop.title"
754
+ descriptionKey="section.loop.description"
755
+ tone="loop"
756
+ >
757
+ <BooleanField
758
+ wide
759
+ id="auto-continue-loop-guard"
760
+ label={t('field.loopGuard')}
761
+ hint={t('field.loopGuardHint')}
762
+ {...shared}
763
+ {...state.loopGuard}
764
+ onEdit={(text) => props.edit('loopGuard', text)}
765
+ onReset={() => props.resetField('loopGuard')}
766
+ />
767
+ <ValueField
768
+ id="auto-continue-loop-short-chars"
769
+ label={t('field.loopShortChars')}
770
+ hint={t('field.loopShortCharsHint')}
771
+ numeric
772
+ {...shared}
773
+ {...state.loopShortChars}
774
+ onEdit={(text) => props.edit('loopShortChars', text)}
775
+ onReset={() => props.resetField('loopShortChars')}
776
+ />
777
+ <ValueField
778
+ id="auto-continue-loop-window-ms"
779
+ label={t('field.loopWindowMs')}
780
+ hint={t('field.loopWindowMsHint')}
781
+ numeric
782
+ {...shared}
783
+ {...state.loopWindowMs}
784
+ onEdit={(text) => props.edit('loopWindowMs', text)}
785
+ onReset={() => props.resetField('loopWindowMs')}
786
+ />
787
+ <ValueField
788
+ id="auto-continue-loop-short-count"
789
+ label={t('field.loopShortCount')}
790
+ hint={t('field.loopShortCountHint')}
791
+ numeric
792
+ {...shared}
793
+ {...state.loopShortCount}
794
+ onEdit={(text) => props.edit('loopShortCount', text)}
795
+ onReset={() => props.resetField('loopShortCount')}
796
+ />
797
+ <ValueField
798
+ id="auto-continue-loop-repeat-text"
799
+ label={t('field.loopRepeatText')}
800
+ hint={t('field.loopRepeatTextHint')}
801
+ numeric
802
+ {...shared}
803
+ {...state.loopRepeatText}
804
+ onEdit={(text) => props.edit('loopRepeatText', text)}
805
+ onReset={() => props.resetField('loopRepeatText')}
806
+ />
807
+ <ValueField
808
+ id="auto-continue-loop-tool-repeat"
809
+ label={t('field.loopToolRepeat')}
810
+ hint={t('field.loopToolRepeatHint')}
811
+ numeric
812
+ {...shared}
813
+ {...state.loopToolRepeat}
814
+ onEdit={(text) => props.edit('loopToolRepeat', text)}
815
+ onReset={() => props.resetField('loopToolRepeat')}
816
+ />
817
+ <ValueField
818
+ wide
819
+ id="auto-continue-loop-text"
820
+ label={t('field.loopText')}
821
+ hint={t('field.loopTextHint')}
822
+ {...shared}
823
+ {...state.loopText}
824
+ onEdit={(text) => props.edit('loopText', text)}
825
+ placeholder={t('default.loopText')}
826
+ onReset={() => props.resetField('loopText')}
827
+ />
828
+ </SettingsSection>
829
+
830
+ <SettingsSection
831
+ t={t}
832
+ titleKey="section.live.title"
833
+ descriptionKey="section.live.description"
834
+ tone="live"
835
+ >
836
+ <LivePanels t={t} />
837
+ </SettingsSection>
838
+ </div>
684
839
  </SettingsCard>
685
840
  );
686
841
  }