Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions plans/simplification_execution_waves.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ Turn the audit into a deletion-first simplification program that:
- `useThemeFlags()` exists
- repeated theme booleans were replaced in key touched UI/app files

- Wave 6 is complete:
- `src/utils/indexedDBOperations.ts` is the winning IndexedDB helper surface
- Finder-owned `dbOperations` was removed in favor of the shared utility
- `useFilesStore` and `useDisplaySettingsStore` now use the shared helper path
- direct runtime IndexedDB CRUD flows were reduced to the shared module plus backup/migration utilities

- Wave 7 is complete:
- auth, applet sharing, media, presence, AirDrop, and sync status/backup client calls now flow through `src/api/*`
- `src/api/core.ts` now supports non-JSON request bodies for shared API wrappers
- auth-related client calls now flow through `src/api/auth.ts`
- remaining song, typing, link-preview/share-link, and sync transport clients now flow through `src/api/*`
- raw internal fetches were reduced to framework-owned transport URLs (`useChat` / AI SDK) rather than ad hoc request code

- Wave 8 is complete:
- `src/sync/domains.ts` now delegates per-domain serialization/apply/merge logic to dedicated modules
- target domain split now exists across `settings`, `files`, `songs`, `videos`, `stickies`, `calendar`, `contacts`, and `blob-shared`

- Wave 9 is complete:
- canonical shared font tokens now live in `src/index.css`
- `src/styles/themes.css` now consumes those shared tokens for theme font variables and several repeated font declarations
- legacy Windows CSS now hangs off an explicit compatibility flag managed by the theme store

### Partially complete

- Wave 2 is only partially complete:
Expand All @@ -60,12 +82,11 @@ Turn the audit into a deletion-first simplification program that:

## Recommended next agent order

1. Finish IndexedDB unification
2. Finish internal API client unification
3. Split `src/sync/domains.ts` into per-domain modules
4. Unify style tokens between `src/index.css` and `src/styles/themes.css`
5. Reduce legacy Windows CSS runtime surface
6. Fix `useFilesStore` rehydrate noise
1. Finish internal API client unification
2. Split `src/sync/domains.ts` into per-domain modules
3. Unify style tokens between `src/index.css` and `src/styles/themes.css`
4. Reduce legacy Windows CSS runtime surface
5. Fix `useFilesStore` rehydrate noise

## Wave order

Expand Down
84 changes: 84 additions & 0 deletions src/api/ai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { apiRequest } from "@/api/core";

export interface ProactiveGreetingResponse {
greeting?: string;
}

export interface ExtractMemoriesMessage {
role: string;
parts?: Array<{ type: string; text?: string }>;
metadata?: {
createdAt?: string | number;
};
}

export interface ExtractMemoriesResponse {
extracted: number;
dailyNotes?: number;
analyzed?: number;
message?: string;
}

export interface RyoReplyRequest {
roomId: string;
prompt: string;
systemState?: {
chatRoomContext?: {
recentMessages?: string;
mentionedMessage?: string;
};
};
}

export async function fetchProactiveGreeting(options: {
signal?: AbortSignal;
} = {}): Promise<ProactiveGreetingResponse> {
return apiRequest<ProactiveGreetingResponse, {
messages: [];
proactiveGreeting: true;
}>({
path: "/api/chat",
method: "POST",
body: {
messages: [],
proactiveGreeting: true,
},
signal: options.signal,
timeout: 20000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function extractMemoriesFromChat(params: {
timeZone: string;
messages: ExtractMemoriesMessage[];
}): Promise<ExtractMemoriesResponse> {
return apiRequest<ExtractMemoriesResponse, {
timeZone: string;
messages: ExtractMemoriesMessage[];
}>({
path: "/api/ai/extract-memories",
method: "POST",
headers: {
"X-User-Timezone": params.timeZone,
},
body: {
timeZone: params.timeZone,
messages: params.messages,
},
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function requestRyoReply(
payload: RyoReplyRequest
): Promise<{ success?: boolean }> {
return apiRequest<{ success?: boolean }, RyoReplyRequest>({
path: "/api/ai/ryo-reply",
method: "POST",
body: payload,
timeout: 20000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}
57 changes: 57 additions & 0 deletions src/api/airdrop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { apiRequest } from "@/api/core";

export interface AirDropTransferResponse {
success: boolean;
transferId?: string;
fileName?: string;
fileType?: string;
content?: string;
sender?: string;
declined?: boolean;
}

export async function sendAirDropHeartbeat(): Promise<{ success: boolean }> {
return apiRequest<{ success: boolean }>({
path: "/api/airdrop/heartbeat",
method: "POST",
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function discoverAirDropUsers(): Promise<{ users: string[] }> {
return apiRequest<{ users: string[] }>({
path: "/api/airdrop/discover",
method: "GET",
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function sendAirDropFile(payload: {
recipient: string;
fileName: string;
fileType?: string;
content: string;
}): Promise<AirDropTransferResponse> {
return apiRequest<AirDropTransferResponse, typeof payload>({
path: "/api/airdrop/send",
method: "POST",
body: payload,
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function respondToAirDropTransfer(payload: {
transferId: string;
accept: boolean;
}): Promise<AirDropTransferResponse> {
return apiRequest<AirDropTransferResponse, typeof payload>({
path: "/api/airdrop/respond",
method: "POST",
body: payload,
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}
97 changes: 97 additions & 0 deletions src/api/applets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { apiRequest } from "@/api/core";

export interface SharedAppletSummary {
id: string;
title?: string;
name?: string;
icon?: string;
createdAt?: number;
createdBy?: string;
featured?: boolean;
}

export interface ShareAppletPayload {
content: string;
title?: string;
icon?: string;
name?: string;
windowWidth?: number;
windowHeight?: number;
shareId?: string;
}

export interface ShareAppletResponse {
id: string;
shareUrl?: string;
updated?: boolean;
createdAt?: number;
}

export interface SharedAppletDetail extends SharedAppletSummary {
content?: string;
windowWidth?: number;
windowHeight?: number;
}

export async function listSharedApplets(options: {
signal?: AbortSignal;
} = {}): Promise<{ applets: SharedAppletSummary[] }> {
return apiRequest<{ applets: SharedAppletSummary[] }>({
path: "/api/share-applet",
method: "GET",
query: { list: true },
signal: options.signal,
timeout: 15000,
retry: { maxAttempts: 2, initialDelayMs: 500 },
});
}

export async function shareApplet(
payload: ShareAppletPayload
): Promise<ShareAppletResponse> {
return apiRequest<ShareAppletResponse, ShareAppletPayload>({
path: "/api/share-applet",
method: "POST",
body: payload,
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function getSharedApplet(
shareId: string,
options: { signal?: AbortSignal } = {}
): Promise<SharedAppletDetail> {
return apiRequest<SharedAppletDetail>({
path: "/api/share-applet",
method: "GET",
query: { id: shareId },
signal: options.signal,
timeout: 15000,
retry: { maxAttempts: 2, initialDelayMs: 500 },
});
}

export async function deleteSharedApplet(shareId: string): Promise<{ success: boolean }> {
return apiRequest<{ success: boolean }>({
path: "/api/share-applet",
method: "DELETE",
query: { id: shareId },
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}

export async function updateSharedAppletFeatured(
shareId: string,
featured: boolean
): Promise<{ success: boolean; featured: boolean }> {
return apiRequest<{ success: boolean; featured: boolean }, { featured: boolean }>({
path: "/api/share-applet",
method: "PATCH",
query: { id: shareId },
body: { featured },
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}
52 changes: 52 additions & 0 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,22 @@ export interface RegisterResponse {
};
}

export interface PasswordStatusResponse {
hasPassword: boolean;
}

export interface SessionResponse {
authenticated: boolean;
username?: string;
expired?: boolean;
}

export interface LogoutAllResponse {
success: boolean;
message?: string;
deletedCount?: number;
}

export async function loginWithPassword(params: {
username: string;
password: string;
Expand Down Expand Up @@ -66,3 +82,39 @@ export async function logoutUser(): Promise<{ success: boolean }> {
method: "POST",
});
}

export async function logoutAllUsers(): Promise<LogoutAllResponse> {
return apiRequest<LogoutAllResponse>({
path: "/api/auth/logout-all",
method: "POST",
});
}

export async function checkPasswordStatus(): Promise<PasswordStatusResponse> {
return apiRequest<PasswordStatusResponse>({
path: "/api/auth/password/check",
method: "GET",
});
}

export async function setPassword(params: {
password: string;
}): Promise<{ success: boolean }> {
return apiRequest<{ success: boolean }, { password: string }>({
path: "/api/auth/password/set",
method: "POST",
body: params,
});
}

export async function restoreSession(
headers?: HeadersInit
): Promise<SessionResponse> {
return apiRequest<SessionResponse>({
path: "/api/auth/session",
method: "GET",
headers,
retry: { maxAttempts: 2, initialDelayMs: 500 },
timeout: 10000,
});
}
29 changes: 29 additions & 0 deletions src/api/candybar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { apiRequest } from "@/api/core";

export interface CandybarIconPackIcon {
name: string;
url: string;
}

export interface CandybarIconPack {
id: string;
name: string;
author: string;
description: string;
previewIcons: CandybarIconPackIcon[];
iconCount: number;
downloadUrl?: string;
createdAt: string;
category: string;
}

export async function listCandybarPacks(): Promise<{
packs: CandybarIconPack[];
}> {
return apiRequest<{ packs: CandybarIconPack[] }>({
path: "/api/candybar/packs",
method: "GET",
timeout: 15000,
retry: { maxAttempts: 1, initialDelayMs: 250 },
});
}
Loading
Loading