Implement quick-add registry (task 21)

- Add url field to QuickAddAction; make action optional
- Add getQuickAdds() + SerializedQuickAddItem to registry so quick-add
  data can cross the server→client RSC boundary without serializing fns
- Update calendar/lists/notes manifests with navigation URLs
- QuickAddProvider wraps root layout: holds sheet/palette open state and
  global cmd/ctrl+k shortcut
- QuickAddSheet: bottom drawer on mobile, right-side popover on desktop,
  actions grouped by module
- CommandPalette: cmdk-powered modal with fuzzy filter and full keyboard
  nav (arrows + enter + esc)
- QuickAddFab: client button replacing the plain + stub in the dashboard
- Add .claude/** to ESLint ignores to prevent stale worktree artifacts
  from failing lint

All 4 E2E specs pass; typecheck, lint, and build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ginnoir
2026-05-06 13:44:33 -05:00
co-authored by Claude Sonnet 4.6
parent b9d3cce73a
commit 1133fa8aac
17 changed files with 845 additions and 25 deletions
+26 -1
View File
@@ -1,4 +1,4 @@
import type { ModuleManifest, EntityTypeRegistration, DashboardWidget } from "./module";
import type { ModuleManifest, EntityTypeRegistration, DashboardWidget, QuickAddAction } from "./module";
const modules = new Map<string, ModuleManifest>();
const entityTypes = new Map<string, EntityTypeRegistration>();
@@ -29,3 +29,28 @@ export function getEntityType(type: string): EntityTypeRegistration | undefined
export function getWidget(id: string): DashboardWidget | undefined {
return widgets.get(id);
}
export type QuickAddItem = QuickAddAction & { moduleId: string; moduleName: string };
/** Serializable subset safe to pass from server components to client components. */
export type SerializedQuickAddItem = {
id: string;
label: string;
icon?: string;
url: string;
moduleId: string;
moduleName: string;
};
export function getQuickAdds(): SerializedQuickAddItem[] {
return [...modules.values()].flatMap((manifest) =>
(manifest.quickAdds ?? []).map(({ id, label, icon, url }) => ({
id,
label,
icon,
url,
moduleId: manifest.id,
moduleName: manifest.name,
})),
);
}