@focus8/settings-registry 0.8.2 → 0.8.4

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/dist/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * App-specific defaults (theme, locale, enrolled mode) are parameterized
13
13
  * via `createRegistry(config)`.
14
14
  */
15
- export const ALARM_TIMEOUTS = [1, 2, 3, 5, 10];
15
+ export const ALARM_TIMEOUTS = [5, 30, 60, 120, 180, 300, 600];
16
16
  export const LOCK_SCREEN_MAX_IMAGES = 10;
17
17
  // ---------------------------------------------------------------------------
18
18
  // Setting categories — used for UI grouping and profile organization
@@ -96,100 +96,114 @@ export function getCategoriesForCalendarType(calendarType) {
96
96
  });
97
97
  }
98
98
  // ---------------------------------------------------------------------------
99
- // Settings groupsuser-facing groups that reorganize raw categories
99
+ // Settings sectionstop-level groupings (e.g. "Kalender", "Enhet")
100
100
  // ---------------------------------------------------------------------------
101
- /**
102
- * Settings group IDs matching the mobile app's settings menu structure.
103
- * These provide user-friendly groupings that may pull settings from multiple
104
- * raw registry categories.
105
- */
106
- export const SETTINGS_GROUP_IDS = [
107
- 'appearance',
108
- 'calendarType',
101
+ export const SETTINGS_SECTION_IDS = ['calendar', 'unit'];
102
+ export const SETTINGS_SECTIONS = [
103
+ { id: 'calendar', labelKey: 'Calendar.LabelCalendar' },
104
+ { id: 'unit', labelKey: 'Settings.Unit' },
105
+ ];
106
+ // ---------------------------------------------------------------------------
107
+ // Settings menu items — ordered list of all items in the settings menu
108
+ // ---------------------------------------------------------------------------
109
+ export const SETTINGS_MENU_ITEM_IDS = [
110
+ // Calendar section
109
111
  'calendarView',
110
- 'activities',
111
- 'dateWeather',
112
- 'soundAlerts',
112
+ 'calendars',
113
+ 'eventCategories',
114
+ 'notification',
113
115
  'timer',
116
+ 'guests',
117
+ 'eventForm',
118
+ 'chronologicalEventForm',
119
+ 'weather',
120
+ 'gallery',
121
+ 'audioClips',
122
+ 'gestures',
123
+ 'calendarType',
124
+ 'sharing',
125
+ // Unit section
126
+ 'screen',
127
+ 'volume',
114
128
  'lockScreen',
115
- 'touch',
116
- 'chronological',
117
- 'device',
118
129
  'language',
130
+ 'wifi',
131
+ 'devices',
132
+ 'profiles',
133
+ 'tts',
134
+ 'updates',
135
+ 'about',
136
+ 'help',
137
+ 'dev',
138
+ // Account (rendered separately)
139
+ 'account',
140
+ 'activityLog',
119
141
  ];
120
142
  /**
121
- * Group definitions matching the mobile app's settings structure.
122
- *
123
- * i18n keys use the format `Settings.GroupLabel.<Id>` and must exist
124
- * in every consumer's i18n catalog.
143
+ * Ordered list of all settings menu items. The array order defines the
144
+ * display order within each section. Items without registry setting keys
145
+ * are navigation-only (e.g. gallery, guests).
125
146
  */
126
- export const SETTINGS_GROUPS = [
127
- {
128
- id: 'appearance',
129
- labelKey: 'Settings.Appearance',
130
- icon: 'Palette',
131
- keys: [
132
- 'appearance.theme',
133
- 'appearance.clockType',
134
- 'appearance.enableDayColors',
135
- ],
136
- },
137
- {
138
- id: 'calendarType',
139
- labelKey: 'Settings.CalendarType',
140
- icon: 'ArrowLeftRight',
141
- keys: ['calendarView.type'],
142
- },
147
+ export const SETTINGS_MENU_ITEMS = [
148
+ // -- Calendar section --
143
149
  {
144
150
  id: 'calendarView',
145
- labelKey: 'Settings.CalendarView',
151
+ labelKey: 'Settings.Calendar',
146
152
  icon: 'Eye',
153
+ section: 'calendar',
154
+ screens: [
155
+ 'calendarDayView',
156
+ 'calendarWeekView',
157
+ 'chronologicalHeader',
158
+ 'chronologicalFooter',
159
+ 'chronologicalMenu',
160
+ 'dayColors',
161
+ 'dateTime',
162
+ ],
147
163
  keys: [
148
164
  'calendarView.showCalendarNames',
149
165
  'calendarView.splitView',
150
166
  'calendarView.dayViewZoom',
151
167
  'calendarView.weekViewZoom',
152
168
  'calendarView.calendarColumns',
169
+ 'appearance.enableDayColors',
153
170
  ],
154
171
  },
155
172
  {
156
- id: 'activities',
157
- labelKey: 'Settings.Activities',
158
- icon: 'CalendarPlus',
159
- keys: ['eventForm.', 'chronologicalEventForm.'],
173
+ id: 'calendars',
174
+ labelKey: 'Common.Calendars',
175
+ icon: 'Calendar',
176
+ section: 'calendar',
177
+ screens: ['calendars'],
160
178
  },
161
179
  {
162
- id: 'dateWeather',
163
- labelKey: 'Settings.DateWeather',
164
- icon: 'CloudSun',
165
- keys: [
166
- 'calendarView.showWeatherOnTimeline',
167
- 'calendarView.weatherLocation',
168
- 'calendarView.showWeatherOnEvents',
169
- ],
180
+ id: 'eventCategories',
181
+ labelKey: 'EventCategory.SettingsTitle',
182
+ icon: 'Tag',
183
+ section: 'calendar',
184
+ screens: ['eventCategories'],
185
+ calendarType: 'chronological',
170
186
  },
171
187
  {
172
- id: 'soundAlerts',
173
- labelKey: 'Settings.SoundAlerts',
174
- icon: 'Volume2',
188
+ id: 'notification',
189
+ labelKey: 'Settings.Notification',
190
+ icon: 'Bell',
191
+ section: 'calendar',
192
+ screens: ['notification'],
175
193
  keys: [
176
- 'sound.reminderVolume',
177
- 'sound.mediaVolume',
178
194
  'sound.reminderAlarmSound',
179
- 'sound.startAlarmSound',
180
- 'sound.endAlarmSound',
181
195
  'sound.reminderAlarmTimeout',
196
+ 'sound.reminderVolume',
182
197
  'sound.allowCustomReminderSounds',
183
- 'notification.',
184
- 'sound.ttsEnabled',
185
- 'sound.ttsRate',
186
- 'sound.alarmSound',
187
198
  ],
188
199
  },
189
200
  {
190
201
  id: 'timer',
191
202
  labelKey: 'Settings.TimerTitle',
192
203
  icon: 'Timer',
204
+ section: 'calendar',
205
+ screens: ['timerFeatures', 'timer'],
206
+ appMode: 'ENROLLED',
193
207
  keys: [
194
208
  'chronological.timer.',
195
209
  'timer.',
@@ -197,32 +211,140 @@ export const SETTINGS_GROUPS = [
197
211
  'sound.timerAlarmTimeout',
198
212
  'sound.timerVolume',
199
213
  ],
214
+ },
215
+ {
216
+ id: 'guests',
217
+ labelKey: 'Settings.GuestUsers',
218
+ icon: 'Users',
219
+ section: 'calendar',
220
+ screens: ['guests'],
221
+ },
222
+ {
223
+ id: 'eventForm',
224
+ labelKey: 'SettingsEventForm.Title',
225
+ icon: 'ClipboardList',
226
+ section: 'calendar',
227
+ screens: ['eventForm'],
228
+ calendarType: 'time-based',
229
+ keys: ['eventForm.'],
230
+ },
231
+ {
232
+ id: 'chronologicalEventForm',
233
+ labelKey: 'SettingsChronologicalEventForm.Title',
234
+ icon: 'Plus',
235
+ section: 'calendar',
236
+ screens: ['chronologicalEventForm'],
237
+ calendarType: 'chronological',
238
+ keys: ['chronologicalEventForm.'],
239
+ },
240
+ {
241
+ id: 'weather',
242
+ labelKey: 'Settings.WeatherTitle',
243
+ icon: 'CloudSun',
244
+ section: 'calendar',
245
+ screens: ['weather'],
246
+ calendarType: 'time-based',
247
+ keys: [
248
+ 'calendarView.showWeatherOnTimeline',
249
+ 'calendarView.weatherLocation',
250
+ 'calendarView.showWeatherOnEvents',
251
+ ],
252
+ },
253
+ {
254
+ id: 'gallery',
255
+ labelKey: 'Gallery.Title',
256
+ icon: 'Images',
257
+ section: 'calendar',
258
+ screens: ['gallery'],
259
+ noScrollView: true,
260
+ },
261
+ {
262
+ id: 'audioClips',
263
+ labelKey: 'AudioClips.Title',
264
+ icon: 'AudioLines',
265
+ section: 'calendar',
266
+ screens: ['audioClips'],
267
+ noScrollView: true,
268
+ },
269
+ {
270
+ id: 'gestures',
271
+ labelKey: 'Settings.FunctionsTitle',
272
+ icon: 'Hand',
273
+ section: 'calendar',
274
+ screens: ['gestures'],
275
+ keys: ['touch.'],
276
+ },
277
+ {
278
+ id: 'calendarType',
279
+ labelKey: 'Settings.Mode',
280
+ icon: 'SlidersHorizontal',
281
+ section: 'calendar',
282
+ screens: ['calendarType'],
283
+ keys: ['calendarView.type'],
284
+ },
285
+ {
286
+ id: 'sharing',
287
+ labelKey: 'Settings.Sharing',
288
+ icon: 'Share2',
289
+ section: 'calendar',
290
+ screens: ['icalSubscriptions'],
291
+ },
292
+ // -- Unit section --
293
+ {
294
+ id: 'screen',
295
+ labelKey: 'Settings.Screen',
296
+ icon: 'LaptopMinimal',
297
+ section: 'unit',
298
+ screens: ['displayDensity', 'darkMode'],
299
+ keys: ['appearance.theme', 'appearance.clockType'],
300
+ },
301
+ {
302
+ id: 'volume',
303
+ labelKey: 'Common.Volume',
304
+ icon: 'Volume2',
305
+ section: 'unit',
306
+ screens: ['volume'],
200
307
  appMode: 'ENROLLED',
308
+ keys: [
309
+ 'sound.mediaVolume',
310
+ 'sound.startAlarmSound',
311
+ 'sound.endAlarmSound',
312
+ 'sound.ttsEnabled',
313
+ 'sound.ttsRate',
314
+ 'sound.alarmSound',
315
+ ],
201
316
  },
202
317
  {
203
318
  id: 'lockScreen',
204
319
  labelKey: 'Settings.LockScreen',
205
320
  icon: 'Lock',
206
- keys: ['lockScreen.'],
321
+ section: 'unit',
322
+ screens: ['inactivity', 'lockScreen'],
207
323
  appMode: 'ENROLLED',
324
+ keys: ['lockScreen.'],
208
325
  },
209
326
  {
210
- id: 'touch',
211
- labelKey: 'Settings.FunctionsTitle',
212
- icon: 'Hand',
213
- keys: ['touch.'],
327
+ id: 'language',
328
+ labelKey: 'Settings.Language',
329
+ icon: 'Globe',
330
+ section: 'unit',
331
+ screens: ['language'],
332
+ keys: ['language.'],
214
333
  },
215
334
  {
216
- id: 'chronological',
217
- labelKey: 'Settings.Chronological',
218
- icon: 'List',
219
- keys: ['chronological.'],
220
- calendarType: 'chronological',
335
+ id: 'wifi',
336
+ labelKey: 'Settings.WifiTitle',
337
+ icon: 'Wifi',
338
+ section: 'unit',
339
+ screens: ['wifi'],
340
+ appMode: 'ENROLLED',
221
341
  },
222
342
  {
223
- id: 'device',
343
+ id: 'devices',
224
344
  labelKey: 'SettingsDevices.Title',
225
345
  icon: 'Smartphone',
346
+ section: 'unit',
347
+ screens: ['devices'],
226
348
  keys: [
227
349
  'device.',
228
350
  'calendarView.autoReturnToTodayEnabled',
@@ -230,10 +352,70 @@ export const SETTINGS_GROUPS = [
230
352
  ],
231
353
  },
232
354
  {
233
- id: 'language',
234
- labelKey: 'Settings.Language',
235
- icon: 'Globe',
236
- keys: ['language.'],
355
+ id: 'profiles',
356
+ labelKey: 'SettingsProfiles.Title',
357
+ icon: 'BookCopy',
358
+ section: 'unit',
359
+ screens: ['profiles'],
360
+ },
361
+ {
362
+ id: 'tts',
363
+ labelKey: 'Settings.TtsTitle',
364
+ icon: 'Speech',
365
+ section: 'unit',
366
+ screens: ['tts'],
367
+ },
368
+ {
369
+ id: 'updates',
370
+ labelKey: 'Settings.UpdatesTitle',
371
+ icon: 'Download',
372
+ section: 'unit',
373
+ screens: ['updates'],
374
+ },
375
+ {
376
+ id: 'about',
377
+ labelKey: 'Settings.About',
378
+ icon: 'Info',
379
+ section: 'unit',
380
+ screens: ['restoreDefaults', 'recycleAppData', 'license', 'version'],
381
+ },
382
+ {
383
+ id: 'help',
384
+ labelKey: 'Settings.HelpTitle',
385
+ icon: 'CircleQuestionMark',
386
+ section: 'unit',
387
+ screens: ['help'],
388
+ },
389
+ {
390
+ id: 'dev',
391
+ labelKey: 'Settings.Dev',
392
+ icon: 'FolderCode',
393
+ section: 'unit',
394
+ screens: [
395
+ 'devDeviceInfo',
396
+ 'devComponents',
397
+ 'devStores',
398
+ 'devActions',
399
+ 'devAuth',
400
+ 'devNavigation',
401
+ 'devNetwork',
402
+ 'devAndroidSettings',
403
+ ],
404
+ },
405
+ // -- Account (rendered separately) --
406
+ {
407
+ id: 'account',
408
+ labelKey: 'Settings.Profile',
409
+ icon: 'User',
410
+ section: 'account',
411
+ screens: ['account', 'mfa'],
412
+ },
413
+ {
414
+ id: 'activityLog',
415
+ labelKey: 'SettingsActivityLog.Title',
416
+ icon: 'ScrollText',
417
+ section: 'account',
418
+ screens: ['activityLog'],
237
419
  },
238
420
  ];
239
421
  /**
@@ -465,15 +647,15 @@ export const SETTINGS_LABELS = {
465
647
  'eventForm.checklist': { labelKey: 'EventChecklist.DefaultName' },
466
648
  'eventForm.images': { labelKey: 'EventImageGallery.SectionTitle' },
467
649
  'eventForm.audioClips': { labelKey: 'EventAudioGallery.SectionTitle' },
468
- 'eventForm.notificationReceivers': { labelKey: 'EventForm.NotificationReceivers' },
650
+ 'eventForm.notificationReceivers': {
651
+ labelKey: 'EventForm.NotificationReceivers',
652
+ },
469
653
  'eventForm.visibility': { labelKey: 'EventVisibility.Title' },
654
+ // ── Chronological event form ────────────────────────────────────────────
655
+ 'chronologicalEventForm.field.category': {
656
+ labelKey: 'ChronologicalEventForm.CategoryField',
657
+ },
470
658
  };
471
- /**
472
- * Check whether a setting key matches a group's key matchers.
473
- */
474
- function matchesGroup(key, group) {
475
- return group.keys.some((matcher) => key === matcher || (matcher.endsWith('.') && key.startsWith(matcher)));
476
- }
477
659
  /** Return the index of the first matching key pattern in the group, for sorting. */
478
660
  function groupKeyIndex(key, keys) {
479
661
  for (let i = 0; i < keys.length; i++) {
@@ -500,68 +682,62 @@ function registryKeyOrder() {
500
682
  return _registryKeyOrder;
501
683
  }
502
684
  /**
503
- * Group parsed setting entries into user-facing groups matching the mobile
504
- * app's settings menu structure.
505
- *
506
- * Takes a flat list of parsed settings (from `parseSettingsSnapshot`) and
507
- * buckets them into the standard groups, filtering by calendar type and
508
- * excluded keys.
685
+ * Group parsed setting entries using the app's menu structure (SETTINGS_MENU_ITEMS).
686
+ * Only includes menu items that have `keys` defined (editable settings).
687
+ * This ensures the web editor has the exact same menu items, order, sections,
688
+ * labels, and icons as the mobile app.
509
689
  */
510
- export function groupSettingsForDevice(allSettings, calendarType, appMode) {
690
+ export function groupSettingsForWeb(allSettings, calendarType, appMode) {
511
691
  // Filter excluded and hidden settings
512
692
  const settings = allSettings.filter((s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key) && s.uiType !== 'HIDDEN');
513
- // Calendar type filtering for event form keys
514
693
  const isChronological = calendarType === 'chronological';
515
694
  const filteredSettings = settings.filter((s) => {
516
- if (isChronological && s.key.startsWith('eventForm.')) {
695
+ if (isChronological && s.key.startsWith('eventForm.'))
517
696
  return false;
518
- }
519
- if (!isChronological && s.key.startsWith('chronologicalEventForm.')) {
697
+ if (!isChronological && s.key.startsWith('chronologicalEventForm.'))
520
698
  return false;
521
- }
522
- // Per-setting calendar type restriction
523
- if (s.calendarType && s.calendarType !== calendarType) {
699
+ if (s.calendarType && s.calendarType !== calendarType)
524
700
  return false;
525
- }
526
- // Per-setting app mode restriction
527
- if (s.appMode && s.appMode !== appMode) {
701
+ if (s.appMode && s.appMode !== appMode)
528
702
  return false;
529
- }
530
703
  return true;
531
704
  });
532
705
  const claimed = new Set();
533
706
  const result = [];
534
- for (const group of SETTINGS_GROUPS) {
535
- // Skip groups not relevant for current calendar type
536
- if (group.calendarType && group.calendarType !== calendarType) {
707
+ for (const menuItem of SETTINGS_MENU_ITEMS) {
708
+ // Skip items without keys (navigation-only like gallery, guests, etc.)
709
+ if (!menuItem.keys || menuItem.keys.length === 0)
537
710
  continue;
538
- }
539
- // Skip groups not relevant for current app mode
540
- if (group.appMode && group.appMode !== appMode) {
711
+ // Skip items not relevant for current calendar type
712
+ if (menuItem.calendarType && menuItem.calendarType !== calendarType)
541
713
  continue;
542
- }
543
- const matched = filteredSettings.filter((s) => !claimed.has(s.key) && matchesGroup(s.key, group));
544
- if (matched.length === 0) {
714
+ // Skip items not relevant for current app mode
715
+ if (menuItem.appMode && menuItem.appMode !== appMode)
545
716
  continue;
546
- }
547
- // Sort matched settings by the order defined in group.keys,
548
- // with registry declaration order as tiebreaker for prefix matches
717
+ const matched = filteredSettings.filter((s) => {
718
+ if (claimed.has(s.key))
719
+ return false;
720
+ return menuItem.keys.some((k) => k.endsWith('.') ? s.key.startsWith(k) : s.key === k);
721
+ });
722
+ if (matched.length === 0)
723
+ continue;
724
+ // Sort by key order within the menu item's keys array
549
725
  const order = registryKeyOrder();
550
726
  matched.sort((a, b) => {
551
- const aIdx = groupKeyIndex(a.key, group.keys);
552
- const bIdx = groupKeyIndex(b.key, group.keys);
553
- if (aIdx !== bIdx) {
727
+ const aIdx = groupKeyIndex(a.key, menuItem.keys);
728
+ const bIdx = groupKeyIndex(b.key, menuItem.keys);
729
+ if (aIdx !== bIdx)
554
730
  return aIdx - bIdx;
555
- }
556
731
  return (order.get(a.key) ?? Infinity) - (order.get(b.key) ?? Infinity);
557
732
  });
558
733
  for (const s of matched) {
559
734
  claimed.add(s.key);
560
735
  }
561
736
  result.push({
562
- id: group.id,
563
- labelKey: group.labelKey,
564
- icon: group.icon,
737
+ id: menuItem.id,
738
+ labelKey: menuItem.labelKey,
739
+ icon: menuItem.icon,
740
+ section: menuItem.section,
565
741
  settings: matched,
566
742
  });
567
743
  }
@@ -577,23 +753,25 @@ export const DEFAULT_REGISTRY_CONFIG = {
577
753
  // Shared option arrays (reused across multiple settings)
578
754
  // ---------------------------------------------------------------------------
579
755
  const ALARM_SOUND_OPTIONS = [
580
- { value: 'none', labelKey: 'Settings.Option.AlarmSound.None' },
581
- { value: 'alarm1', labelKey: 'Settings.Option.AlarmSound.Alarm1' },
582
- { value: 'alarm2', labelKey: 'Settings.Option.AlarmSound.Alarm2' },
583
- { value: 'alarm3', labelKey: 'Settings.Option.AlarmSound.Alarm3' },
584
- { value: 'alarm4', labelKey: 'Settings.Option.AlarmSound.Alarm4' },
585
- { value: 'alarm5', labelKey: 'Settings.Option.AlarmSound.Alarm5' },
586
- { value: 'alarm6', labelKey: 'Settings.Option.AlarmSound.Alarm6' },
587
- { value: 'alarm7', labelKey: 'Settings.Option.AlarmSound.Alarm7' },
588
- { value: 'alarm8', labelKey: 'Settings.Option.AlarmSound.Alarm8' },
589
- { value: 'alarm9', labelKey: 'Settings.Option.AlarmSound.Alarm9' },
756
+ { value: 'none', labelKey: 'AlarmAsset.none' },
757
+ { value: 'alarm1', labelKey: 'AlarmAsset.alarm1' },
758
+ { value: 'alarm2', labelKey: 'AlarmAsset.alarm2' },
759
+ { value: 'alarm3', labelKey: 'AlarmAsset.alarm3' },
760
+ { value: 'alarm4', labelKey: 'AlarmAsset.alarm4' },
761
+ { value: 'alarm5', labelKey: 'AlarmAsset.alarm5' },
762
+ { value: 'alarm6', labelKey: 'AlarmAsset.alarm6' },
763
+ { value: 'alarm7', labelKey: 'AlarmAsset.alarm7' },
764
+ { value: 'alarm8', labelKey: 'AlarmAsset.alarm8' },
765
+ { value: 'alarm9', labelKey: 'AlarmAsset.alarm9' },
590
766
  ];
591
767
  const ALARM_TIMEOUT_OPTIONS = [
592
- { value: 1, labelKey: 'Settings.Option.AlarmTimeout.1min' },
593
- { value: 2, labelKey: 'Settings.Option.AlarmTimeout.2min' },
594
- { value: 3, labelKey: 'Settings.Option.AlarmTimeout.3min' },
595
- { value: 5, labelKey: 'Settings.Option.AlarmTimeout.5min' },
596
- { value: 10, labelKey: 'Settings.Option.AlarmTimeout.10min' },
768
+ { value: 5, labelKey: 'Duration.CountSeconds', labelValues: { count: 5 } },
769
+ { value: 30, labelKey: 'Duration.CountSeconds', labelValues: { count: 30 } },
770
+ { value: 60, labelKey: 'Duration.CountMinutes', labelValues: { count: 1 } },
771
+ { value: 120, labelKey: 'Duration.CountMinutes', labelValues: { count: 2 } },
772
+ { value: 180, labelKey: 'Duration.CountMinutes', labelValues: { count: 3 } },
773
+ { value: 300, labelKey: 'Duration.CountMinutes', labelValues: { count: 5 } },
774
+ { value: 600, labelKey: 'Duration.CountMinutes', labelValues: { count: 10 } },
597
775
  ];
598
776
  const DAY_VIEW_ZOOM_OPTIONS = [
599
777
  { value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
@@ -635,11 +813,16 @@ function buildEntries(config) {
635
813
  // Appearance
636
814
  // ═══════════════════════════════════════════════════════════════════════
637
815
  'appearance.theme': def('appearance', 'string', config.defaultTheme, 'CUSTOM_THEME_PICKER', {
638
- options: [
639
- { value: 'light', labelKey: 'Settings.Option.Theme.Light' },
640
- { value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
641
- { value: 'system', labelKey: 'Settings.Option.Theme.System' },
642
- ],
816
+ options: config.isEnrolled
817
+ ? [
818
+ { value: 'light', labelKey: 'Settings.Option.Theme.Light' },
819
+ { value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
820
+ ]
821
+ : [
822
+ { value: 'light', labelKey: 'Settings.Option.Theme.Light' },
823
+ { value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
824
+ { value: 'system', labelKey: 'Settings.Option.Theme.System' },
825
+ ],
643
826
  }),
644
827
  'appearance.clockType': def('appearance', 'string', 'digital', 'CUSTOM_CLOCK_TYPE', {
645
828
  options: [
@@ -719,24 +902,35 @@ function buildEntries(config) {
719
902
  // ═══════════════════════════════════════════════════════════════════════
720
903
  'sound.timerVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER'),
721
904
  'sound.reminderVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
722
- 'sound.mediaVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
723
- 'sound.alarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
905
+ 'sound.mediaVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', {
906
+ appMode: 'ENROLLED',
907
+ }),
908
+ 'sound.alarmSound': def('sound', 'string', 'alarm1', 'SELECT', {
909
+ options: ALARM_SOUND_OPTIONS,
910
+ }),
724
911
  'sound.reminderAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
725
- 'sound.startAlarmSound': def('sound', 'string', 'none', 'SELECT', { options: ALARM_SOUND_OPTIONS, appMode: 'ENROLLED' }),
726
- 'sound.endAlarmSound': def('sound', 'string', 'none', 'SELECT', { options: ALARM_SOUND_OPTIONS, appMode: 'ENROLLED' }),
912
+ 'sound.startAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
913
+ 'sound.endAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
727
914
  'sound.timerAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
728
- 'sound.timerAlarmTimeout': def('sound', 'number', 3, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
729
- 'sound.reminderAlarmTimeout': def('sound', 'number', 3, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
915
+ 'sound.timerAlarmTimeout': def('sound', 'number', 180, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
916
+ 'sound.reminderAlarmTimeout': def('sound', 'number', 180, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
730
917
  'sound.allowCustomReminderSounds': def('sound', 'boolean', false, 'TOGGLE'),
731
918
  'sound.ttsEnabled': def('sound', 'boolean', config.isEnrolled, 'TOGGLE', { appMode: 'ENROLLED' }),
732
- 'sound.ttsRate': def('sound', 'number', 1.0, 'SLIDER', { sliderConfig: { min: 0.5, max: 2, step: 0.1 }, appMode: 'ENROLLED' }),
919
+ 'sound.ttsRate': def('sound', 'number', 1.0, 'SLIDER', {
920
+ sliderConfig: { min: 0.5, max: 2, step: 0.1 },
921
+ appMode: 'ENROLLED',
922
+ }),
733
923
  // ═══════════════════════════════════════════════════════════════════════
734
924
  // Timer
735
925
  // ═══════════════════════════════════════════════════════════════════════
736
926
  'timer.showTimeRemaining': def('timer', 'boolean', true, 'TOGGLE'),
737
- 'timer.showEndTime': def('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
927
+ 'timer.showEndTime': def('timer', 'boolean', true, 'TOGGLE', {
928
+ calendarType: 'time-based',
929
+ }),
738
930
  'timer.showRestartButton': def('timer', 'boolean', true, 'TOGGLE'),
739
- 'timer.showPauseButton': def('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
931
+ 'timer.showPauseButton': def('timer', 'boolean', true, 'TOGGLE', {
932
+ calendarType: 'time-based',
933
+ }),
740
934
  // ═══════════════════════════════════════════════════════════════════════
741
935
  // Lock screen
742
936
  // ═══════════════════════════════════════════════════════════════════════
@@ -867,8 +1061,10 @@ function buildEntries(config) {
867
1061
  // Chronological event form
868
1062
  // ═══════════════════════════════════════════════════════════════════════
869
1063
  // Fixed fields
1064
+ 'chronologicalEventForm.fixedField.category': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
870
1065
  'chronologicalEventForm.fixedField.allDay': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
871
1066
  'chronologicalEventForm.fixedField.endTime': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
1067
+ 'chronologicalEventForm.fixedField.alarm': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
872
1068
  'chronologicalEventForm.fixedField.visibility': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
873
1069
  // Toggleable fields
874
1070
  'chronologicalEventForm.field.acknowledge': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
@@ -878,6 +1074,7 @@ function buildEntries(config) {
878
1074
  'chronologicalEventForm.field.extraImages': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
879
1075
  'chronologicalEventForm.field.reminders': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
880
1076
  'chronologicalEventForm.field.audioClips': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
1077
+ 'chronologicalEventForm.field.category': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
881
1078
  // Suggest end time
882
1079
  'chronologicalEventForm.suggestEndTime.enabled': def('chronologicalEventForm', 'boolean', false, 'TOGGLE'),
883
1080
  'chronologicalEventForm.suggestEndTime.value': def('chronologicalEventForm', 'number', 30, 'SLIDER', { sliderConfig: { min: 5, max: 480, step: 5 } }),
@@ -901,6 +1098,9 @@ function buildEntries(config) {
901
1098
  { value: 'Custom', labelKey: 'Settings.Option.Visibility.Custom' },
902
1099
  ],
903
1100
  }),
1101
+ // Default alarm toggles
1102
+ 'chronologicalEventForm.defaultAlarm.atStart': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
1103
+ 'chronologicalEventForm.defaultAlarm.atEnd': def('chronologicalEventForm', 'boolean', false, 'TOGGLE'),
904
1104
  // Reminder presets
905
1105
  'chronologicalEventForm.reminderPresets.timed': def('chronologicalEventForm', 'json', [5, 15, 30, 60, 120, 1440], 'CUSTOM_REMINDER_PRESETS'),
906
1106
  'chronologicalEventForm.reminderPresets.allDay': def('chronologicalEventForm', 'json', [
@@ -1076,9 +1276,7 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
1076
1276
  groups.set(category, []);
1077
1277
  }
1078
1278
  const labelDef = SETTINGS_LABELS[key];
1079
- const entryDef = key in registry.entries
1080
- ? registry.entries[key]
1081
- : undefined;
1279
+ const entryDef = key in registry.entries ? registry.entries[key] : undefined;
1082
1280
  groups.get(category).push({
1083
1281
  key,
1084
1282
  name,
@@ -1087,7 +1285,9 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
1087
1285
  descriptionKey: labelDef?.descriptionKey,
1088
1286
  value,
1089
1287
  uiType: entryDef?.uiType ?? 'TEXT_INPUT',
1090
- ...(entryDef?.options && { options: entryDef.options }),
1288
+ ...(entryDef?.options && {
1289
+ options: entryDef.options,
1290
+ }),
1091
1291
  ...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
1092
1292
  ...(entryDef?.appMode && { appMode: entryDef.appMode }),
1093
1293
  ...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),