From f8971480b1f800b5c537b8e94bf2ab4412c1fff0 Mon Sep 17 00:00:00 2001 From: LeviiiCraft2 Date: Sun, 12 Oct 2025 19:10:44 +0200 Subject: [PATCH 1/2] Implement liquidGlass visuals & enhanced absence view --- src/settings/editor/settingsBlueprint.ts | 5 ++ src/settings/settings.ts | 1 + src/utils/scriptable/componentHelper.ts | 63 +++++++++++++++++++++++- src/views/absences.ts | 12 +++-- src/views/exams.ts | 14 +++--- src/views/grades.ts | 16 +++--- 6 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/settings/editor/settingsBlueprint.ts b/src/settings/editor/settingsBlueprint.ts index 40d2d57..632fca2 100644 --- a/src/settings/editor/settingsBlueprint.ts +++ b/src/settings/editor/settingsBlueprint.ts @@ -394,6 +394,11 @@ export const settingsBlueprint: SettingsCategory = { description: 'The background color of the widget.', type: SettingsValueType.COLOR, }, + liquidGlass: { + title: '🪟 Liquid Glass Mode', + description: 'Use slightly transparent backgrounds for better readability on iOS "Clear" homescreens (iOS 26).', + type: SettingsValueType.ON_OFF, + }, }, }, debugSettings: { diff --git a/src/settings/settings.ts b/src/settings/settings.ts index a38326c..1134dbc 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -90,6 +90,7 @@ export const defaultSettings = { fontSize: 14, footer: true, backgroundColor: unparsedColors.background.tertiary, + liquidGlass: false, }, debugSettings: { diff --git a/src/utils/scriptable/componentHelper.ts b/src/utils/scriptable/componentHelper.ts index 5a527fe..eaeafa9 100644 --- a/src/utils/scriptable/componentHelper.ts +++ b/src/utils/scriptable/componentHelper.ts @@ -34,15 +34,20 @@ export function addBreak( showToTime: boolean, widgetConfig: Settings ) { + // Liquid Glass + const __liquidItemColors = getItemColors(colors.background.primary, widgetConfig, false) const breakContainer = makeTimelineEntry(to, breakFrom, widgetConfig, { - backgroundColor: colors.background.primary, + backgroundColor: __liquidItemColors.backgroundColor, showTime: true, showToTime: showToTime, toTime: breakTo, }) + + breakContainer.cornerRadius = widgetConfig.appearance.cornerRadius + const breakTitle = breakContainer.addText('Break') breakTitle.font = Font.mediumSystemFont(widgetConfig.appearance.fontSize) - breakTitle.textColor = colors.text.secondary + breakTitle.textColor = __liquidItemColors.secondaryTextColor breakContainer.addSpacer() } @@ -136,9 +141,63 @@ function getLessonColors(lesson: TransformedLesson) { secondaryTextColor = colors.text.red } + // Liquid Glass override + try { + const liquid = !!(widgetConfig && widgetConfig.appearance && widgetConfig.appearance.liquidGlass) + if (liquid) { + // For normal lessons use a slight translucent overlay that adapts to appearance: + // light appearance white 30% overlay, dark appearance black 30% overlay + if (lesson.state !== LessonState.CANCELED && lesson.state !== LessonState.FREE && !isRescheduledAway) { + backgroundColor = Color.dynamic(new Color('#ffffff', 0.30), new Color('#000000', 0.30)) + // Ensure high contrast text colors + textColor = Color.dynamic(new Color('#000000'), new Color('#ffffff')) + secondaryTextColor = Color.dynamic(new Color('#666666'), new Color('#999999')) + } else { + // For cancelled/free/rescheduled keep disabled look but ensure contrast + textColor = colors.text.disabled + secondaryTextColor = colors.text.disabled + backgroundColor = colors.background.primary + } + } + } catch (e) { + // if something fails, fall back to default colors + console.warn('Liquid Glass override failed: ' + e) + } + return { backgroundColor, textColor, secondaryTextColor } } +/** + * Generic color helper that applies the Liquid Glass override when enabled. + * baseBackgroundColor: the original background color that would have been used (may be null) + * widgetConfig: the widget configuration object + * disabled: whether the item should be shown as disabled (uses disabled text/background) + */ +function getItemColors(baseBackgroundColor, widgetConfig, disabled = false) { + let backgroundColor = baseBackgroundColor ?? colors.background.primary + let textColor = colors.text.primary + let secondaryTextColor = colors.text.secondary + if (disabled) { + backgroundColor = colors.background.primary + textColor = colors.text.disabled + secondaryTextColor = colors.text.disabled + } + // Liquid Glass override (same approach as lessons) + try { + const liquid = !!(widgetConfig && widgetConfig.appearance && widgetConfig.appearance.liquidGlass) + if (liquid && !disabled) { + // translucent overlay that adapts to appearance + backgroundColor = Color.dynamic(new Color('#ffffff', 0.30), new Color('#000000', 0.30)) + // high contrast text colors + textColor = Color.dynamic(new Color('#000000'), new Color('#ffffff')); + secondaryTextColor = Color.dynamic(new Color('#666666'), new Color('#999999')) + } + } catch (e) { + console.warn('getItemColors Liquid Glass override failed: ' + e) + } + return { backgroundColor, textColor, secondaryTextColor } +} + /** * Adds a lesson to the widget. This includes its subject, additional info (as an icon) and the time. * The state is also shown as through colors. (canceled, event) diff --git a/src/views/absences.ts b/src/views/absences.ts index e819ddd..636596c 100644 --- a/src/views/absences.ts +++ b/src/views/absences.ts @@ -37,7 +37,9 @@ export function addViewAbsences( absenceContainer.layoutHorizontally() absenceContainer.setPadding(padding, padding, padding, padding) absenceContainer.spacing = widgetConfig.appearance.spacing - absenceContainer.backgroundColor = colors.background.primary + const __liquidItemColors = getItemColors(colors.background.primary, widgetConfig, absence.isExcused); + absenceContainer.backgroundColor = __liquidItemColors.backgroundColor + const textColor = __liquidItemColors.textColor absenceContainer.cornerRadius = widgetConfig.appearance.cornerRadius const shortFromDate = absence.from.toLocaleDateString(LOCALE, { day: '2-digit', month: 'short' }) @@ -58,7 +60,7 @@ export function addViewAbsences( widgetConfig.appearance.spacing, Font.mediumSystemFont(widgetConfig.appearance.fontSize), widgetConfig.appearance.fontSize, - colors.text.primary + textColor ) /** @@ -77,14 +79,14 @@ export function addViewAbsences( type: 'icon', icon: 'pills.circle', size: widgetConfig.appearance.fontSize, - color: colors.text.primary, + color: textColor, priority: 1, }) // add the absence date staticLayoutRow.addItem({ type: 'text', - color: colors.text.secondary, + color: __liquidItemColors.secondaryTextColor, variants: [ { text: shortFromDate, @@ -115,7 +117,7 @@ export function addViewAbsences( // add the creator staticLayoutRow.addItem({ type: 'text', - color: colors.text.secondary, + color: __liquidItemColors.secondaryTextColor, variants: [ { // toLoweCase to make it less prominent diff --git a/src/views/exams.ts b/src/views/exams.ts index 102990c..1617cd8 100644 --- a/src/views/exams.ts +++ b/src/views/exams.ts @@ -75,7 +75,9 @@ export function addViewExams( examContainer.layoutHorizontally() examContainer.setPadding(padding, padding, padding, padding) examContainer.spacing = widgetConfig.appearance.spacing - examContainer.backgroundColor = backgroundColor + const __liquidItemColors = getItemColors(backgroundColor, widgetConfig, false) + examContainer.backgroundColor = __liquidItemColors.backgroundColor + const textColor = __liquidItemColors.textColor examContainer.cornerRadius = widgetConfig.appearance.cornerRadius // build the layout row @@ -84,7 +86,7 @@ export function addViewExams( widgetConfig.appearance.spacing, Font.mediumSystemFont(widgetConfig.appearance.fontSize), widgetConfig.appearance.fontSize, - colors.text.primary + textColor ) /** @@ -102,7 +104,7 @@ export function addViewExams( type: 'icon', icon: 'book.circle', size: widgetConfig.appearance.fontSize, - color: colors.text.primary, + color: textColor, priority: 1, }) @@ -120,14 +122,14 @@ export function addViewExams( }, ], fontSize: widgetConfig.appearance.fontSize, - color: colors.text.primary, + color: textColor, }) // add the exam type staticLayoutRow.addItem({ type: 'text', variants: [{ text: exam.type, priority: 4 }], - color: colors.text.secondary, + color: __liquidItemColors.secondaryTextColor, }) // add the date @@ -143,7 +145,7 @@ export function addViewExams( priority: 6, }, ], - color: colors.text.primary, + color: textColor, }) staticLayoutRow.build(examContainer) diff --git a/src/views/grades.ts b/src/views/grades.ts index ca10c4d..2ebde6f 100644 --- a/src/views/grades.ts +++ b/src/views/grades.ts @@ -73,7 +73,9 @@ export function addViewGrades( gradeContainer.layoutHorizontally() gradeContainer.setPadding(padding, padding, padding, padding) gradeContainer.spacing = widgetConfig.appearance.spacing - gradeContainer.backgroundColor = backgroundColor + const __liquidItemColors = getItemColors(backgroundColor, widgetConfig, false) + gradeContainer.backgroundColor = __liquidItemColors.backgroundColor + const textColor = __liquidItemColors.textColor gradeContainer.cornerRadius = widgetConfig.appearance.cornerRadius // build the layout row @@ -82,7 +84,7 @@ export function addViewGrades( widgetConfig.appearance.spacing, Font.mediumSystemFont(widgetConfig.appearance.fontSize), widgetConfig.appearance.fontSize, - colors.text.primary + textColor ) /** @@ -100,7 +102,7 @@ export function addViewGrades( type: 'icon', icon: iconName ?? defaultIconName, size: widgetConfig.appearance.fontSize, - color: colors.text.primary, + color: textColor, priority: iconName ? 1 : 3, }) @@ -115,7 +117,7 @@ export function addViewGrades( priority: 1, }, ], - color: colors.text.primary, + color: textColor, }) } @@ -128,7 +130,7 @@ export function addViewGrades( priority: 2, }, ], - color: colors.text.primary, + color: textColor, }) // add the exam type @@ -140,13 +142,13 @@ export function addViewGrades( priority: 5, }, ], - color: colors.text.secondary, + color: __liquidItemColors.secondaryTextColor, }) // add the date staticLayoutRow.addItem({ type: 'text', - color: colors.text.secondary, + color: __liquidItemColors.secondaryTextColor, variants: [ { text: shortDate, From 6a13f1e1163572562fae0bb720e35564e3af8670 Mon Sep 17 00:00:00 2001 From: LeviiiCraft2 Date: Mon, 13 Oct 2025 12:03:01 +0200 Subject: [PATCH 2/2] fix: added missing imports --- src/utils/scriptable/componentHelper.ts | 8 ++++---- src/views/absences.ts | 1 + src/views/exams.ts | 1 + src/views/grades.ts | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils/scriptable/componentHelper.ts b/src/utils/scriptable/componentHelper.ts index eaeafa9..3d2cb36 100644 --- a/src/utils/scriptable/componentHelper.ts +++ b/src/utils/scriptable/componentHelper.ts @@ -119,7 +119,7 @@ function makeTimelineEntry( return lessonContainer } -function getLessonColors(lesson: TransformedLesson) { +function getLessonColors(lesson: TransformedLesson, widgetConfig: Settings) { /** Whether the lesson was rescheduled away from here. (isSource) * The lesson state seems to be CANCELED? */ const isRescheduledAway = lesson.rescheduleInfo?.isSource @@ -173,7 +173,7 @@ function getLessonColors(lesson: TransformedLesson) { * widgetConfig: the widget configuration object * disabled: whether the item should be shown as disabled (uses disabled text/background) */ -function getItemColors(baseBackgroundColor, widgetConfig, disabled = false) { +export function getItemColors(baseBackgroundColor: Color, widgetConfig: Settings, disabled = false) { let backgroundColor = baseBackgroundColor ?? colors.background.primary let textColor = colors.text.primary let secondaryTextColor = colors.text.secondary @@ -221,7 +221,7 @@ export function addWidgetLesson( } ) { // get the colors for the lesson based on its state (red, disabled, normal) - const { backgroundColor, textColor, secondaryTextColor } = getLessonColors(lesson) + const { backgroundColor, textColor, secondaryTextColor } = getLessonColors(lesson, widgetConfig) // consider breaks during the combined lesson let toTime = lesson.to @@ -312,7 +312,7 @@ export function addWidgetLesson( * @param widgetConfig */ export function fillContainerWithSubject(lesson: TransformedLesson, container: WidgetStack, widgetConfig: Settings) { - const { backgroundColor, textColor, secondaryTextColor } = getLessonColors(lesson) + const { backgroundColor, textColor, secondaryTextColor } = getLessonColors(lesson, widgetConfig) container.backgroundColor = backgroundColor container.layoutHorizontally() diff --git a/src/views/absences.ts b/src/views/absences.ts index 636596c..3b55101 100644 --- a/src/views/absences.ts +++ b/src/views/absences.ts @@ -5,6 +5,7 @@ import { Duration } from '@/utils/duration' import { getCharHeight } from '@/utils/helper' import { StaticLayoutRow } from '@/utils/scriptable/layout/staticLayoutRow' import { ViewBuildData } from '@/widget' +import { getItemColors } from '@/utils/scriptable/componentHelper' export function addViewAbsences( absences: TransformedAbsence[], diff --git a/src/views/exams.ts b/src/views/exams.ts index 1617cd8..f4ed669 100644 --- a/src/views/exams.ts +++ b/src/views/exams.ts @@ -6,6 +6,7 @@ import { getCharHeight, getCharWidth, getTextWidth } from '@/utils/helper' import { FlowLayoutRow } from '@/utils/scriptable/layout/flowLayoutRow' import { StaticLayoutRow } from '@/utils/scriptable/layout/staticLayoutRow' import { ViewBuildData } from '@/widget' +import { getItemColors } from '@/utils/scriptable/componentHelper' export function addViewExams( exams: TransformedExam[], diff --git a/src/views/grades.ts b/src/views/grades.ts index 2ebde6f..7d0c165 100644 --- a/src/views/grades.ts +++ b/src/views/grades.ts @@ -5,6 +5,7 @@ import { TransformedGrade } from '@/types/transformed' import { getCharHeight } from '@/utils/helper' import { StaticLayoutRow } from '@/utils/scriptable/layout/staticLayoutRow' import { ViewBuildData } from '@/widget' +import { getItemColors } from '@/utils/scriptable/componentHelper' // TODO: the date of grades matches the date of the exam, not when it was added (breaks scope) export function addViewGrades(