185 files

This commit is contained in:
Home Assistant Version Control committed 2026-08-19 11:38:13 +00:00
1 parent 5f9330abd7
commit a718af6423
185 files changed
+16868 -3088

No files matched your search

@@ -1144,6 +1144,23 @@ class TaskMateChildCard extends LitElement {
.recurrence-label ha-icon { --mdc-icon-size: 12px; }
/* Dependency-blocked (#793). Under dim the row stays readable so a child
can see what unlocks next; show leaves it looking normal, but either
way the tap is refused in the handler. */
.chore-card.dependency-blocked {
opacity: 0.5;
filter: grayscale(0.5);
}
.dependency-label {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 0.72rem;
font-weight: 600;
color: var(--secondary-text-color);
}
.dependency-label ha-icon { --mdc-icon-size: 12px; }
/* Recurring chore not yet available — dimmed/greyed */
.chore-card.recurrence-unavailable {
@@ -1272,6 +1289,11 @@ class TaskMateChildCard extends LitElement {
.pre-tile-icon ha-icon { --mdc-icon-size: 64px; color: var(--primary-color); }
.pre-tile-stars { display: flex; gap: 2px; }
.pre-tile-stars ha-icon { --mdc-icon-size: 18px; color: #f1c40f; }
.pre-tile-points {
display: flex; align-items: center; gap: 4px;
font-size: 1.15rem; font-weight: 700; color: var(--primary-text-color);
}
.pre-tile-points ha-icon { --mdc-icon-size: 20px; color: #f1c40f; }
.pre-tile-label {
font-size: 0.85rem; font-weight: 700; text-align: center;
color: var(--secondary-text-color); line-height: 1.2;
@@ -1282,6 +1304,7 @@ class TaskMateChildCard extends LitElement {
}
.pre-tile.done .pre-tile-icon ha-icon { opacity: 0.35; }
.pre-tile.done .pre-tile-stars { opacity: 0.35; }
.pre-tile.done .pre-tile-points { opacity: 0.35; }
.pre-tile-tick {
position: absolute; inset: 0;
display: grid; place-items: center;
@@ -2418,8 +2441,8 @@ class TaskMateChildCard extends LitElement {
${this._renderRoulette(child, childChores, pointsIcon)}
${this.config.pre_reader === true ? html`
<div class="pre-reader-grid">
${childChores.map((chore, index) =>
this._renderPreReaderTile(chore, child, todaysCompletions, index))}
${childChores.map((chore) =>
this._renderPreReaderTile(chore, child, pointsIcon, todaysCompletions))}
</div>
` : childChores.map((chore, index) => html`
${chore.task_type === 'timed'
@@ -2626,13 +2649,21 @@ class TaskMateChildCard extends LitElement {
const dueDaysMode = this.config.due_days_mode || "hide";
const elapsedTimeMode = this.config.elapsed_time_mode || "dim";
const dependencyMode = this.config.dependency_mode || "hide";
const recurrenceDoneMode = this.config.recurrence_done_mode || "dim";
// Annotate each chore with done-state + handlers, preserving the classic path's logic.
const rows = childChores.map((chore, i) => {
const { done, completions } = this._isChoreDone(chore, child, todaysCompletions);
const loading = !!this._loading[chore.id];
// Blocked chores only reach here under dim/show — hide is filtered
// upstream. Either way the tap must be refused (#793).
const blocked = !!chore._isDependencyBlocked && !done;
// Recurring chore still inside its cooldown. Excluded when done so the
// undo chip stays live, exactly as on the classic row.
const recLocked = !!chore._isRecurrenceLocked && !done;
const onAct = () => {
if (loading) return;
if (loading || blocked || recLocked) return;
if (done) this._handleUndo(chore, child, completions);
else this._handleComplete(chore, child);
};
@@ -2642,10 +2673,12 @@ class TaskMateChildCard extends LitElement {
const dimmed = !done && (
(notDueToday && dueDaysMode === "dim") ||
(chore._isTimeElapsed && elapsedTimeMode === "dim") ||
(blocked && dependencyMode === "dim") ||
(chore._isRecurrenceLocked && recurrenceDoneMode === "dim") ||
chore._isLockedPreview === true
);
return {
chore, child, done, loading, onAct, index: i, dimmed,
chore, child, done, loading, onAct, index: i, dimmed, blocked, recLocked,
tone: this._designTone(i),
glyph: this._choreGlyph(chore),
points: chore.effective_points ?? chore.points,
@@ -2683,15 +2716,15 @@ class TaskMateChildCard extends LitElement {
const body = this.config.pre_reader === true
? html`
<div class="pre-reader-grid">
${childChores.map((chore, index) =>
this._renderPreReaderTile(chore, child, todaysCompletions, index))}
${childChores.map((chore) =>
this._renderPreReaderTile(chore, child, pointsIcon, todaysCompletions))}
</div>`
: design === "playroom" ? this._designPlayroom(child, rows, remaining, tone) :
design === "console" ? this._designConsole(child, rows, remaining, tone) :
this._designCleanpro(child, rows, remaining, tone);
return html`<ha-card class="tmd" style="--hd:${hd}">
${this._designHeaderFull(child, design, remaining, rows.length, tone, pendingPoints, pointsIcon)}
${this._designHeaderFull(child, design, remaining, rows.length, tone, pendingPoints)}
<div class="tmd-bd">
${attrs.vacation_active ? html`
<div class="tmd-vacation">
@@ -2742,7 +2775,7 @@ class TaskMateChildCard extends LitElement {
}
/** Designed header: avatar/title, remaining pill, pending-points chip. */
_designHeaderFull(child, design, remaining, total, tone, pendingPoints, pointsIcon) {
_designHeaderFull(child, design, remaining, total, tone, pendingPoints) {
const title = design === "console"
? `${(child.name || "").toUpperCase()} · ${this._t("child.todays_chores").toUpperCase()}`
: this._t("points_display.single_title", { name: child.name });
@@ -2775,11 +2808,21 @@ class TaskMateChildCard extends LitElement {
/** Shared meta tags (mandatory / photo / description) for a designed chore row. */
_designChoreMeta(r) {
const showDesc = this.config.show_description === true && r.chore.description;
// A blocked chore says what unlocks it here too — the classic row carries
// the same label, and a dimmed row with no reason is just confusing (#793).
const depNames = r.blocked ? (r.chore._dependencyNames || []) : [];
// Same reason label the classic row carries, so a dimmed designed row is
// not just mysteriously grey (#803).
const recLabel = r.recLocked
? (r.chore.recurrence ? r.chore.recurrence.replace(/_/g, " ") : this._t("child.recurring"))
: "";
return html`
${r.mandatory || r.photo ? html`
${r.mandatory || r.photo || depNames.length || recLabel ? html`
<div class="tmd-meta">
${r.mandatory ? html`<span class="tmd-tag mandatory">⚠ ${this._t("child.mandatory")}</span>` : ""}
${r.photo ? html`<span class="tmd-tag photo">📷 ${this._t("child.photo_needed")}</span>` : ""}
${depNames.length ? html`<span class="tmd-tag">🔒 ${this._t("child.blocked_by_dependency", { chores: depNames.join(", ") })}</span>` : ""}
${recLabel ? html`<span class="tmd-tag">🕒 ${recLabel}</span>` : ""}
</div>` : ""}
${showDesc ? html`<div class="tmd-desc">${r.chore.description}</div>` : ""}`;
}
@@ -2795,7 +2838,7 @@ class TaskMateChildCard extends LitElement {
_designDoneBtn(r, label, cls) {
return html`<button class="btn ${cls || ""}"
?disabled=${r.loading}
?disabled=${r.loading || r.blocked || r.recLocked}
@click=${(e) => { e.stopPropagation(); r.onAct(); }}>${label}</button>`;
}
@@ -2820,7 +2863,7 @@ class TaskMateChildCard extends LitElement {
return this._renderBonusSubtasks(r.chore, r.child, r.pointsIcon, r.todaysCompletions);
}
_designPlayroom(child, rows, remaining, tone) {
_designPlayroom(child, rows, _remaining, _tone) {
if (rows.length === 0) return html`<div class="tmd-empty">${this._t("child.all_done")}</div>`;
return html`<div class="tmd-chores">
${rows.map(r => r.timed ? this._designTimed(r) : html`
@@ -2840,7 +2883,7 @@ class TaskMateChildCard extends LitElement {
</div>`;
}
_designConsole(child, rows, remaining, tone) {
_designConsole(child, rows, _remaining, _tone) {
if (rows.length === 0) return html`<div class="tmd-empty">${this._t("child.all_done")}</div>`;
return html`<div class="grid">
${rows.map(r => r.timed ? this._designTimed(r) : html`
@@ -2862,7 +2905,7 @@ class TaskMateChildCard extends LitElement {
</div>`;
}
_designCleanpro(child, rows, remaining, tone) {
_designCleanpro(child, rows, _remaining, _tone) {
if (rows.length === 0) return html`<div class="tmd-empty">${this._t("child.all_done")}</div>`;
return html`<div class="tmd-checklist">
${rows.map(r => r.timed ? this._designTimed(r) : html`
@@ -2951,11 +2994,8 @@ class TaskMateChildCard extends LitElement {
_filterAndSortChores(chores, child) {
const childId = String(child.id || "");
const childName = child.name;
const choreOrder = child.chore_order || [];
// Debug logging to diagnose assignment filtering issues
// Get today's day of week from sensor (set by backend) or compute client-side
const entity = this.hass?.states?.[this.config.entity];
const attrs = (window.__taskmate_attrs && window.__taskmate_attrs(this.hass, this.config.entity)) || entity?.attributes || {};
@@ -2965,6 +3005,18 @@ class TaskMateChildCard extends LitElement {
const dueDaysMode = this.config.due_days_mode || 'hide';
const showDueDaysOnly = this.config.show_due_days_only !== false;
const elapsedTimeMode = this.config.elapsed_time_mode || 'dim';
// Hide by default: the backend simply reports a dependency-blocked chore
// as unavailable, so hiding is what matches it (#793).
const dependencyMode = this.config.dependency_mode || 'hide';
const recurrenceDoneMode = this.config.recurrence_done_mode || 'dim';
const allCompletionsToday = attrs.todays_completions || [];
// Availability comes from the backend's own matrix (#803). The card cannot
// work a recurrence window out for itself: last_completed is not exposed,
// `recurrence` is omitted from the payload when it is the default weekly,
// and the maths is calendar-month aware. A missing matrix (older backend,
// or a sensor that hasn't published yet) has to read as available, or the
// card would blank itself.
const availability = attrs.chore_availability || {};
// First, filter chores for this child and time category
const filteredChores = chores.filter(chore => {
@@ -3112,6 +3164,42 @@ class TaskMateChildCard extends LitElement {
chore._isDueToday = isDueToday;
chore._hasDueDays = hasDueDays;
// Chore dependencies (#793). Mirrors is_chore_available_for_child():
// a prerequisite counts as satisfied only when this same child has an
// approved, non-bonus completion of it today. todays_completions is
// already scoped to today, so the date check is implicit. A looser rule
// here would unlock a chore the backend then refuses to complete.
const dependsOn = Array.isArray(chore.depends_on) ? chore.depends_on : [];
chore._isDependencyBlocked = dependsOn.length > 0 && !dependsOn.every(
(depId) => allCompletionsToday.some(
(comp) => comp.chore_id === depId
&& String(comp.child_id) === childId
&& comp.approved
&& !comp.bonus_subtask_id
)
);
chore._dependencyNames = chore._isDependencyBlocked
? dependsOn
.map((depId) => (chores.find((c) => c.id === depId) || {}).name)
.filter(Boolean)
: [];
if (chore._isDependencyBlocked && dependencyMode === 'hide') return false;
// Recurring chores in their cooldown window (#803). Both of these are
// read by the render path, which until now found them undefined.
chore._isRecurring = (chore.schedule_mode || 'specific_days') === 'recurring';
const availableNow = (availability[chore.id] || {})[childId] !== false;
// Completing a recurring chore makes it unavailable at once, so hiding on
// availability alone would vanish it the instant it is ticked — no done
// state, no way to undo. The mode is about the days *after* that.
const completedToday = allCompletionsToday.some(
(comp) => comp.chore_id === chore.id
&& (String(comp.child_id) === childId || comp.child_id === '__parent__')
&& !comp.bonus_subtask_id
);
chore._isRecurrenceLocked = chore._isRecurring && !availableNow && !completedToday;
if (chore._isRecurrenceLocked && recurrenceDoneMode === 'hide') return false;
// Mark whether this chore's time period has elapsed (grace-aware).
chore._isTimeElapsed = this._isTimePeriodElapsed(chore);
@@ -3503,8 +3591,12 @@ class TaskMateChildCard extends LitElement {
* Pre-reader mode (#683): a picture-only tile for children who can't read
* yet. No chore name, no numbers — a big icon, a row of stars for the
* points, and a huge tick when it's done.
*
* `pre_reader_points` (#795) swaps the stars for the value itself, for
* families whose children can already read a number and who found the
* rounded star count contradicted the balance on the rest of the dashboard.
*/
_renderPreReaderTile(chore, child, todaysCompletions = [], choreIndex = 0) {
_renderPreReaderTile(chore, child, pointsIcon, todaysCompletions = []) {
const dailyLimit = chore.daily_limit || 1;
// Counted the same way the standard row does: pending completions hold a
// place too, and a parent completing on behalf lands under "__parent__".
@@ -3515,11 +3607,15 @@ class TaskMateChildCard extends LitElement {
);
const isDone = childCompletionsToday.length >= dailyLimit;
const isLoading = this._loading[chore.id];
const available = chore._isAvailableForChild !== false && !chore._isLockedPreview;
const available = chore._isAvailableForChild !== false && !chore._isLockedPreview
&& !chore._isDependencyBlocked;
// Stars, not digits: a 4-year-old can count pictures, not read "+3".
// The count is deliberately lossy — it rounds and clamps to 1..5 — which
// is why showing the number instead is opt-in rather than a second row.
const points = chore.effective_points ?? chore.points ?? 0;
const stars = Math.max(1, Math.min(5, Math.round(points / 2) || 1));
const showPoints = this.config.pre_reader_points === true;
const v = window.__taskmate_chore_visual(chore);
const icon = (v.kind === "icon" ? v.icon : "")
@@ -3542,9 +3638,15 @@ class TaskMateChildCard extends LitElement {
: html`<ha-icon icon="${icon}"></ha-icon>`}
</div>
${isDone ? html`<div class="pre-tile-tick"><ha-icon icon="mdi:check-bold"></ha-icon></div>` : ''}
<div class="pre-tile-stars">
${Array.from({ length: stars }, () => html`<ha-icon icon="mdi:star"></ha-icon>`)}
</div>
${showPoints
? html`
<div class="pre-tile-points">
<ha-icon icon="${pointsIcon}"></ha-icon>+${points}
</div>`
: html`
<div class="pre-tile-stars">
${Array.from({ length: stars }, () => html`<ha-icon icon="mdi:star"></ha-icon>`)}
</div>`}
${this.config.pre_reader_labels === true
? html`<div class="pre-tile-label">${chore.name}</div>` : ''}
</button>
@@ -3592,8 +3694,6 @@ class TaskMateChildCard extends LitElement {
// This is defensive - we'd rather show "completed" incorrectly than allow double-completions
// The optimistic completion will be cleaned up once we verify the server state reflects it
if (hasOptimisticCompletion) {
// Calculate how many optimistic completions we've tracked that aren't yet in the data
const optimisticCount = optimisticData.count || 1;
// Get timestamps from actual completions for this chore/child today
const actualTimestamps = childCompletionsToday.map(comp =>
@@ -3616,8 +3716,6 @@ class TaskMateChildCard extends LitElement {
const isCompletedForToday = completionsToday >= dailyLimit;
// Check if the most recent completion is pending approval
const hasPendingCompletion = childCompletionsToday.some((comp) => !comp.approved) || hasOptimisticCompletion;
// Chore number (1-indexed for display) and color class (cycle through 8 colors)
const choreNumber = choreIndex + 1;
@@ -3626,11 +3724,21 @@ class TaskMateChildCard extends LitElement {
// Click handler for the entire row
const notDueToday = chore._hasDueDays && !chore._isDueToday && this.config.due_days_mode === 'dim';
const recurrenceDoneMode = this.config.recurrence_done_mode || 'dim';
const notAvailableRecurrence = chore._isRecurring && !chore._isAvailableForChild && recurrenceDoneMode === 'dim';
// The tap is refused whenever the chore is locked: async_complete_chore
// no-ops a recurring chore inside its window, so a clickable row under
// `show` would just swallow the tap (#805). Only the dim STYLING is
// mode-gated. Same split dependency_mode uses.
const recurrenceLocked = !!chore._isRecurrenceLocked && !isCompletedForToday;
const notAvailableRecurrence = recurrenceLocked && recurrenceDoneMode === 'dim';
// Elapsed: only dim incomplete chores — completed ones keep their green "done" style
const elapsedTimeMode = this.config.elapsed_time_mode || 'dim';
const timeElapsed = chore._isTimeElapsed && !isCompletedForToday && elapsedTimeMode === 'dim';
const isLockedPreview = !!chore._isLockedPreview && !isCompletedForToday;
// Under dim/show the blocked chore is still on screen, so the tap has to be
// refused here — the backend would reject the completion anyway (#793).
// Only `dim` greys it out; `show` is meant to look untouched.
const depBlocked = !!chore._isDependencyBlocked && !isCompletedForToday;
const depDimmed = depBlocked && (this.config.dependency_mode || 'hide') === 'dim';
const periodStartHour = isLockedPreview
? (this._getPeriodHours(chore.time_category)?.[0] ?? null)
: null;
@@ -3640,8 +3748,9 @@ class TaskMateChildCard extends LitElement {
const handleRowClick = () => {
if (isLoading) return;
if (notDueToday) return; // Dim mode — not interactive
if (notAvailableRecurrence) return; // Recurrence window not open — not interactive
if (recurrenceLocked) return; // Recurrence window not open — not interactive
if (isLockedPreview) return; // Next-period preview — not yet claimable
if (depBlocked) return; // Prerequisite chore not approved yet
if (timeElapsed) return; // Time period passed — not interactive
if (isCompletedForToday) {
this._handleUndo(chore, child, childCompletionsToday);
@@ -3649,7 +3758,7 @@ class TaskMateChildCard extends LitElement {
this._handleComplete(chore, child);
}
};
const isInteractive = !(isLoading || notDueToday || notAvailableRecurrence || isLockedPreview || timeElapsed);
const isInteractive = !(isLoading || notDueToday || recurrenceLocked || isLockedPreview || depBlocked || timeElapsed);
const handleRowKeyDown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
@@ -3659,6 +3768,8 @@ class TaskMateChildCard extends LitElement {
const titleText = isLockedPreview
? (lockedUntilLabel || this._t('child.chore_locked_until_generic'))
: depBlocked
? this._t('child.blocked_by_dependency', { chores: (chore._dependencyNames || []).join(', ') })
: notDueToday
? this._t('child.not_scheduled_for_today')
: timeElapsed
@@ -3669,7 +3780,7 @@ class TaskMateChildCard extends LitElement {
return html`
<div
class="chore-card ${chore.mandatory ? "mandatory" : ""} ${isLoading ? "loading" : ""} ${isCelebrating ? "celebrating" : ""} ${isCompletedForToday ? "completed" : ""} ${notDueToday ? "not-due-today" : ""} ${notAvailableRecurrence ? "recurrence-unavailable" : ""} ${timeElapsed ? "time-elapsed" : ""} ${isLockedPreview ? "chore-locked" : ""}"
class="chore-card ${chore.mandatory ? "mandatory" : ""} ${isLoading ? "loading" : ""} ${isCelebrating ? "celebrating" : ""} ${isCompletedForToday ? "completed" : ""} ${notDueToday ? "not-due-today" : ""} ${notAvailableRecurrence ? "recurrence-unavailable" : ""} ${timeElapsed ? "time-elapsed" : ""} ${isLockedPreview ? "chore-locked" : ""} ${depDimmed ? "dependency-blocked" : ""}"
role="button"
tabindex="${isInteractive ? '0' : '-1'}"
aria-disabled="${isInteractive ? 'false' : 'true'}"
@@ -3689,7 +3800,13 @@ class TaskMateChildCard extends LitElement {
${this.config.show_description && chore.description ? html`
<div class="chore-description">${chore.description}</div>
` : ''}
${chore._isRecurring && !chore._isAvailableForChild ? html`
${depBlocked && (chore._dependencyNames || []).length ? html`
<div class="dependency-label">
<ha-icon icon="mdi:lock-outline"></ha-icon>
${this._t('child.blocked_by_dependency', { chores: chore._dependencyNames.join(', ') })}
</div>
` : ''}
${chore._isRecurrenceLocked ? html`
<div class="recurrence-label">
<ha-icon icon="mdi:clock-outline"></ha-icon>
${chore.recurrence ? chore.recurrence.replace(/_/g, ' ') : this._t('child.recurring')}
@@ -3728,6 +3845,9 @@ class TaskMateChildCard extends LitElement {
const colorClass = `color-${choreIndex % 8}`;
const choreNumber = choreIndex + 1;
// A blocked timed chore must not be startable under dim/show (#793).
// Pause/stop stay live so a session already running can still be ended.
const depBlocked = !!chore._isDependencyBlocked;
// Calculate elapsed seconds
let elapsed = session ? (session.total_seconds_today || 0) : 0;
@@ -3759,6 +3879,7 @@ class TaskMateChildCard extends LitElement {
const nearCap = maxMin > 0 && capPct >= 85;
const handleStart = (e) => {
if (depBlocked) { e.stopPropagation(); return; }
e.stopPropagation();
if (isLoading) return;
this._loading = { ...this._loading, [`timed_${chore.id}`]: true };
@@ -3829,7 +3950,7 @@ class TaskMateChildCard extends LitElement {
<div class="timer-actions">
${state === 'idle' ? html`
<button class="timer-btn start" @click="${handleStart}" ?disabled="${isLoading}">
<button class="timer-btn start" @click="${handleStart}" ?disabled="${isLoading || depBlocked}">
<span class="btn-icon">&#9654;</span>
${this._t('child.start') || 'START'}
</button>
@@ -3845,7 +3966,7 @@ class TaskMateChildCard extends LitElement {
</button>
` : ''}
${state === 'paused' ? html`
<button class="timer-btn resume" @click="${handleStart}" ?disabled="${isLoading}">
<button class="timer-btn resume" @click="${handleStart}" ?disabled="${isLoading || depBlocked}">
<span class="btn-icon">&#9654;</span>
${this._t('child.resume') || 'RESUME'}
</button>
@@ -4708,6 +4829,19 @@ class TaskMateChildCardEditor extends LitElement {
},
},
},
{
name: 'dependency_mode',
selector: {
select: {
options: [
{ value: 'hide', label: this._t('child.editor.dependency_hide') },
{ value: 'dim', label: this._t('child.editor.dependency_dim') },
{ value: 'show', label: this._t('child.editor.dependency_show') },
],
mode: 'dropdown',
},
},
},
{
name: 'elapsed_time_mode',
selector: {
@@ -4725,6 +4859,7 @@ class TaskMateChildCardEditor extends LitElement {
{ name: 'show_description', selector: { boolean: {} } },
{ name: 'pre_reader', selector: { boolean: {} } },
{ name: 'pre_reader_labels', selector: { boolean: {} } },
{ name: 'pre_reader_points', selector: { boolean: {} } },
{ name: 'debug', selector: { boolean: {} } },
];
}
@@ -4737,11 +4872,13 @@ class TaskMateChildCardEditor extends LitElement {
card_design: this._t('common.design.field_label'),
due_days_mode: this._t('child.editor.chores_not_due_today'),
recurrence_done_mode: this._t('child.editor.recurring_when_completed'),
dependency_mode: this._t('child.editor.dependency_mode'),
elapsed_time_mode: this._t('child.editor.missed_time_chores'),
show_countdown: this._t('child.editor.show_countdown'),
show_description: this._t('child.editor.show_description'),
pre_reader: this._t('child.editor.pre_reader'),
pre_reader_labels: this._t('child.editor.pre_reader_labels'),
pre_reader_points: this._t('child.editor.pre_reader_points'),
debug: this._t('child.editor.show_debug'),
};
return labels[entry.name] ?? entry.name;
@@ -4754,6 +4891,7 @@ class TaskMateChildCardEditor extends LitElement {
time_category: this._t('child.editor.time_category_helper'),
due_days_mode: this._t('child.editor.due_days_helper'),
recurrence_done_mode: this._t('child.editor.recurrence_helper'),
dependency_mode: this._t('child.editor.dependency_helper'),
elapsed_time_mode: this._t('child.editor.elapsed_helper'),
};
return helpers[entry.name] ?? '';
@@ -4769,11 +4907,13 @@ class TaskMateChildCardEditor extends LitElement {
card_design: this.config.card_design || 'global',
due_days_mode: this.config.due_days_mode || 'hide',
recurrence_done_mode: this.config.recurrence_done_mode || 'dim',
dependency_mode: this.config.dependency_mode || 'hide',
elapsed_time_mode: this.config.elapsed_time_mode || 'dim',
show_countdown: this.config.show_countdown !== false,
show_description: this.config.show_description === true,
pre_reader: this.config.pre_reader === true,
pre_reader_labels: this.config.pre_reader_labels === true,
pre_reader_points: this.config.pre_reader_points === true,
debug: this.config.debug === true,
};