@focus8/settings-registry 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -77,6 +77,47 @@ export type AllDayPreset = {
77
77
  time: string;
78
78
  };
79
79
 
80
+ // ---------------------------------------------------------------------------
81
+ // UI type discriminator — determines which component renders each setting
82
+ // ---------------------------------------------------------------------------
83
+
84
+ export type SettingUiType =
85
+ | 'TOGGLE'
86
+ | 'SELECT'
87
+ | 'VOLUME_SLIDER'
88
+ | 'SLIDER'
89
+ | 'NUMBER_INPUT'
90
+ | 'TEXT_INPUT'
91
+ | 'PIN_INPUT'
92
+ | 'CUSTOM_THEME_PICKER'
93
+ | 'CUSTOM_LANGUAGE_PICKER'
94
+ | 'CUSTOM_CALENDAR_TYPE'
95
+ | 'CUSTOM_CLOCK_TYPE'
96
+ | 'CUSTOM_WEATHER_LOCATION'
97
+ | 'CUSTOM_IMAGE'
98
+ | 'CUSTOM_IMAGE_ARRAY'
99
+ | 'CUSTOM_CALENDAR_IDS'
100
+ | 'CUSTOM_REMINDER_PRESETS'
101
+ | 'CUSTOM_EVENT_FORM'
102
+ | 'CUSTOM_CHRONOLOGICAL_EVENT_FORM'
103
+ | 'CUSTOM_DISPLAY_DENSITY'
104
+ | 'CUSTOM_BRIGHTNESS'
105
+ | 'CUSTOM_SPLIT_VIEW_CONFIG'
106
+ | 'HIDDEN';
107
+
108
+ export type SettingOption<T = unknown> = {
109
+ /** The option value */
110
+ value: T;
111
+ /** i18n message key for the option label */
112
+ labelKey: string;
113
+ };
114
+
115
+ export type SliderConfig = {
116
+ min: number;
117
+ max: number;
118
+ step: number;
119
+ };
120
+
80
121
  // ---------------------------------------------------------------------------
81
122
  // Setting categories — used for UI grouping and profile organization
82
123
  // ---------------------------------------------------------------------------
@@ -219,6 +260,8 @@ export type SettingsGroupDef = {
219
260
  keys: string[];
220
261
  /** Only show for this calendar type (undefined = always) */
221
262
  calendarType?: CalendarType;
263
+ /** Only show for this app mode (undefined = always) */
264
+ appMode?: 'ENROLLED';
222
265
  };
223
266
 
224
267
  /**
@@ -242,21 +285,18 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
242
285
  id: 'calendarType',
243
286
  labelKey: 'Settings.CalendarType',
244
287
  icon: 'ArrowLeftRight',
245
- keys: ['calendarView.type'],
288
+ keys: ['calendarView.type', 'calendarView.view'],
246
289
  },
247
290
  {
248
291
  id: 'calendarView',
249
292
  labelKey: 'Settings.CalendarView',
250
293
  icon: 'Eye',
251
294
  keys: [
252
- 'calendarView.view',
253
- 'calendarView.dayViewZoom',
254
- 'calendarView.weekViewZoom',
255
- 'calendarView.splitView',
256
295
  'calendarView.showCalendarNames',
296
+ 'calendarView.splitView',
257
297
  'calendarView.calendarColumns',
258
- 'calendarView.autoReturnToTodayEnabled',
259
- 'calendarView.autoReturnToTodayTimeoutSeconds',
298
+ 'calendarView.dayViewZoom',
299
+ 'calendarView.weekViewZoom',
260
300
  ],
261
301
  },
262
302
  {
@@ -301,12 +341,14 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
301
341
  'sound.timerAlarmTimeout',
302
342
  'timer.',
303
343
  ],
344
+ appMode: 'ENROLLED',
304
345
  },
305
346
  {
306
347
  id: 'lockScreen',
307
348
  labelKey: 'Settings.LockScreen',
308
349
  icon: 'Lock',
309
350
  keys: ['lockScreen.'],
351
+ appMode: 'ENROLLED',
310
352
  },
311
353
  {
312
354
  id: 'touch',
@@ -325,7 +367,11 @@ export const SETTINGS_GROUPS: readonly SettingsGroupDef[] = [
325
367
  id: 'device',
326
368
  labelKey: 'SettingsDevices.Title',
327
369
  icon: 'Smartphone',
328
- keys: ['device.'],
370
+ keys: [
371
+ 'device.',
372
+ 'calendarView.autoReturnToTodayEnabled',
373
+ 'calendarView.autoReturnToTodayTimeoutSeconds',
374
+ ],
329
375
  },
330
376
  {
331
377
  id: 'language',
@@ -346,6 +392,251 @@ export const EXCLUDED_DEVICE_SETTINGS: ReadonlySet<string> = new Set([
346
392
  'notification.hasBeenPrompted',
347
393
  ]);
348
394
 
395
+ // ---------------------------------------------------------------------------
396
+ // Per-setting i18n label / description keys
397
+ // ---------------------------------------------------------------------------
398
+
399
+ export type SettingLabelDef = {
400
+ /** i18n message key for the setting label */
401
+ labelKey: string;
402
+ /** Optional i18n message key for a description shown below the label */
403
+ descriptionKey?: string;
404
+ };
405
+
406
+ /**
407
+ * Maps each setting key to its i18n label and optional description key.
408
+ * These keys must exist in every consumer's i18n catalog.
409
+ *
410
+ * When a key is missing from this map, consumers should fall back to
411
+ * the auto-generated label from `SettingsRegistry.getSettingLabel()`.
412
+ */
413
+ export const SETTINGS_LABELS: Readonly<Record<string, SettingLabelDef>> = {
414
+ // ── Appearance ──────────────────────────────────────────────────────────
415
+ 'appearance.theme': { labelKey: 'Settings.SelectTheme' },
416
+ 'appearance.clockType': {
417
+ labelKey: 'Settings.ClockType',
418
+ descriptionKey: 'Settings.ClockTypeDescription',
419
+ },
420
+ 'appearance.enableDayColors': {
421
+ labelKey: 'Settings.EnableDayColors',
422
+ descriptionKey: 'Settings.EnableDayColorsDescription',
423
+ },
424
+
425
+ // ── Calendar type ───────────────────────────────────────────────────────
426
+ 'calendarView.type': {
427
+ labelKey: 'Settings.CalendarType',
428
+ descriptionKey: 'Settings.ModeDescription',
429
+ },
430
+ 'calendarView.view': { labelKey: 'Settings.Calendar' },
431
+
432
+ // ── Calendar view ───────────────────────────────────────────────────────
433
+ 'calendarView.showCalendarNames': { labelKey: 'Settings.ShowCalendarNames' },
434
+ 'calendarView.splitView': {
435
+ labelKey: 'Settings.SplitViewLabel',
436
+ descriptionKey: 'Settings.SplitViewDescription',
437
+ },
438
+ 'calendarView.calendarColumns': {
439
+ labelKey: 'Settings.SplitViewConfig',
440
+ descriptionKey: 'Settings.SplitViewConfigDescription',
441
+ },
442
+ 'calendarView.dayViewZoom': {
443
+ labelKey: 'Settings.CalendarDayViewIntervalTitle',
444
+ descriptionKey: 'Settings.CalendarDayViewIntervalDescription',
445
+ },
446
+ 'calendarView.weekViewZoom': {
447
+ labelKey: 'Settings.CalendarWeekViewIntervalTitle',
448
+ descriptionKey: 'Settings.CalendarWeekViewIntervalDescription',
449
+ },
450
+
451
+ // ── Auto return to today ────────────────────────────────────────────────
452
+ 'calendarView.autoReturnToTodayEnabled': {
453
+ labelKey: 'Settings.AutoReturnToTodayEnabled',
454
+ },
455
+ 'calendarView.autoReturnToTodayTimeoutSeconds': {
456
+ labelKey: 'Settings.AutoReturnToTodayTimeout',
457
+ },
458
+
459
+ // ── Weather ─────────────────────────────────────────────────────────────
460
+ 'calendarView.weatherLocation': { labelKey: 'Settings.WeatherLocation' },
461
+ 'calendarView.showWeatherOnEvents': {
462
+ labelKey: 'Settings.ShowWeatherOnEvents',
463
+ descriptionKey: 'Settings.ShowWeatherOnEventsDescription',
464
+ },
465
+ 'calendarView.showWeatherOnTimeline': {
466
+ labelKey: 'Settings.ShowWeatherOnTimeline',
467
+ descriptionKey: 'Settings.ShowWeatherOnTimelineDescription',
468
+ },
469
+
470
+ // ── Sound & alerts ──────────────────────────────────────────────────────
471
+ 'sound.reminderVolume': {
472
+ labelKey: 'Settings.ReminderVolume',
473
+ descriptionKey: 'Settings.ReminderVolumeDescription',
474
+ },
475
+ 'sound.timerVolume': {
476
+ labelKey: 'Settings.TimerVolume',
477
+ descriptionKey: 'Settings.TimerVolumeDescription',
478
+ },
479
+ 'sound.mediaVolume': {
480
+ labelKey: 'Settings.MediaVolume',
481
+ descriptionKey: 'Settings.MediaVolumeDescription',
482
+ },
483
+ 'sound.alarmSound': { labelKey: 'Settings.AlarmSound' },
484
+ 'sound.reminderAlarmSound': { labelKey: 'Settings.ReminderAlarmAsset' },
485
+ 'sound.reminderAlarmTimeout': { labelKey: 'Settings.ReminderAlarmTimeout' },
486
+ 'sound.timerAlarmSound': { labelKey: 'Settings.TimerAlarmSound' },
487
+ 'sound.timerAlarmTimeout': { labelKey: 'Settings.TimerAlarmTimeout' },
488
+ 'sound.allowCustomReminderSounds': {
489
+ labelKey: 'Settings.AllowCustomReminderSounds',
490
+ },
491
+ 'sound.ttsEnabled': {
492
+ labelKey: 'Settings.EnableSpeech',
493
+ descriptionKey: 'Settings.EnableSpeechDescription',
494
+ },
495
+ 'sound.ttsRate': { labelKey: 'Settings.TtsTitle' },
496
+
497
+ // ── Timer ───────────────────────────────────────────────────────────────
498
+ 'timer.showTimeRemaining': { labelKey: 'Settings.TimerShowTimeRemaining' },
499
+ 'timer.showEndTime': { labelKey: 'Settings.TimerShowEndTime' },
500
+ 'timer.showRestartButton': { labelKey: 'Settings.TimerShowRestartButton' },
501
+ 'timer.showPauseButton': { labelKey: 'Settings.TimerShowPauseButton' },
502
+
503
+ // ── Lock screen ─────────────────────────────────────────────────────────
504
+ 'lockScreen.pin': { labelKey: 'Settings.LockScreenTitlePin' },
505
+ 'lockScreen.clockDisplay': { labelKey: 'Settings.LockScreenClockTitle' },
506
+ 'lockScreen.showDate': { labelKey: 'Settings.LockScreenShowDate' },
507
+ 'lockScreen.showHourNumbers': {
508
+ labelKey: 'Settings.LockScreenShowHourNumbers',
509
+ },
510
+ 'lockScreen.imageMode': { labelKey: 'Settings.LockScreenImageModeTitle' },
511
+ 'lockScreen.backgroundImage': {
512
+ labelKey: 'Settings.LockScreenImageModeBackground',
513
+ },
514
+ 'lockScreen.photoFrameImages': {
515
+ labelKey: 'Settings.LockScreenImageModePhotoFrame',
516
+ },
517
+ 'lockScreen.photoFrameIntervalSeconds': {
518
+ labelKey: 'Settings.LockScreenPhotoFrameIntervalTitle',
519
+ },
520
+ 'lockScreen.inactivityLockEnabled': {
521
+ labelKey: 'SettingsInactivity.EnableLabel',
522
+ },
523
+ 'lockScreen.inactivityTimeoutMinutes': {
524
+ labelKey: 'SettingsInactivity.TimeoutLabel',
525
+ },
526
+
527
+ // ── Touch / gestures ───────────────────────────────────────────────────
528
+ 'touch.enableTapToCreate': {
529
+ labelKey: 'Settings.EnableTapToCreate',
530
+ descriptionKey: 'Settings.EnableTapToCreateDescription',
531
+ },
532
+ 'touch.enableDragDrop': {
533
+ labelKey: 'Settings.EnableDragDrop',
534
+ descriptionKey: 'Settings.EnableDragDropDescription',
535
+ },
536
+
537
+ // ── Device ──────────────────────────────────────────────────────────────
538
+ 'device.timePickerMode': { labelKey: 'Settings.DateTimeTitle' },
539
+
540
+ // ── Language ────────────────────────────────────────────────────────────
541
+ 'language.locale': { labelKey: 'Settings.SelectLanguage' },
542
+
543
+ // ── Notifications ───────────────────────────────────────────────────────
544
+ 'notification.enabled': { labelKey: 'Settings.NotificationsEnabled' },
545
+ 'notification.notifyAllCalendars': {
546
+ labelKey: 'Settings.NotifyAllCalendars',
547
+ },
548
+ 'notification.enabledCalendarIds': {
549
+ labelKey: 'Settings.NotificationsCalendars',
550
+ },
551
+
552
+ // ── Chronological header ────────────────────────────────────────────────
553
+ 'chronological.header.showNavigationArrows': {
554
+ labelKey: 'ChronologicalFeatures.NavigationArrows',
555
+ },
556
+ 'chronological.header.showClock': {
557
+ labelKey: 'ChronologicalFeatures.Clock',
558
+ },
559
+ 'chronological.header.showCurrentYearInDate': {
560
+ labelKey: 'ChronologicalFeatures.ShowCurrentYear',
561
+ },
562
+ 'chronological.header.showTimeOfDay': {
563
+ labelKey: 'ChronologicalFeatures.TimeOfDay',
564
+ },
565
+
566
+ // ── Chronological footer ────────────────────────────────────────────────
567
+ 'chronological.footer.showMenuButton': {
568
+ labelKey: 'ChronologicalFeatures.MenuButton',
569
+ },
570
+ 'chronological.footer.showViewSwitcherDay': {
571
+ labelKey: 'ChronologicalFeatures.ViewSwitcherDay',
572
+ },
573
+ 'chronological.footer.showViewSwitcherWeek': {
574
+ labelKey: 'ChronologicalFeatures.ViewSwitcherWeek',
575
+ },
576
+ 'chronological.footer.showViewSwitcherMonth': {
577
+ labelKey: 'ChronologicalFeatures.ViewSwitcherMonth',
578
+ },
579
+ 'chronological.footer.showTimerButton': {
580
+ labelKey: 'ChronologicalFeatures.TimerButton',
581
+ },
582
+ 'chronological.footer.showNewEventButton': {
583
+ labelKey: 'ChronologicalFeatures.NewEventButton',
584
+ },
585
+ 'chronological.footer.showSettingsButton': {
586
+ labelKey: 'ChronologicalFeatures.SettingsButton',
587
+ },
588
+
589
+ // ── Chronological timer ─────────────────────────────────────────────────
590
+ 'chronological.timer.showNewCountdown': {
591
+ labelKey: 'TimerFeatures.ShowNewCountdown',
592
+ },
593
+ 'chronological.timer.showFromTemplate': {
594
+ labelKey: 'TimerFeatures.ShowFromTemplate',
595
+ },
596
+ 'chronological.timer.showEditTemplate': {
597
+ labelKey: 'TimerFeatures.ShowEditTemplate',
598
+ },
599
+ 'chronological.timer.showDeleteTemplate': {
600
+ labelKey: 'TimerFeatures.ShowDeleteTemplate',
601
+ },
602
+ 'chronological.timer.showAddTemplate': {
603
+ labelKey: 'TimerFeatures.ShowAddTemplate',
604
+ },
605
+
606
+ // ── Chronological menu ──────────────────────────────────────────────────
607
+ 'chronological.menu.showSettingsButton': {
608
+ labelKey: 'ChronologicalFeatures.MenuSettingsButton',
609
+ },
610
+ 'chronological.quickSettings.showTimerVolume': {
611
+ labelKey: 'ChronologicalFeatures.QuickSettingsTimerVolume',
612
+ },
613
+ 'chronological.quickSettings.showReminderVolume': {
614
+ labelKey: 'ChronologicalFeatures.QuickSettingsReminderVolume',
615
+ },
616
+ 'chronological.quickSettings.showMediaVolume': {
617
+ labelKey: 'ChronologicalFeatures.QuickSettingsMediaVolume',
618
+ },
619
+ 'chronological.quickSettings.showBrightness': {
620
+ labelKey: 'ChronologicalFeatures.QuickSettingsBrightness',
621
+ },
622
+ 'chronological.quickSettings.showLockScreen': {
623
+ labelKey: 'ChronologicalFeatures.QuickSettingsLockScreen',
624
+ },
625
+
626
+ // ── Event form (time-based) ─────────────────────────────────────────────
627
+ 'eventForm.recurrence': { labelKey: 'Calendar.LabelRecurrence' },
628
+ 'eventForm.reminders': { labelKey: 'Calendar.LabelReminders' },
629
+ 'eventForm.emailReminders': { labelKey: 'Calendar.LabelEmailReminders' },
630
+ 'eventForm.location': { labelKey: 'Common.Location' },
631
+ 'eventForm.travelTime': { labelKey: 'TravelTime.Title' },
632
+ 'eventForm.description': { labelKey: 'Common.Description' },
633
+ 'eventForm.checklist': { labelKey: 'EventChecklist.DefaultName' },
634
+ 'eventForm.images': { labelKey: 'EventImageGallery.SectionTitle' },
635
+ 'eventForm.audioClips': { labelKey: 'EventAudioGallery.SectionTitle' },
636
+ 'eventForm.notificationReceivers': { labelKey: 'EventForm.NotificationReceivers' },
637
+ 'eventForm.visibility': { labelKey: 'EventVisibility.Title' },
638
+ };
639
+
349
640
  /**
350
641
  * Check whether a setting key matches a group's key matchers.
351
642
  */
@@ -367,10 +658,11 @@ function matchesGroup(key: string, group: SettingsGroupDef): boolean {
367
658
  export function groupSettingsForDevice(
368
659
  allSettings: ParsedSettingEntry[],
369
660
  calendarType: CalendarType,
661
+ appMode?: 'ENROLLED',
370
662
  ): { id: SettingsGroupId; labelKey: string; icon: string; settings: ParsedSettingEntry[] }[] {
371
- // Filter excluded
663
+ // Filter excluded and hidden settings
372
664
  const settings = allSettings.filter(
373
- (s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key),
665
+ (s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key) && s.uiType !== 'HIDDEN',
374
666
  );
375
667
 
376
668
  // Calendar type filtering for event form keys
@@ -399,6 +691,11 @@ export function groupSettingsForDevice(
399
691
  continue;
400
692
  }
401
693
 
694
+ // Skip groups not relevant for current app mode
695
+ if (group.appMode && group.appMode !== appMode) {
696
+ continue;
697
+ }
698
+
402
699
  const matched = filteredSettings.filter(
403
700
  (s) => !claimed.has(s.key) && matchesGroup(s.key, group),
404
701
  );
@@ -437,6 +734,12 @@ export type SettingDef<T = unknown> = {
437
734
  default: T;
438
735
  /** Whether this setting should be synced to the server. Default true. */
439
736
  sync: boolean;
737
+ /** UI component type for rendering this setting */
738
+ uiType: SettingUiType;
739
+ /** Available options for SELECT-type settings */
740
+ options?: readonly SettingOption<T>[];
741
+ /** Slider configuration for SLIDER-type settings */
742
+ sliderConfig?: SliderConfig;
440
743
  };
441
744
 
442
745
  // ---------------------------------------------------------------------------
@@ -459,6 +762,39 @@ export const DEFAULT_REGISTRY_CONFIG: RegistryConfig = {
459
762
  defaultLocale: 'nb',
460
763
  };
461
764
 
765
+ // ---------------------------------------------------------------------------
766
+ // Shared option arrays (reused across multiple settings)
767
+ // ---------------------------------------------------------------------------
768
+
769
+ const ALARM_SOUND_OPTIONS: readonly SettingOption<AlarmSound>[] = [
770
+ { value: 'none', labelKey: 'Settings.Option.AlarmSound.None' },
771
+ { value: 'alarm1', labelKey: 'Settings.Option.AlarmSound.Alarm1' },
772
+ { value: 'alarm2', labelKey: 'Settings.Option.AlarmSound.Alarm2' },
773
+ { value: 'alarm3', labelKey: 'Settings.Option.AlarmSound.Alarm3' },
774
+ { value: 'alarm4', labelKey: 'Settings.Option.AlarmSound.Alarm4' },
775
+ { value: 'alarm5', labelKey: 'Settings.Option.AlarmSound.Alarm5' },
776
+ { value: 'alarm6', labelKey: 'Settings.Option.AlarmSound.Alarm6' },
777
+ { value: 'alarm7', labelKey: 'Settings.Option.AlarmSound.Alarm7' },
778
+ { value: 'alarm8', labelKey: 'Settings.Option.AlarmSound.Alarm8' },
779
+ { value: 'alarm9', labelKey: 'Settings.Option.AlarmSound.Alarm9' },
780
+ ];
781
+
782
+ const ALARM_TIMEOUT_OPTIONS: readonly SettingOption<AlarmTimeout>[] = [
783
+ { value: 1, labelKey: 'Settings.Option.AlarmTimeout.1min' },
784
+ { value: 2, labelKey: 'Settings.Option.AlarmTimeout.2min' },
785
+ { value: 3, labelKey: 'Settings.Option.AlarmTimeout.3min' },
786
+ { value: 5, labelKey: 'Settings.Option.AlarmTimeout.5min' },
787
+ { value: 10, labelKey: 'Settings.Option.AlarmTimeout.10min' },
788
+ ];
789
+
790
+ const DAY_VIEW_ZOOM_OPTIONS: readonly SettingOption<CalendarDayViewCellZoom>[] = [
791
+ { value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
792
+ { value: 30, labelKey: 'Settings.Option.DayViewZoom.30min' },
793
+ { value: 60, labelKey: 'Settings.Option.DayViewZoom.60min' },
794
+ ];
795
+
796
+ const TIME_OF_DAY_SLIDER: SliderConfig = { min: 0, max: 23, step: 1 };
797
+
462
798
  // ---------------------------------------------------------------------------
463
799
  // Helper
464
800
  // ---------------------------------------------------------------------------
@@ -467,9 +803,27 @@ function def<T>(
467
803
  category: SettingsCategory,
468
804
  type: SettingsDataType,
469
805
  defaultValue: T,
470
- sync = true,
806
+ uiType: SettingUiType,
807
+ extra?: {
808
+ sync?: boolean;
809
+ options?: readonly SettingOption<T>[];
810
+ sliderConfig?: SliderConfig;
811
+ },
471
812
  ): SettingDef<T> {
472
- return { category, type, default: defaultValue, sync };
813
+ const result: SettingDef<T> = {
814
+ category,
815
+ type,
816
+ default: defaultValue,
817
+ sync: extra?.sync ?? true,
818
+ uiType,
819
+ };
820
+ if (extra?.options) {
821
+ result.options = extra.options;
822
+ }
823
+ if (extra?.sliderConfig) {
824
+ result.sliderConfig = extra.sliderConfig;
825
+ }
826
+ return result;
473
827
  }
474
828
 
475
829
  // ---------------------------------------------------------------------------
@@ -485,9 +839,33 @@ function buildEntries(config: RegistryConfig) {
485
839
  'appearance',
486
840
  'string',
487
841
  config.defaultTheme,
842
+ 'CUSTOM_THEME_PICKER',
843
+ {
844
+ options: [
845
+ { value: 'light', labelKey: 'Settings.Option.Theme.Light' },
846
+ { value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
847
+ { value: 'system', labelKey: 'Settings.Option.Theme.System' },
848
+ ],
849
+ },
850
+ ),
851
+ 'appearance.clockType': def<ClockType>(
852
+ 'appearance',
853
+ 'string',
854
+ 'digital',
855
+ 'CUSTOM_CLOCK_TYPE',
856
+ {
857
+ options: [
858
+ { value: 'digital', labelKey: 'Settings.Option.ClockType.Digital' },
859
+ { value: 'analog', labelKey: 'Settings.Option.ClockType.Analog' },
860
+ ],
861
+ },
862
+ ),
863
+ 'appearance.enableDayColors': def<boolean>(
864
+ 'appearance',
865
+ 'boolean',
866
+ false,
867
+ 'TOGGLE',
488
868
  ),
489
- 'appearance.clockType': def<ClockType>('appearance', 'string', 'digital'),
490
- 'appearance.enableDayColors': def<boolean>('appearance', 'boolean', false),
491
869
 
492
870
  // ═══════════════════════════════════════════════════════════════════════
493
871
  // Calendar view
@@ -496,152 +874,346 @@ function buildEntries(config: RegistryConfig) {
496
874
  'calendarView',
497
875
  'string',
498
876
  'time-based',
877
+ 'CUSTOM_CALENDAR_TYPE',
878
+ {
879
+ options: [
880
+ {
881
+ value: 'chronological',
882
+ labelKey: 'Settings.Option.CalendarType.Chronological',
883
+ },
884
+ {
885
+ value: 'time-based',
886
+ labelKey: 'Settings.Option.CalendarType.TimeBased',
887
+ },
888
+ ],
889
+ },
890
+ ),
891
+ 'calendarView.view': def<CalendarViewMode>(
892
+ 'calendarView',
893
+ 'string',
894
+ 'day',
895
+ 'SELECT',
896
+ {
897
+ options: [
898
+ { value: 'day', labelKey: 'Settings.Option.CalendarView.Day' },
899
+ {
900
+ value: '3-days',
901
+ labelKey: 'Settings.Option.CalendarView.3Days',
902
+ },
903
+ {
904
+ value: '5-days',
905
+ labelKey: 'Settings.Option.CalendarView.5Days',
906
+ },
907
+ {
908
+ value: '7-days',
909
+ labelKey: 'Settings.Option.CalendarView.7Days',
910
+ },
911
+ { value: 'week', labelKey: 'Settings.Option.CalendarView.Week' },
912
+ {
913
+ value: 'month',
914
+ labelKey: 'Settings.Option.CalendarView.Month',
915
+ },
916
+ {
917
+ value: 'overview',
918
+ labelKey: 'Settings.Option.CalendarView.Overview',
919
+ },
920
+ ],
921
+ },
499
922
  ),
500
- 'calendarView.view': def<CalendarViewMode>('calendarView', 'string', 'day'),
501
923
  'calendarView.dayViewZoom': def<CalendarDayViewCellZoom>(
502
924
  'calendarView',
503
925
  'number',
504
926
  60,
927
+ 'SELECT',
928
+ { options: DAY_VIEW_ZOOM_OPTIONS },
505
929
  ),
506
930
  'calendarView.weekViewZoom': def<CalendarDayViewCellZoom>(
507
931
  'calendarView',
508
932
  'number',
509
933
  60,
934
+ 'SELECT',
935
+ { options: DAY_VIEW_ZOOM_OPTIONS },
936
+ ),
937
+ 'calendarView.splitView': def<boolean>(
938
+ 'calendarView',
939
+ 'boolean',
940
+ false,
941
+ 'TOGGLE',
510
942
  ),
511
- 'calendarView.splitView': def<boolean>('calendarView', 'boolean', false),
512
943
  'calendarView.showCalendarNames': def<boolean>(
513
944
  'calendarView',
514
945
  'boolean',
515
946
  true,
947
+ 'TOGGLE',
948
+ ),
949
+ 'calendarView.calendarColumns': def<unknown[]>(
950
+ 'calendarView',
951
+ 'json',
952
+ [],
953
+ 'CUSTOM_SPLIT_VIEW_CONFIG',
516
954
  ),
517
- 'calendarView.calendarColumns': def<unknown[]>('calendarView', 'json', []),
518
955
  'calendarView.autoReturnToTodayEnabled': def<boolean>(
519
956
  'calendarView',
520
957
  'boolean',
521
958
  config.isEnrolled,
959
+ 'TOGGLE',
522
960
  ),
523
961
  'calendarView.autoReturnToTodayTimeoutSeconds': def<number>(
524
962
  'calendarView',
525
963
  'number',
526
964
  300,
965
+ 'SLIDER',
966
+ { sliderConfig: { min: 30, max: 600, step: 30 } },
527
967
  ),
528
968
  'calendarView.showWeatherOnEvents': def<boolean>(
529
969
  'calendarView',
530
970
  'boolean',
531
971
  false,
972
+ 'TOGGLE',
532
973
  ),
533
974
  'calendarView.showWeatherOnTimeline': def<boolean>(
534
975
  'calendarView',
535
976
  'boolean',
536
977
  false,
978
+ 'TOGGLE',
537
979
  ),
538
980
  'calendarView.weatherLocation': def<WeatherLocation | null>(
539
981
  'calendarView',
540
982
  'json',
541
983
  null,
984
+ 'CUSTOM_WEATHER_LOCATION',
542
985
  ),
543
986
 
544
987
  // ═══════════════════════════════════════════════════════════════════════
545
988
  // Event form field visibility (time-based)
546
989
  // ═══════════════════════════════════════════════════════════════════════
547
- 'eventForm.recurrence': def<boolean>('eventForm', 'boolean', true),
548
- 'eventForm.reminders': def<boolean>('eventForm', 'boolean', true),
549
- 'eventForm.emailReminders': def<boolean>('eventForm', 'boolean', false),
550
- 'eventForm.location': def<boolean>('eventForm', 'boolean', true),
551
- 'eventForm.travelTime': def<boolean>('eventForm', 'boolean', false),
552
- 'eventForm.description': def<boolean>('eventForm', 'boolean', true),
553
- 'eventForm.checklist': def<boolean>('eventForm', 'boolean', true),
554
- 'eventForm.images': def<boolean>('eventForm', 'boolean', false),
555
- 'eventForm.audioClips': def<boolean>('eventForm', 'boolean', false),
990
+ 'eventForm.recurrence': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
991
+ 'eventForm.reminders': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
992
+ 'eventForm.emailReminders': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
993
+ 'eventForm.location': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
994
+ 'eventForm.travelTime': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
995
+ 'eventForm.description': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
996
+ 'eventForm.checklist': def<boolean>('eventForm', 'boolean', true, 'TOGGLE'),
997
+ 'eventForm.images': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
998
+ 'eventForm.audioClips': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
556
999
  'eventForm.notificationReceivers': def<boolean>(
557
1000
  'eventForm',
558
1001
  'boolean',
559
1002
  true,
1003
+ 'TOGGLE',
560
1004
  ),
561
- 'eventForm.visibility': def<boolean>('eventForm', 'boolean', false),
1005
+ 'eventForm.visibility': def<boolean>('eventForm', 'boolean', false, 'TOGGLE'),
562
1006
 
563
1007
  // ═══════════════════════════════════════════════════════════════════════
564
1008
  // Sound & alerts
565
1009
  // ═══════════════════════════════════════════════════════════════════════
566
- 'sound.timerVolume': def<number>('sound', 'number', 0.5),
567
- 'sound.reminderVolume': def<number>('sound', 'number', 0.5),
568
- 'sound.mediaVolume': def<number>('sound', 'number', 0.5),
569
- 'sound.alarmSound': def<AlarmSound>('sound', 'string', 'alarm1'),
570
- 'sound.reminderAlarmSound': def<AlarmSound>('sound', 'string', 'alarm1'),
571
- 'sound.timerAlarmSound': def<AlarmSound>('sound', 'string', 'alarm1'),
572
- 'sound.timerAlarmTimeout': def<AlarmTimeout>('sound', 'number', 3),
573
- 'sound.reminderAlarmTimeout': def<AlarmTimeout>('sound', 'number', 3),
574
- 'sound.allowCustomReminderSounds': def<boolean>('sound', 'boolean', false),
575
- 'sound.ttsEnabled': def<boolean>('sound', 'boolean', config.isEnrolled),
576
- 'sound.ttsRate': def<number>('sound', 'number', 1.0),
1010
+ 'sound.timerVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
1011
+ 'sound.reminderVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
1012
+ 'sound.mediaVolume': def<number>('sound', 'number', 0.5, 'VOLUME_SLIDER'),
1013
+ 'sound.alarmSound': def<AlarmSound>(
1014
+ 'sound',
1015
+ 'string',
1016
+ 'alarm1',
1017
+ 'SELECT',
1018
+ { options: ALARM_SOUND_OPTIONS },
1019
+ ),
1020
+ 'sound.reminderAlarmSound': def<AlarmSound>(
1021
+ 'sound',
1022
+ 'string',
1023
+ 'alarm1',
1024
+ 'SELECT',
1025
+ { options: ALARM_SOUND_OPTIONS },
1026
+ ),
1027
+ 'sound.timerAlarmSound': def<AlarmSound>(
1028
+ 'sound',
1029
+ 'string',
1030
+ 'alarm1',
1031
+ 'SELECT',
1032
+ { options: ALARM_SOUND_OPTIONS },
1033
+ ),
1034
+ 'sound.timerAlarmTimeout': def<AlarmTimeout>(
1035
+ 'sound',
1036
+ 'number',
1037
+ 3,
1038
+ 'SELECT',
1039
+ { options: ALARM_TIMEOUT_OPTIONS },
1040
+ ),
1041
+ 'sound.reminderAlarmTimeout': def<AlarmTimeout>(
1042
+ 'sound',
1043
+ 'number',
1044
+ 3,
1045
+ 'SELECT',
1046
+ { options: ALARM_TIMEOUT_OPTIONS },
1047
+ ),
1048
+ 'sound.allowCustomReminderSounds': def<boolean>(
1049
+ 'sound',
1050
+ 'boolean',
1051
+ false,
1052
+ 'TOGGLE',
1053
+ ),
1054
+ 'sound.ttsEnabled': def<boolean>(
1055
+ 'sound',
1056
+ 'boolean',
1057
+ config.isEnrolled,
1058
+ 'TOGGLE',
1059
+ ),
1060
+ 'sound.ttsRate': def<number>(
1061
+ 'sound',
1062
+ 'number',
1063
+ 1.0,
1064
+ 'SLIDER',
1065
+ { sliderConfig: { min: 0.5, max: 2, step: 0.1 } },
1066
+ ),
577
1067
 
578
1068
  // ═══════════════════════════════════════════════════════════════════════
579
1069
  // Timer
580
1070
  // ═══════════════════════════════════════════════════════════════════════
581
- 'timer.showTimeRemaining': def<boolean>('timer', 'boolean', true),
582
- 'timer.showEndTime': def<boolean>('timer', 'boolean', true),
583
- 'timer.showRestartButton': def<boolean>('timer', 'boolean', true),
584
- 'timer.showPauseButton': def<boolean>('timer', 'boolean', true),
1071
+ 'timer.showTimeRemaining': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
1072
+ 'timer.showEndTime': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
1073
+ 'timer.showRestartButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
1074
+ 'timer.showPauseButton': def<boolean>('timer', 'boolean', true, 'TOGGLE'),
585
1075
 
586
1076
  // ═══════════════════════════════════════════════════════════════════════
587
1077
  // Lock screen
588
1078
  // ═══════════════════════════════════════════════════════════════════════
589
- 'lockScreen.pin': def<string>('lockScreen', 'string', ''),
1079
+ 'lockScreen.pin': def<string>('lockScreen', 'string', '', 'PIN_INPUT'),
590
1080
  'lockScreen.inactivityLockEnabled': def<boolean>(
591
1081
  'lockScreen',
592
1082
  'boolean',
593
1083
  false,
1084
+ 'TOGGLE',
594
1085
  ),
595
1086
  'lockScreen.inactivityTimeoutMinutes': def<InactivityTimeoutMinutes>(
596
1087
  'lockScreen',
597
1088
  'number',
598
1089
  5,
1090
+ 'SELECT',
1091
+ {
1092
+ options: [
1093
+ { value: 1, labelKey: 'Settings.Option.InactivityTimeout.1min' },
1094
+ { value: 5, labelKey: 'Settings.Option.InactivityTimeout.5min' },
1095
+ { value: 10, labelKey: 'Settings.Option.InactivityTimeout.10min' },
1096
+ { value: 15, labelKey: 'Settings.Option.InactivityTimeout.15min' },
1097
+ { value: 30, labelKey: 'Settings.Option.InactivityTimeout.30min' },
1098
+ { value: 45, labelKey: 'Settings.Option.InactivityTimeout.45min' },
1099
+ ],
1100
+ },
599
1101
  ),
600
1102
  'lockScreen.clockDisplay': def<LockScreenClockDisplay>(
601
1103
  'lockScreen',
602
1104
  'string',
603
1105
  'digital',
1106
+ 'SELECT',
1107
+ {
1108
+ options: [
1109
+ { value: 'none', labelKey: 'Settings.Option.ClockDisplay.None' },
1110
+ {
1111
+ value: 'digital',
1112
+ labelKey: 'Settings.Option.ClockDisplay.Digital',
1113
+ },
1114
+ {
1115
+ value: 'analog',
1116
+ labelKey: 'Settings.Option.ClockDisplay.Analog',
1117
+ },
1118
+ ],
1119
+ },
1120
+ ),
1121
+ 'lockScreen.showDate': def<boolean>(
1122
+ 'lockScreen',
1123
+ 'boolean',
1124
+ true,
1125
+ 'TOGGLE',
1126
+ ),
1127
+ 'lockScreen.showHourNumbers': def<boolean>(
1128
+ 'lockScreen',
1129
+ 'boolean',
1130
+ true,
1131
+ 'TOGGLE',
604
1132
  ),
605
- 'lockScreen.showDate': def<boolean>('lockScreen', 'boolean', true),
606
- 'lockScreen.showHourNumbers': def<boolean>('lockScreen', 'boolean', true),
607
1133
  'lockScreen.imageMode': def<LockScreenImageMode>(
608
1134
  'lockScreen',
609
1135
  'string',
610
1136
  'none',
1137
+ 'SELECT',
1138
+ {
1139
+ options: [
1140
+ { value: 'none', labelKey: 'Settings.Option.ImageMode.None' },
1141
+ {
1142
+ value: 'background',
1143
+ labelKey: 'Settings.Option.ImageMode.Background',
1144
+ },
1145
+ {
1146
+ value: 'photoFrame',
1147
+ labelKey: 'Settings.Option.ImageMode.PhotoFrame',
1148
+ },
1149
+ ],
1150
+ },
611
1151
  ),
612
1152
  'lockScreen.backgroundImage': def<string | null>(
613
1153
  'lockScreen',
614
1154
  'json',
615
1155
  null,
1156
+ 'CUSTOM_IMAGE',
616
1157
  ),
617
1158
  'lockScreen.photoFrameImages': def<LockScreenImage[]>(
618
1159
  'lockScreen',
619
1160
  'json',
620
1161
  [],
1162
+ 'CUSTOM_IMAGE_ARRAY',
621
1163
  ),
622
1164
  'lockScreen.photoFrameIntervalSeconds': def<PhotoFrameIntervalSeconds>(
623
1165
  'lockScreen',
624
1166
  'number',
625
1167
  60,
1168
+ 'SELECT',
1169
+ {
1170
+ options: [
1171
+ { value: 30, labelKey: 'Settings.Option.PhotoFrameInterval.30sec' },
1172
+ { value: 60, labelKey: 'Settings.Option.PhotoFrameInterval.1min' },
1173
+ { value: 120, labelKey: 'Settings.Option.PhotoFrameInterval.2min' },
1174
+ { value: 300, labelKey: 'Settings.Option.PhotoFrameInterval.5min' },
1175
+ ],
1176
+ },
626
1177
  ),
627
1178
 
628
1179
  // ═══════════════════════════════════════════════════════════════════════
629
1180
  // Touch / gestures
630
1181
  // ═══════════════════════════════════════════════════════════════════════
631
- 'touch.enableTapToCreate': def<boolean>('touch', 'boolean', false),
632
- 'touch.enableDragDrop': def<boolean>('touch', 'boolean', false),
1182
+ 'touch.enableTapToCreate': def<boolean>('touch', 'boolean', false, 'TOGGLE'),
1183
+ 'touch.enableDragDrop': def<boolean>('touch', 'boolean', false, 'TOGGLE'),
633
1184
 
634
1185
  // ═══════════════════════════════════════════════════════════════════════
635
1186
  // Device (not synced unless noted)
636
1187
  // ═══════════════════════════════════════════════════════════════════════
637
- 'device.id': def<string>('device', 'string', '', false),
638
- 'device.timePickerMode': def<TimePickerMode>('device', 'string', 'dials'),
639
- 'device.devMenuEnabled': def<boolean>('device', 'boolean', false, false),
1188
+ 'device.id': def<string>('device', 'string', '', 'HIDDEN', { sync: false }),
1189
+ 'device.timePickerMode': def<TimePickerMode>(
1190
+ 'device',
1191
+ 'string',
1192
+ 'dials',
1193
+ 'SELECT',
1194
+ {
1195
+ options: [
1196
+ { value: 'dials', labelKey: 'Settings.Option.TimePickerMode.Dials' },
1197
+ {
1198
+ value: 'keypad',
1199
+ labelKey: 'Settings.Option.TimePickerMode.Keypad',
1200
+ },
1201
+ ],
1202
+ },
1203
+ ),
1204
+ 'device.devMenuEnabled': def<boolean>(
1205
+ 'device',
1206
+ 'boolean',
1207
+ false,
1208
+ 'HIDDEN',
1209
+ { sync: false },
1210
+ ),
640
1211
  'device.authWarningDismissTtlDays': def<number>(
641
1212
  'device',
642
1213
  'number',
643
1214
  3,
644
- false,
1215
+ 'HIDDEN',
1216
+ { sync: false },
645
1217
  ),
646
1218
 
647
1219
  // ═══════════════════════════════════════════════════════════════════════
@@ -651,27 +1223,42 @@ function buildEntries(config: RegistryConfig) {
651
1223
  'language',
652
1224
  'string',
653
1225
  config.defaultLocale,
1226
+ 'CUSTOM_LANGUAGE_PICKER',
1227
+ {
1228
+ options: [
1229
+ { value: 'nb', labelKey: 'Settings.Option.Language.Norwegian' },
1230
+ { value: 'en', labelKey: 'Settings.Option.Language.English' },
1231
+ ],
1232
+ },
654
1233
  ),
655
1234
 
656
1235
  // ═══════════════════════════════════════════════════════════════════════
657
1236
  // Notifications
658
1237
  // ═══════════════════════════════════════════════════════════════════════
659
- 'notification.enabled': def<boolean>('notification', 'boolean', false),
1238
+ 'notification.enabled': def<boolean>(
1239
+ 'notification',
1240
+ 'boolean',
1241
+ false,
1242
+ 'TOGGLE',
1243
+ ),
660
1244
  'notification.notifyAllCalendars': def<boolean>(
661
1245
  'notification',
662
1246
  'boolean',
663
1247
  true,
1248
+ 'TOGGLE',
664
1249
  ),
665
1250
  'notification.enabledCalendarIds': def<string[]>(
666
1251
  'notification',
667
1252
  'json',
668
1253
  [],
1254
+ 'CUSTOM_CALENDAR_IDS',
669
1255
  ),
670
1256
  'notification.hasBeenPrompted': def<boolean>(
671
1257
  'notification',
672
1258
  'boolean',
673
1259
  false,
674
- false,
1260
+ 'HIDDEN',
1261
+ { sync: false },
675
1262
  ),
676
1263
 
677
1264
  // ═══════════════════════════════════════════════════════════════════════
@@ -682,141 +1269,173 @@ function buildEntries(config: RegistryConfig) {
682
1269
  'chronological',
683
1270
  'boolean',
684
1271
  true,
1272
+ 'TOGGLE',
685
1273
  ),
686
1274
  'chronological.header.showClock': def<boolean>(
687
1275
  'chronological',
688
1276
  'boolean',
689
1277
  true,
1278
+ 'TOGGLE',
690
1279
  ),
691
1280
  'chronological.header.showCurrentYearInDate': def<boolean>(
692
1281
  'chronological',
693
1282
  'boolean',
694
1283
  false,
1284
+ 'TOGGLE',
695
1285
  ),
696
1286
  'chronological.header.showTimeOfDay': def<boolean>(
697
1287
  'chronological',
698
1288
  'boolean',
699
1289
  false,
1290
+ 'TOGGLE',
700
1291
  ),
701
1292
  // Footer
702
1293
  'chronological.footer.showMenuButton': def<boolean>(
703
1294
  'chronological',
704
1295
  'boolean',
705
1296
  true,
1297
+ 'TOGGLE',
706
1298
  ),
707
1299
  'chronological.footer.showViewSwitcherDay': def<boolean>(
708
1300
  'chronological',
709
1301
  'boolean',
710
1302
  true,
1303
+ 'TOGGLE',
711
1304
  ),
712
1305
  'chronological.footer.showViewSwitcherWeek': def<boolean>(
713
1306
  'chronological',
714
1307
  'boolean',
715
1308
  true,
1309
+ 'TOGGLE',
716
1310
  ),
717
1311
  'chronological.footer.showViewSwitcherMonth': def<boolean>(
718
1312
  'chronological',
719
1313
  'boolean',
720
1314
  true,
1315
+ 'TOGGLE',
721
1316
  ),
722
1317
  'chronological.footer.showTimerButton': def<boolean>(
723
1318
  'chronological',
724
1319
  'boolean',
725
1320
  true,
1321
+ 'TOGGLE',
726
1322
  ),
727
1323
  'chronological.footer.showNewEventButton': def<boolean>(
728
1324
  'chronological',
729
1325
  'boolean',
730
1326
  true,
1327
+ 'TOGGLE',
731
1328
  ),
732
1329
  'chronological.footer.showSettingsButton': def<boolean>(
733
1330
  'chronological',
734
1331
  'boolean',
735
1332
  true,
1333
+ 'TOGGLE',
736
1334
  ),
737
1335
  // Timer features
738
1336
  'chronological.timer.showNewCountdown': def<boolean>(
739
1337
  'chronological',
740
1338
  'boolean',
741
1339
  true,
1340
+ 'TOGGLE',
742
1341
  ),
743
1342
  'chronological.timer.showFromTemplate': def<boolean>(
744
1343
  'chronological',
745
1344
  'boolean',
746
1345
  true,
1346
+ 'TOGGLE',
747
1347
  ),
748
1348
  'chronological.timer.showEditTemplate': def<boolean>(
749
1349
  'chronological',
750
1350
  'boolean',
751
1351
  true,
1352
+ 'TOGGLE',
752
1353
  ),
753
1354
  'chronological.timer.showDeleteTemplate': def<boolean>(
754
1355
  'chronological',
755
1356
  'boolean',
756
1357
  true,
1358
+ 'TOGGLE',
757
1359
  ),
758
1360
  'chronological.timer.showAddTemplate': def<boolean>(
759
1361
  'chronological',
760
1362
  'boolean',
761
1363
  true,
1364
+ 'TOGGLE',
762
1365
  ),
763
1366
  // Menu
764
1367
  'chronological.menu.showSettingsButton': def<boolean>(
765
1368
  'chronological',
766
1369
  'boolean',
767
1370
  true,
1371
+ 'TOGGLE',
768
1372
  ),
769
1373
  // Quick settings
770
1374
  'chronological.quickSettings.showTimerVolume': def<boolean>(
771
1375
  'chronological',
772
1376
  'boolean',
773
1377
  true,
1378
+ 'TOGGLE',
774
1379
  ),
775
1380
  'chronological.quickSettings.showReminderVolume': def<boolean>(
776
1381
  'chronological',
777
1382
  'boolean',
778
1383
  true,
1384
+ 'TOGGLE',
779
1385
  ),
780
1386
  'chronological.quickSettings.showMediaVolume': def<boolean>(
781
1387
  'chronological',
782
1388
  'boolean',
783
1389
  true,
1390
+ 'TOGGLE',
784
1391
  ),
785
1392
  'chronological.quickSettings.showBrightness': def<boolean>(
786
1393
  'chronological',
787
1394
  'boolean',
788
1395
  true,
1396
+ 'TOGGLE',
789
1397
  ),
790
1398
  'chronological.quickSettings.showLockScreen': def<boolean>(
791
1399
  'chronological',
792
1400
  'boolean',
793
1401
  true,
1402
+ 'TOGGLE',
794
1403
  ),
795
1404
  // Time-of-day periods
796
1405
  'chronological.timeOfDay.morningStart': def<number>(
797
1406
  'chronological',
798
1407
  'number',
799
1408
  6,
1409
+ 'SLIDER',
1410
+ { sliderConfig: TIME_OF_DAY_SLIDER },
800
1411
  ),
801
1412
  'chronological.timeOfDay.forenoonStart': def<number>(
802
1413
  'chronological',
803
1414
  'number',
804
1415
  9,
1416
+ 'SLIDER',
1417
+ { sliderConfig: TIME_OF_DAY_SLIDER },
805
1418
  ),
806
1419
  'chronological.timeOfDay.afternoonStart': def<number>(
807
1420
  'chronological',
808
1421
  'number',
809
1422
  12,
1423
+ 'SLIDER',
1424
+ { sliderConfig: TIME_OF_DAY_SLIDER },
810
1425
  ),
811
1426
  'chronological.timeOfDay.eveningStart': def<number>(
812
1427
  'chronological',
813
1428
  'number',
814
1429
  18,
1430
+ 'SLIDER',
1431
+ { sliderConfig: TIME_OF_DAY_SLIDER },
815
1432
  ),
816
1433
  'chronological.timeOfDay.nightStart': def<number>(
817
1434
  'chronological',
818
1435
  'number',
819
1436
  0,
1437
+ 'SLIDER',
1438
+ { sliderConfig: TIME_OF_DAY_SLIDER },
820
1439
  ),
821
1440
 
822
1441
  // ═══════════════════════════════════════════════════════════════════════
@@ -827,80 +1446,115 @@ function buildEntries(config: RegistryConfig) {
827
1446
  'chronologicalEventForm',
828
1447
  'boolean',
829
1448
  true,
1449
+ 'TOGGLE',
830
1450
  ),
831
1451
  'chronologicalEventForm.field.recurrence': def<boolean>(
832
1452
  'chronologicalEventForm',
833
1453
  'boolean',
834
1454
  true,
1455
+ 'TOGGLE',
835
1456
  ),
836
1457
  'chronologicalEventForm.field.acknowledge': def<boolean>(
837
1458
  'chronologicalEventForm',
838
1459
  'boolean',
839
1460
  true,
1461
+ 'TOGGLE',
840
1462
  ),
841
1463
  'chronologicalEventForm.field.checklist': def<boolean>(
842
1464
  'chronologicalEventForm',
843
1465
  'boolean',
844
1466
  true,
1467
+ 'TOGGLE',
845
1468
  ),
846
1469
  'chronologicalEventForm.field.extraImages': def<boolean>(
847
1470
  'chronologicalEventForm',
848
1471
  'boolean',
849
1472
  true,
1473
+ 'TOGGLE',
850
1474
  ),
851
1475
  'chronologicalEventForm.field.reminders': def<boolean>(
852
1476
  'chronologicalEventForm',
853
1477
  'boolean',
854
1478
  true,
1479
+ 'TOGGLE',
855
1480
  ),
856
1481
  'chronologicalEventForm.field.audioClips': def<boolean>(
857
1482
  'chronologicalEventForm',
858
1483
  'boolean',
859
1484
  true,
1485
+ 'TOGGLE',
860
1486
  ),
861
1487
  // Fixed fields
862
1488
  'chronologicalEventForm.fixedField.allDay': def<boolean>(
863
1489
  'chronologicalEventForm',
864
1490
  'boolean',
865
1491
  true,
1492
+ 'TOGGLE',
866
1493
  ),
867
1494
  'chronologicalEventForm.fixedField.endTime': def<boolean>(
868
1495
  'chronologicalEventForm',
869
1496
  'boolean',
870
1497
  true,
1498
+ 'TOGGLE',
871
1499
  ),
872
1500
  'chronologicalEventForm.fixedField.visibility': def<boolean>(
873
1501
  'chronologicalEventForm',
874
1502
  'boolean',
875
1503
  true,
1504
+ 'TOGGLE',
876
1505
  ),
877
1506
  // Suggest end time
878
1507
  'chronologicalEventForm.suggestEndTime.enabled': def<boolean>(
879
1508
  'chronologicalEventForm',
880
1509
  'boolean',
881
1510
  false,
1511
+ 'TOGGLE',
882
1512
  ),
883
1513
  'chronologicalEventForm.suggestEndTime.value': def<number>(
884
1514
  'chronologicalEventForm',
885
1515
  'number',
886
1516
  30,
1517
+ 'SLIDER',
1518
+ { sliderConfig: { min: 5, max: 480, step: 5 } },
887
1519
  ),
888
1520
  'chronologicalEventForm.suggestEndTime.unit': def<SuggestEndTimeUnit>(
889
1521
  'chronologicalEventForm',
890
1522
  'string',
891
1523
  'minutes',
1524
+ 'SELECT',
1525
+ {
1526
+ options: [
1527
+ {
1528
+ value: 'minutes',
1529
+ labelKey: 'Settings.Option.SuggestEndTimeUnit.Minutes',
1530
+ },
1531
+ {
1532
+ value: 'hours',
1533
+ labelKey: 'Settings.Option.SuggestEndTimeUnit.Hours',
1534
+ },
1535
+ ],
1536
+ },
892
1537
  ),
893
1538
  // Default visibility
894
1539
  'chronologicalEventForm.defaultVisibility': def<EventVisibility>(
895
1540
  'chronologicalEventForm',
896
1541
  'string',
897
1542
  'Private',
1543
+ 'SELECT',
1544
+ {
1545
+ options: [
1546
+ { value: 'Public', labelKey: 'Settings.Option.Visibility.Public' },
1547
+ { value: 'Private', labelKey: 'Settings.Option.Visibility.Private' },
1548
+ { value: 'Custom', labelKey: 'Settings.Option.Visibility.Custom' },
1549
+ ],
1550
+ },
898
1551
  ),
899
1552
  // Reminder presets
900
1553
  'chronologicalEventForm.reminderPresets.timed': def<number[]>(
901
1554
  'chronologicalEventForm',
902
1555
  'json',
903
1556
  [5, 15, 30, 60, 120, 1440],
1557
+ 'CUSTOM_REMINDER_PRESETS',
904
1558
  ),
905
1559
  'chronologicalEventForm.reminderPresets.allDay': def<AllDayPreset[]>(
906
1560
  'chronologicalEventForm',
@@ -910,6 +1564,7 @@ function buildEntries(config: RegistryConfig) {
910
1564
  { daysBefore: 1, time: '18:00' },
911
1565
  { daysBefore: 2, time: '09:00' },
912
1566
  ],
1567
+ 'CUSTOM_REMINDER_PRESETS',
913
1568
  ),
914
1569
  } as const satisfies Record<string, SettingDef>;
915
1570
  }
@@ -1064,8 +1719,18 @@ export type ParsedSettingEntry = {
1064
1719
  name: string;
1065
1720
  /** Human-readable label (e.g. 'Inactivity Timeout Minutes') */
1066
1721
  label: string;
1722
+ /** i18n message key for the label (from SETTINGS_LABELS) */
1723
+ labelKey?: string;
1724
+ /** i18n message key for a description (from SETTINGS_LABELS) */
1725
+ descriptionKey?: string;
1067
1726
  /** The setting value */
1068
1727
  value: unknown;
1728
+ /** UI component type for rendering this setting */
1729
+ uiType: SettingUiType;
1730
+ /** Available options for SELECT-type settings */
1731
+ options?: readonly SettingOption[];
1732
+ /** Slider configuration for SLIDER-type settings */
1733
+ sliderConfig?: SliderConfig;
1069
1734
  };
1070
1735
 
1071
1736
  export type ParsedSettingsGroup = {
@@ -1122,11 +1787,21 @@ export function parseSettingsSnapshot(
1122
1787
  if (!groups.has(category)) {
1123
1788
  groups.set(category, []);
1124
1789
  }
1790
+
1791
+ const labelDef = SETTINGS_LABELS[key];
1792
+ const entryDef = key in registry.entries
1793
+ ? registry.entries[key as SettingKey]
1794
+ : undefined;
1125
1795
  groups.get(category)!.push({
1126
1796
  key,
1127
1797
  name,
1128
1798
  label: registry.getSettingLabel(key),
1799
+ labelKey: labelDef?.labelKey,
1800
+ descriptionKey: labelDef?.descriptionKey,
1129
1801
  value,
1802
+ uiType: entryDef?.uiType ?? 'TEXT_INPUT',
1803
+ ...(entryDef?.options && { options: entryDef.options as readonly SettingOption[] }),
1804
+ ...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
1130
1805
  });
1131
1806
  }
1132
1807