fix(packages): modal keydown handlers, $derived.by usage, UserData fields

Eight more package-level type errors that all came from the same
small handful of patterns.

Modal escape-key handlers calling click-style functions
  Four modals (AuthGateModal, GuestWelcomeModal, ConfirmationPopover,
  ShareModal) had `onkeydown={(e) => { if (e.key === 'Escape')
  handleBackdropClick(); }}` — but handleBackdropClick took a MouseEvent
  parameter, so the no-arg call failed with "Expected 1 arguments,
  got 0". Fix: route the keyboard escape path through the right
  no-arg helper (`onClose` / `handleClose` / `handleContinueAsGuest`)
  or pass the keyboard event through with a cast for the popover
  trigger that genuinely shares its handler with the click path.

WallpaperModal $derived
  `currentLayout` and `currentBackground` were declared with
  `$derived(() => {...})` — passing a function expression. The
  variant that takes a thunk is `$derived.by(...)`; plain `$derived`
  expects a single value expression. Result: the variables held the
  arrow function itself, the call sites had to invoke them as
  `currentLayout()`, and TS rejected the function value where Layout
  was expected. Switch to `$derived.by`, drop the call-site parens.

TagList.svelte
  Generic param was named `Tag` in the handler signature
  (`tag: Tag`) but the imported type was aliased as `TagType`. Tag
  was undefined → "Cannot find name 'Tag'". Renamed to TagType.

TagStrip.svelte
  `dropAccepts?: string[]` is too wide for `passiveDropZone`'s
  `accepts: DragType[]`. Narrowed the prop type to `DragType[]`
  and added the missing import.

shared-auth/types: UserData.{name,image}?
  Two more optional fields for the public user shape. Both come
  from the JWT user_metadata claim when the user has filled in
  their profile during onboarding. Without these the
  ProfileStep.svelte onboarding component couldn't read
  `authStore.user?.name` / `?.image` without `as any`. Added
  alongside `twoFactorEnabled` from the previous shared-auth
  commit; same Optional rationale (guest tokens omit the claim).

Net: -10 type errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-09 20:24:05 +02:00
parent ab24db36dd
commit c31ce4448f
8 changed files with 26 additions and 13 deletions

View file

@ -207,7 +207,7 @@
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
onclick={handleBackdropClick}
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
if (e.key === 'Escape') onClose();
}}
role="presentation"
tabindex="-1"

View file

@ -242,7 +242,7 @@
class="modal-backdrop"
onclick={handleBackdropClick}
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
if (e.key === 'Escape') handleContinueAsGuest();
}}
role="dialog"
aria-modal="true"

View file

@ -59,6 +59,13 @@ export interface UserData {
* button on this should default to `false` when undefined.
*/
twoFactorEnabled?: boolean;
/**
* Display name + avatar URL, populated from the JWT's user_metadata
* claim when the user has filled in their profile. Both are
* optional because the onboarding flow lets users skip this step.
*/
name?: string;
image?: string;
}
/**

View file

@ -222,7 +222,7 @@
bind:this={triggerRef}
onclick={handleTriggerClick}
onkeydown={(e) => {
if (e.key === 'Enter' || e.key === ' ') handleTriggerClick();
if (e.key === 'Enter' || e.key === ' ') handleTriggerClick(e as unknown as MouseEvent);
}}
role="button"
tabindex="0"

View file

@ -29,7 +29,7 @@
return tag.color ?? tag.style?.color ?? DEFAULT_TAG_COLOR;
}
function handleKeyDown(e: KeyboardEvent, tag: Tag, action: 'click' | 'edit' | 'delete') {
function handleKeyDown(e: KeyboardEvent, tag: TagType, action: 'click' | 'edit' | 'delete') {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (action === 'click' && onClick) onClick(tag);

View file

@ -3,7 +3,7 @@
import { Tag, Plus, X } from '@mana/shared-icons';
import { dragSource } from '../dnd/drag-source';
import { passiveDropZone } from '../dnd/passive-drop';
import type { DragPayload } from '../dnd/types';
import type { DragPayload, DragType } from '../dnd/types';
interface TagItem {
id: string;
@ -23,7 +23,7 @@
/** Called when an item (task, card, etc.) is dropped on a tag pill */
onTagDrop?: (tagId: string, payload: DragPayload) => void;
/** Drag types accepted for drop-on-tag (default: ['task']) */
dropAccepts?: string[];
dropAccepts?: DragType[];
/** Link for "Tags verwalten" pill */
managementHref?: string;
/** Loading state */

View file

@ -106,7 +106,7 @@
style="z-index: 9990;"
onclick={handleBackdropClick}
onkeydown={(e) => {
if (e.key === 'Escape') handleBackdropClick();
if (e.key === 'Escape') handleClose();
}}
role="presentation"
tabindex="-1"

View file

@ -48,7 +48,13 @@
const currentDevices = $derived(devicesByCategory[selectedCategory] || []);
const currentLayout = $derived<Layout>(() => {
// `$derived.by(...)` is the variant that takes a thunk and runs it
// inside the derivation. Plain `$derived(expr)` only takes a single
// expression — passing an arrow function there made `currentLayout`
// itself a function value, which is why the call sites below had to
// invoke it as `currentLayout()`. Both call sites now read it as a
// plain value.
const currentLayout = $derived.by<Layout>(() => {
if (layoutType === 'center') {
return { type: 'center', scale: layoutScale };
} else if (layoutType === 'corner') {
@ -58,7 +64,7 @@
}
});
const currentBackground = $derived<Background>(() => {
const currentBackground = $derived.by<Background>(() => {
if (backgroundType === 'solid') {
return { type: 'solid', color: solidColor };
} else {
@ -83,8 +89,8 @@
{ type: 'dataUrl', data: imageDataUrl },
{
device: selectedDeviceId,
layout: currentLayout(),
background: currentBackground(),
layout: currentLayout,
background: currentBackground,
}
);
previewUrl = url;
@ -102,8 +108,8 @@
{ type: 'dataUrl', data: imageDataUrl },
{
device: selectedDeviceId,
layout: currentLayout(),
background: currentBackground(),
layout: currentLayout,
background: currentBackground,
format: 'png',
}
);