@focus8/settings-registry 0.8.3 → 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,53 +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 = [
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 = [
107
110
  // Calendar section
108
111
  'calendarView',
112
+ 'calendars',
113
+ 'eventCategories',
114
+ 'notification',
109
115
  'timer',
110
- 'activities',
111
- 'dateWeather',
112
- 'touch',
116
+ 'guests',
117
+ 'eventForm',
118
+ 'chronologicalEventForm',
119
+ 'weather',
120
+ 'gallery',
121
+ 'audioClips',
122
+ 'gestures',
113
123
  'calendarType',
114
- 'chronological',
124
+ 'sharing',
115
125
  // Unit section
116
- 'appearance',
117
- 'soundAlerts',
126
+ 'screen',
127
+ 'volume',
118
128
  'lockScreen',
119
129
  'language',
120
- 'device',
130
+ 'wifi',
131
+ 'devices',
132
+ 'profiles',
133
+ 'tts',
134
+ 'updates',
135
+ 'about',
136
+ 'help',
137
+ 'dev',
138
+ // Account (rendered separately)
139
+ 'account',
140
+ 'activityLog',
121
141
  ];
122
142
  /**
123
- * Group definitions matching the mobile app's settings structure.
124
- *
125
- * i18n keys use the format `Settings.GroupLabel.<Id>` and must exist
126
- * 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).
127
146
  */
128
- export const SETTINGS_GROUPS = [
129
- // ── Calendar section ──────────────────────────────────────────────────
147
+ export const SETTINGS_MENU_ITEMS = [
148
+ // -- Calendar section --
130
149
  {
131
150
  id: 'calendarView',
132
- labelKey: 'Settings.CalendarView',
151
+ labelKey: 'Settings.Calendar',
133
152
  icon: 'Eye',
153
+ section: 'calendar',
154
+ screens: [
155
+ 'calendarDayView',
156
+ 'calendarWeekView',
157
+ 'chronologicalHeader',
158
+ 'chronologicalFooter',
159
+ 'chronologicalMenu',
160
+ 'dayColors',
161
+ 'dateTime',
162
+ ],
134
163
  keys: [
135
164
  'calendarView.showCalendarNames',
136
165
  'calendarView.splitView',
137
166
  'calendarView.dayViewZoom',
138
167
  'calendarView.weekViewZoom',
139
168
  'calendarView.calendarColumns',
169
+ 'appearance.enableDayColors',
170
+ ],
171
+ },
172
+ {
173
+ id: 'calendars',
174
+ labelKey: 'Common.Calendars',
175
+ icon: 'Calendar',
176
+ section: 'calendar',
177
+ screens: ['calendars'],
178
+ },
179
+ {
180
+ id: 'eventCategories',
181
+ labelKey: 'EventCategory.SettingsTitle',
182
+ icon: 'Tag',
183
+ section: 'calendar',
184
+ screens: ['eventCategories'],
185
+ calendarType: 'chronological',
186
+ },
187
+ {
188
+ id: 'notification',
189
+ labelKey: 'Settings.Notification',
190
+ icon: 'Bell',
191
+ section: 'calendar',
192
+ screens: ['notification'],
193
+ keys: [
194
+ 'sound.reminderAlarmSound',
195
+ 'sound.reminderAlarmTimeout',
196
+ 'sound.reminderVolume',
197
+ 'sound.allowCustomReminderSounds',
140
198
  ],
141
199
  },
142
200
  {
143
201
  id: 'timer',
144
202
  labelKey: 'Settings.TimerTitle',
145
203
  icon: 'Timer',
204
+ section: 'calendar',
205
+ screens: ['timerFeatures', 'timer'],
206
+ appMode: 'ENROLLED',
146
207
  keys: [
147
208
  'chronological.timer.',
148
209
  'timer.',
@@ -150,18 +211,39 @@ export const SETTINGS_GROUPS = [
150
211
  'sound.timerAlarmTimeout',
151
212
  'sound.timerVolume',
152
213
  ],
153
- appMode: 'ENROLLED',
154
214
  },
155
215
  {
156
- id: 'activities',
157
- labelKey: 'Settings.Activities',
158
- icon: 'CalendarPlus',
159
- keys: ['eventForm.', 'chronologicalEventForm.'],
216
+ id: 'guests',
217
+ labelKey: 'Settings.GuestUsers',
218
+ icon: 'Users',
219
+ section: 'calendar',
220
+ screens: ['guests'],
160
221
  },
161
222
  {
162
- id: 'dateWeather',
163
- labelKey: 'Settings.DateWeather',
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',
164
243
  icon: 'CloudSun',
244
+ section: 'calendar',
245
+ screens: ['weather'],
246
+ calendarType: 'time-based',
165
247
  keys: [
166
248
  'calendarView.showWeatherOnTimeline',
167
249
  'calendarView.weatherLocation',
@@ -169,48 +251,64 @@ export const SETTINGS_GROUPS = [
169
251
  ],
170
252
  },
171
253
  {
172
- id: 'touch',
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',
173
271
  labelKey: 'Settings.FunctionsTitle',
174
272
  icon: 'Hand',
273
+ section: 'calendar',
274
+ screens: ['gestures'],
175
275
  keys: ['touch.'],
176
276
  },
177
277
  {
178
278
  id: 'calendarType',
179
- labelKey: 'Settings.CalendarType',
180
- icon: 'ArrowLeftRight',
279
+ labelKey: 'Settings.Mode',
280
+ icon: 'SlidersHorizontal',
281
+ section: 'calendar',
282
+ screens: ['calendarType'],
181
283
  keys: ['calendarView.type'],
182
284
  },
183
285
  {
184
- id: 'chronological',
185
- labelKey: 'Settings.Chronological',
186
- icon: 'List',
187
- keys: ['chronological.'],
188
- calendarType: 'chronological',
286
+ id: 'sharing',
287
+ labelKey: 'Settings.Sharing',
288
+ icon: 'Share2',
289
+ section: 'calendar',
290
+ screens: ['icalSubscriptions'],
189
291
  },
190
- // ── Unit section ──────────────────────────────────────────────────────
292
+ // -- Unit section --
191
293
  {
192
- id: 'appearance',
193
- labelKey: 'Settings.Appearance',
194
- icon: 'Palette',
195
- keys: [
196
- 'appearance.theme',
197
- 'appearance.clockType',
198
- 'appearance.enableDayColors',
199
- ],
294
+ id: 'screen',
295
+ labelKey: 'Settings.Screen',
296
+ icon: 'LaptopMinimal',
297
+ section: 'unit',
298
+ screens: ['displayDensity', 'darkMode'],
299
+ keys: ['appearance.theme', 'appearance.clockType'],
200
300
  },
201
301
  {
202
- id: 'soundAlerts',
203
- labelKey: 'Settings.SoundAlerts',
302
+ id: 'volume',
303
+ labelKey: 'Common.Volume',
204
304
  icon: 'Volume2',
305
+ section: 'unit',
306
+ screens: ['volume'],
307
+ appMode: 'ENROLLED',
205
308
  keys: [
206
- 'sound.reminderVolume',
207
309
  'sound.mediaVolume',
208
- 'sound.reminderAlarmSound',
209
310
  'sound.startAlarmSound',
210
311
  'sound.endAlarmSound',
211
- 'sound.reminderAlarmTimeout',
212
- 'sound.allowCustomReminderSounds',
213
- 'notification.',
214
312
  'sound.ttsEnabled',
215
313
  'sound.ttsRate',
216
314
  'sound.alarmSound',
@@ -220,25 +318,105 @@ export const SETTINGS_GROUPS = [
220
318
  id: 'lockScreen',
221
319
  labelKey: 'Settings.LockScreen',
222
320
  icon: 'Lock',
223
- keys: ['lockScreen.'],
321
+ section: 'unit',
322
+ screens: ['inactivity', 'lockScreen'],
224
323
  appMode: 'ENROLLED',
324
+ keys: ['lockScreen.'],
225
325
  },
226
326
  {
227
327
  id: 'language',
228
328
  labelKey: 'Settings.Language',
229
329
  icon: 'Globe',
330
+ section: 'unit',
331
+ screens: ['language'],
230
332
  keys: ['language.'],
231
333
  },
232
334
  {
233
- id: 'device',
335
+ id: 'wifi',
336
+ labelKey: 'Settings.WifiTitle',
337
+ icon: 'Wifi',
338
+ section: 'unit',
339
+ screens: ['wifi'],
340
+ appMode: 'ENROLLED',
341
+ },
342
+ {
343
+ id: 'devices',
234
344
  labelKey: 'SettingsDevices.Title',
235
345
  icon: 'Smartphone',
346
+ section: 'unit',
347
+ screens: ['devices'],
236
348
  keys: [
237
349
  'device.',
238
350
  'calendarView.autoReturnToTodayEnabled',
239
351
  'calendarView.autoReturnToTodayTimeoutSeconds',
240
352
  ],
241
353
  },
354
+ {
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'],
419
+ },
242
420
  ];
243
421
  /**
244
422
  * Settings excluded from remote editing (web portal / profile management).
@@ -469,15 +647,15 @@ export const SETTINGS_LABELS = {
469
647
  'eventForm.checklist': { labelKey: 'EventChecklist.DefaultName' },
470
648
  'eventForm.images': { labelKey: 'EventImageGallery.SectionTitle' },
471
649
  'eventForm.audioClips': { labelKey: 'EventAudioGallery.SectionTitle' },
472
- 'eventForm.notificationReceivers': { labelKey: 'EventForm.NotificationReceivers' },
650
+ 'eventForm.notificationReceivers': {
651
+ labelKey: 'EventForm.NotificationReceivers',
652
+ },
473
653
  'eventForm.visibility': { labelKey: 'EventVisibility.Title' },
654
+ // ── Chronological event form ────────────────────────────────────────────
655
+ 'chronologicalEventForm.field.category': {
656
+ labelKey: 'ChronologicalEventForm.CategoryField',
657
+ },
474
658
  };
475
- /**
476
- * Check whether a setting key matches a group's key matchers.
477
- */
478
- function matchesGroup(key, group) {
479
- return group.keys.some((matcher) => key === matcher || (matcher.endsWith('.') && key.startsWith(matcher)));
480
- }
481
659
  /** Return the index of the first matching key pattern in the group, for sorting. */
482
660
  function groupKeyIndex(key, keys) {
483
661
  for (let i = 0; i < keys.length; i++) {
@@ -504,68 +682,62 @@ function registryKeyOrder() {
504
682
  return _registryKeyOrder;
505
683
  }
506
684
  /**
507
- * Group parsed setting entries into user-facing groups matching the mobile
508
- * app's settings menu structure.
509
- *
510
- * Takes a flat list of parsed settings (from `parseSettingsSnapshot`) and
511
- * buckets them into the standard groups, filtering by calendar type and
512
- * 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.
513
689
  */
514
- export function groupSettingsForDevice(allSettings, calendarType, appMode) {
690
+ export function groupSettingsForWeb(allSettings, calendarType, appMode) {
515
691
  // Filter excluded and hidden settings
516
692
  const settings = allSettings.filter((s) => !EXCLUDED_DEVICE_SETTINGS.has(s.key) && s.uiType !== 'HIDDEN');
517
- // Calendar type filtering for event form keys
518
693
  const isChronological = calendarType === 'chronological';
519
694
  const filteredSettings = settings.filter((s) => {
520
- if (isChronological && s.key.startsWith('eventForm.')) {
695
+ if (isChronological && s.key.startsWith('eventForm.'))
521
696
  return false;
522
- }
523
- if (!isChronological && s.key.startsWith('chronologicalEventForm.')) {
697
+ if (!isChronological && s.key.startsWith('chronologicalEventForm.'))
524
698
  return false;
525
- }
526
- // Per-setting calendar type restriction
527
- if (s.calendarType && s.calendarType !== calendarType) {
699
+ if (s.calendarType && s.calendarType !== calendarType)
528
700
  return false;
529
- }
530
- // Per-setting app mode restriction
531
- if (s.appMode && s.appMode !== appMode) {
701
+ if (s.appMode && s.appMode !== appMode)
532
702
  return false;
533
- }
534
703
  return true;
535
704
  });
536
705
  const claimed = new Set();
537
706
  const result = [];
538
- for (const group of SETTINGS_GROUPS) {
539
- // Skip groups not relevant for current calendar type
540
- 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)
541
710
  continue;
542
- }
543
- // Skip groups not relevant for current app mode
544
- if (group.appMode && group.appMode !== appMode) {
711
+ // Skip items not relevant for current calendar type
712
+ if (menuItem.calendarType && menuItem.calendarType !== calendarType)
545
713
  continue;
546
- }
547
- const matched = filteredSettings.filter((s) => !claimed.has(s.key) && matchesGroup(s.key, group));
548
- if (matched.length === 0) {
714
+ // Skip items not relevant for current app mode
715
+ if (menuItem.appMode && menuItem.appMode !== appMode)
549
716
  continue;
550
- }
551
- // Sort matched settings by the order defined in group.keys,
552
- // 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
553
725
  const order = registryKeyOrder();
554
726
  matched.sort((a, b) => {
555
- const aIdx = groupKeyIndex(a.key, group.keys);
556
- const bIdx = groupKeyIndex(b.key, group.keys);
557
- if (aIdx !== bIdx) {
727
+ const aIdx = groupKeyIndex(a.key, menuItem.keys);
728
+ const bIdx = groupKeyIndex(b.key, menuItem.keys);
729
+ if (aIdx !== bIdx)
558
730
  return aIdx - bIdx;
559
- }
560
731
  return (order.get(a.key) ?? Infinity) - (order.get(b.key) ?? Infinity);
561
732
  });
562
733
  for (const s of matched) {
563
734
  claimed.add(s.key);
564
735
  }
565
736
  result.push({
566
- id: group.id,
567
- labelKey: group.labelKey,
568
- icon: group.icon,
737
+ id: menuItem.id,
738
+ labelKey: menuItem.labelKey,
739
+ icon: menuItem.icon,
740
+ section: menuItem.section,
569
741
  settings: matched,
570
742
  });
571
743
  }
@@ -581,23 +753,25 @@ export const DEFAULT_REGISTRY_CONFIG = {
581
753
  // Shared option arrays (reused across multiple settings)
582
754
  // ---------------------------------------------------------------------------
583
755
  const ALARM_SOUND_OPTIONS = [
584
- { value: 'none', labelKey: 'Settings.Option.AlarmSound.None' },
585
- { value: 'alarm1', labelKey: 'Settings.Option.AlarmSound.Alarm1' },
586
- { value: 'alarm2', labelKey: 'Settings.Option.AlarmSound.Alarm2' },
587
- { value: 'alarm3', labelKey: 'Settings.Option.AlarmSound.Alarm3' },
588
- { value: 'alarm4', labelKey: 'Settings.Option.AlarmSound.Alarm4' },
589
- { value: 'alarm5', labelKey: 'Settings.Option.AlarmSound.Alarm5' },
590
- { value: 'alarm6', labelKey: 'Settings.Option.AlarmSound.Alarm6' },
591
- { value: 'alarm7', labelKey: 'Settings.Option.AlarmSound.Alarm7' },
592
- { value: 'alarm8', labelKey: 'Settings.Option.AlarmSound.Alarm8' },
593
- { 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' },
594
766
  ];
595
767
  const ALARM_TIMEOUT_OPTIONS = [
596
- { value: 1, labelKey: 'Settings.Option.AlarmTimeout.1min' },
597
- { value: 2, labelKey: 'Settings.Option.AlarmTimeout.2min' },
598
- { value: 3, labelKey: 'Settings.Option.AlarmTimeout.3min' },
599
- { value: 5, labelKey: 'Settings.Option.AlarmTimeout.5min' },
600
- { 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 } },
601
775
  ];
602
776
  const DAY_VIEW_ZOOM_OPTIONS = [
603
777
  { value: 15, labelKey: 'Settings.Option.DayViewZoom.15min' },
@@ -639,11 +813,16 @@ function buildEntries(config) {
639
813
  // Appearance
640
814
  // ═══════════════════════════════════════════════════════════════════════
641
815
  'appearance.theme': def('appearance', 'string', config.defaultTheme, 'CUSTOM_THEME_PICKER', {
642
- options: [
643
- { value: 'light', labelKey: 'Settings.Option.Theme.Light' },
644
- { value: 'dark', labelKey: 'Settings.Option.Theme.Dark' },
645
- { value: 'system', labelKey: 'Settings.Option.Theme.System' },
646
- ],
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
+ ],
647
826
  }),
648
827
  'appearance.clockType': def('appearance', 'string', 'digital', 'CUSTOM_CLOCK_TYPE', {
649
828
  options: [
@@ -723,24 +902,35 @@ function buildEntries(config) {
723
902
  // ═══════════════════════════════════════════════════════════════════════
724
903
  'sound.timerVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER'),
725
904
  'sound.reminderVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
726
- 'sound.mediaVolume': def('sound', 'number', 0.5, 'VOLUME_SLIDER', { appMode: 'ENROLLED' }),
727
- '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
+ }),
728
911
  'sound.reminderAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
729
- 'sound.startAlarmSound': def('sound', 'string', 'none', 'SELECT', { options: ALARM_SOUND_OPTIONS, appMode: 'ENROLLED' }),
730
- '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 }),
731
914
  'sound.timerAlarmSound': def('sound', 'string', 'alarm1', 'SELECT', { options: ALARM_SOUND_OPTIONS }),
732
- 'sound.timerAlarmTimeout': def('sound', 'number', 3, 'SELECT', { options: ALARM_TIMEOUT_OPTIONS }),
733
- '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 }),
734
917
  'sound.allowCustomReminderSounds': def('sound', 'boolean', false, 'TOGGLE'),
735
918
  'sound.ttsEnabled': def('sound', 'boolean', config.isEnrolled, 'TOGGLE', { appMode: 'ENROLLED' }),
736
- '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
+ }),
737
923
  // ═══════════════════════════════════════════════════════════════════════
738
924
  // Timer
739
925
  // ═══════════════════════════════════════════════════════════════════════
740
926
  'timer.showTimeRemaining': def('timer', 'boolean', true, 'TOGGLE'),
741
- 'timer.showEndTime': def('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
927
+ 'timer.showEndTime': def('timer', 'boolean', true, 'TOGGLE', {
928
+ calendarType: 'time-based',
929
+ }),
742
930
  'timer.showRestartButton': def('timer', 'boolean', true, 'TOGGLE'),
743
- 'timer.showPauseButton': def('timer', 'boolean', true, 'TOGGLE', { calendarType: 'time-based' }),
931
+ 'timer.showPauseButton': def('timer', 'boolean', true, 'TOGGLE', {
932
+ calendarType: 'time-based',
933
+ }),
744
934
  // ═══════════════════════════════════════════════════════════════════════
745
935
  // Lock screen
746
936
  // ═══════════════════════════════════════════════════════════════════════
@@ -871,6 +1061,7 @@ function buildEntries(config) {
871
1061
  // Chronological event form
872
1062
  // ═══════════════════════════════════════════════════════════════════════
873
1063
  // Fixed fields
1064
+ 'chronologicalEventForm.fixedField.category': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
874
1065
  'chronologicalEventForm.fixedField.allDay': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
875
1066
  'chronologicalEventForm.fixedField.endTime': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
876
1067
  'chronologicalEventForm.fixedField.alarm': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
@@ -883,6 +1074,7 @@ function buildEntries(config) {
883
1074
  'chronologicalEventForm.field.extraImages': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
884
1075
  'chronologicalEventForm.field.reminders': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
885
1076
  'chronologicalEventForm.field.audioClips': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
1077
+ 'chronologicalEventForm.field.category': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
886
1078
  // Suggest end time
887
1079
  'chronologicalEventForm.suggestEndTime.enabled': def('chronologicalEventForm', 'boolean', false, 'TOGGLE'),
888
1080
  'chronologicalEventForm.suggestEndTime.value': def('chronologicalEventForm', 'number', 30, 'SLIDER', { sliderConfig: { min: 5, max: 480, step: 5 } }),
@@ -906,6 +1098,9 @@ function buildEntries(config) {
906
1098
  { value: 'Custom', labelKey: 'Settings.Option.Visibility.Custom' },
907
1099
  ],
908
1100
  }),
1101
+ // Default alarm toggles
1102
+ 'chronologicalEventForm.defaultAlarm.atStart': def('chronologicalEventForm', 'boolean', true, 'TOGGLE'),
1103
+ 'chronologicalEventForm.defaultAlarm.atEnd': def('chronologicalEventForm', 'boolean', false, 'TOGGLE'),
909
1104
  // Reminder presets
910
1105
  'chronologicalEventForm.reminderPresets.timed': def('chronologicalEventForm', 'json', [5, 15, 30, 60, 120, 1440], 'CUSTOM_REMINDER_PRESETS'),
911
1106
  'chronologicalEventForm.reminderPresets.allDay': def('chronologicalEventForm', 'json', [
@@ -1081,9 +1276,7 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
1081
1276
  groups.set(category, []);
1082
1277
  }
1083
1278
  const labelDef = SETTINGS_LABELS[key];
1084
- const entryDef = key in registry.entries
1085
- ? registry.entries[key]
1086
- : undefined;
1279
+ const entryDef = key in registry.entries ? registry.entries[key] : undefined;
1087
1280
  groups.get(category).push({
1088
1281
  key,
1089
1282
  name,
@@ -1092,7 +1285,9 @@ export function parseSettingsSnapshot(json, calendarType, registry = defaultRegi
1092
1285
  descriptionKey: labelDef?.descriptionKey,
1093
1286
  value,
1094
1287
  uiType: entryDef?.uiType ?? 'TEXT_INPUT',
1095
- ...(entryDef?.options && { options: entryDef.options }),
1288
+ ...(entryDef?.options && {
1289
+ options: entryDef.options,
1290
+ }),
1096
1291
  ...(entryDef?.sliderConfig && { sliderConfig: entryDef.sliderConfig }),
1097
1292
  ...(entryDef?.appMode && { appMode: entryDef.appMode }),
1098
1293
  ...(entryDef?.calendarType && { calendarType: entryDef.calendarType }),