From f0baf904ff9302fd9f4f570be47089f03d8c33c1 Mon Sep 17 00:00:00 2001 From: rajashidattapy Date: Thu, 20 Aug 2026 00:42:51 +0530 Subject: [PATCH 1/2] fix(web): clear the 84 type errors blocking check-types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `apps/web` extends a tsconfig with `noUncheckedIndexedAccess`, so index access and a few genuine defects had accumulated behind a gate that has never been green. Most of it traced to one expression: `useProject().selectedProject` was `normalizedProjects[0]`, typed `string | undefined` even though the array is never empty. Defaulting it to the same tag the array falls back to fixed ~20 errors across chat, add-document, app-experience, quick-note-card and the note modals in one line. The rest: - orbit-memory: type `ring` as `0 | 1` and RR/SPEED as tuples so ring lookups stay `number`, iterate INTEG with `.entries()`, guard the comet refs, and fall back to an empty icon when a node has no key - memory-graph `use-graph-data` and `graph-card`: iterate with `.entries()` and skip edges whose endpoints are missing instead of indexing blind - chat history buckets and user initials: optional access on fixed-shape arrays that TypeScript cannot narrow - `stores/chat`: drop the `msgA.content !== msgB.content` comparison — UIMessage keeps its text in `parts`, so both sides were always undefined — and type the conversations fallback so entries aren't `unknown` - `useResetOrganization`: `retry: 0`, since the object form of RetryOptions needs type/baseDelay/maxDelay - SyncLogoIcon: accept the `style` prop timeline-view already passes it One real bug surfaced on the way: the iOS-shortcut mutation in settings returned `res.key` from `authClient.apiKey.create()`, which resolves to `{ data, error }`. The key was always undefined, so the modal showed nothing and the clipboard copy had nothing to copy. It now unwraps the result the same way the Raycast mutation right below it does. --- apps/web/components/chat/index.tsx | 10 ++-- .../components/memory-graph/graph-card.tsx | 16 +++--- apps/web/components/orbit-memory.tsx | 57 +++++++++++-------- apps/web/components/settings/integrations.tsx | 5 +- apps/web/components/user-profile-menu.tsx | 6 +- apps/web/hooks/use-reset-organization.ts | 2 +- apps/web/stores/chat.ts | 5 +- apps/web/stores/index.ts | 2 +- .../memory-graph/src/hooks/use-graph-data.ts | 3 +- packages/ui/assets/icons.tsx | 11 +++- 10 files changed, 67 insertions(+), 50 deletions(-) diff --git a/apps/web/components/chat/index.tsx b/apps/web/components/chat/index.tsx index 70f01d062..1e29535d6 100644 --- a/apps/web/components/chat/index.tsx +++ b/apps/web/components/chat/index.tsx @@ -1612,11 +1612,11 @@ export function ChatSidebar({ ] for (const t of filtered) { const ts = new Date(t.updatedAt).getTime() - if (ts >= startOfToday) buckets[0].items.push(t) - else if (ts >= startOfToday - day) buckets[1].items.push(t) - else if (ts >= startOfToday - 7 * day) buckets[2].items.push(t) - else if (ts >= startOfToday - 30 * day) buckets[3].items.push(t) - else buckets[4].items.push(t) + if (ts >= startOfToday) buckets[0]?.items.push(t) + else if (ts >= startOfToday - day) buckets[1]?.items.push(t) + else if (ts >= startOfToday - 7 * day) buckets[2]?.items.push(t) + else if (ts >= startOfToday - 30 * day) buckets[3]?.items.push(t) + else buckets[4]?.items.push(t) } return buckets.filter((b) => b.items.length > 0) }, [threads, historySearch]) diff --git a/apps/web/components/memory-graph/graph-card.tsx b/apps/web/components/memory-graph/graph-card.tsx index de8bed3d2..083bb2862 100644 --- a/apps/web/components/memory-graph/graph-card.tsx +++ b/apps/web/components/memory-graph/graph-card.tsx @@ -67,15 +67,13 @@ export function StaticGraphPreview({ const result: { x1: number; y1: number; x2: number; y2: number }[] = [] const edgeCount = Math.min(nodes.length - 1, 20) for (let i = 0; i < edgeCount; i++) { - const a = Math.floor(rand() * nodes.length) - let b = Math.floor(rand() * nodes.length) - if (b === a) b = (a + 1) % nodes.length - result.push({ - x1: nodes[a]?.x, - y1: nodes[a]?.y, - x2: nodes[b]?.x, - y2: nodes[b]?.y, - }) + const aIdx = Math.floor(rand() * nodes.length) + let bIdx = Math.floor(rand() * nodes.length) + if (bIdx === aIdx) bIdx = (aIdx + 1) % nodes.length + const a = nodes[aIdx] + const b = nodes[bIdx] + if (!a || !b) continue + result.push({ x1: a.x, y1: a.y, x2: b.x, y2: b.y }) } return result }, [nodes]) diff --git a/apps/web/components/orbit-memory.tsx b/apps/web/components/orbit-memory.tsx index 757d2a6df..8923da13f 100644 --- a/apps/web/components/orbit-memory.tsx +++ b/apps/web/components/orbit-memory.tsx @@ -48,7 +48,7 @@ const INTEG: { key?: string label?: string sub?: string - ring: number + ring: 0 | 1 ang: number size: number dim: boolean @@ -76,8 +76,8 @@ const DW = 780 const DH = 1024 const CX = 390 const CY = 512 -const RR = [200, 335] -const SPEED = [1.0, 0.78] +const RR: [number, number] = [200, 335] +const SPEED: [number, number] = [1.0, 0.78] const DRAW: [number, number, number, string][] = [ [125, 0.28, 1.7, "#74a8f6"], [200, 0.18, 1.9, "#4389ff"], @@ -167,16 +167,15 @@ export default function OrbitMemory({ track.setAttribute("stroke-opacity", "0") dtrack.setAttribute("stroke-opacity", "0") for (let k = 0; k < NC; k++) { - cms[k].setAttribute("opacity", "0") - cmgs[k].setAttribute("opacity", "0") - oms[k].setAttribute("opacity", "0") - omgs[k].setAttribute("opacity", "0") + cms[k]?.setAttribute("opacity", "0") + cmgs[k]?.setAttribute("opacity", "0") + oms[k]?.setAttribute("opacity", "0") + omgs[k]?.setAttribute("opacity", "0") } } let angle = 0 const place = () => { - for (let i = 0; i < INTEG.length; i++) { - const it = INTEG[i] + for (const [i, it] of INTEG.entries()) { const a = ((it.ang + angle * SPEED[it.ring]) * Math.PI) / 180 const R = RR[it.ring] const ux = CX + R * Math.cos(a) @@ -199,8 +198,8 @@ export default function OrbitMemory({ const DISC = 700 const INN: number[] = [] const OUT: number[] = [] - for (let i = 0; i < INTEG.length; i++) { - ;(INTEG[i].ring === 0 ? INN : OUT).push(i) + for (const [i, it] of INTEG.entries()) { + ;(it.ring === 0 ? INN : OUT).push(i) } let phase = "orbit" let pT0 = 0 @@ -259,12 +258,16 @@ export default function OrbitMemory({ const ay = pyc - uy * HL const bx = pxc + ux * HL const by = pyc + uy * HL - setLine(gr[k], ax, ay, bx, by) - setLine(cr[k], ax, ay, bx, by) - setLine(gl[k], ax, ay, bx, by) + const grad = gr[k] + const comet = cr[k] + const glow = gl[k] + if (!grad || !comet || !glow) continue + setLine(grad, ax, ay, bx, by) + setLine(comet, ax, ay, bx, by) + setLine(glow, ax, ay, bx, by) const o = Math.sin(f * Math.PI) * P - cr[k].setAttribute("opacity", o.toFixed(2)) - gl[k].setAttribute("opacity", (0.6 * o).toFixed(2)) + comet.setAttribute("opacity", o.toFixed(2)) + glow.setAttribute("opacity", (0.6 * o).toFixed(2)) } } const frame = (now: number) => { @@ -276,13 +279,13 @@ export default function OrbitMemory({ if (phase === "orbit" && pt > ORBIT) { phase = "receive" pT0 = now - src = INN[(ci * 5) % INN.length] - dst = OUT[(ci * 7) % OUT.length] + src = INN[(ci * 5) % INN.length] ?? 0 + dst = OUT[(ci * 7) % OUT.length] ?? 0 ci++ nodes[src]?.classList.add("sm-active") stage.style.setProperty( "--cm", - `rgb(${ACCENT[INTEG[src].key!] || "91,157,255"})`, + `rgb(${ACCENT[INTEG[src]?.key ?? ""] || "91,157,255"})`, ) } else if (phase === "receive" && pt > RECV) { phase = "send" @@ -318,8 +321,8 @@ export default function OrbitMemory({ seg( now, track, - INTEG[src]._x!, - INTEG[src]._y!, + INTEG[src]?._x ?? 0, + INTEG[src]?._y ?? 0, ps, grads, cms, @@ -330,8 +333,8 @@ export default function OrbitMemory({ seg( now, dtrack, - INTEG[dst]._x!, - INTEG[dst]._y!, + INTEG[dst]?._x ?? 0, + INTEG[dst]?._y ?? 0, pd, ogrs, oms, @@ -508,7 +511,9 @@ export default function OrbitMemory({ {it.sub} @@ -518,7 +523,9 @@ export default function OrbitMemory({ )} diff --git a/apps/web/components/settings/integrations.tsx b/apps/web/components/settings/integrations.tsx index 355a51e12..77df9a0c2 100644 --- a/apps/web/components/settings/integrations.tsx +++ b/apps/web/components/settings/integrations.tsx @@ -152,7 +152,10 @@ export default function Integrations() { name: `ios-${generateId().slice(0, 8)}`, prefix: `sm_${org?.id}_`, }) - return res.key + if (res.error) + throw new Error(res.error.message ?? "Failed to create API key") + if (!res.data?.key) throw new Error("API key missing from response") + return res.data.key }, onSuccess: (key) => { setApiKey(key) diff --git a/apps/web/components/user-profile-menu.tsx b/apps/web/components/user-profile-menu.tsx index ea034d733..1854cde8a 100644 --- a/apps/web/components/user-profile-menu.tsx +++ b/apps/web/components/user-profile-menu.tsx @@ -79,9 +79,11 @@ export function UserProfileMenu({ const initials = (() => { if (user.name) { const parts = user.name.trim().split(/\s+/) + const first = parts[0] ?? "" + const last = parts[parts.length - 1] ?? "" return parts.length >= 2 - ? `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase() - : parts[0].slice(0, 2).toUpperCase() + ? `${first.slice(0, 1)}${last.slice(0, 1)}`.toUpperCase() + : first.slice(0, 2).toUpperCase() } if (user.email) return user.email.slice(0, 2).toUpperCase() return "SM" diff --git a/apps/web/hooks/use-reset-organization.ts b/apps/web/hooks/use-reset-organization.ts index 4c71580fb..f920d55ee 100644 --- a/apps/web/hooks/use-reset-organization.ts +++ b/apps/web/hooks/use-reset-organization.ts @@ -11,7 +11,7 @@ export function useResetOrganization() { mutationFn: async (body: { confirmation: string }) => { const res = await $fetch("@post/settings/reset", { body, - retry: { attempts: 0 }, + retry: 0, }) if (res.error) { const e = res.error as Record diff --git a/apps/web/stores/chat.ts b/apps/web/stores/chat.ts index 456217b7f..f815bb826 100644 --- a/apps/web/stores/chat.ts +++ b/apps/web/stores/chat.ts @@ -27,8 +27,6 @@ export function areUIMessageArraysEqual( return false } - if (msgA.content !== msgB.content) return false - if (JSON.stringify(msgA.parts) !== JSON.stringify(msgB.parts)) { return false } @@ -202,7 +200,8 @@ export function usePersistentChat() { ) const conversations: ConversationSummary[] = (() => { - const convs = projectState?.conversations ?? {} + const convs: Record = + projectState?.conversations ?? {} return Object.entries(convs).map(([id, rec]) => ({ id, title: rec.title, diff --git a/apps/web/stores/index.ts b/apps/web/stores/index.ts index e13cccef6..85dd569a0 100644 --- a/apps/web/stores/index.ts +++ b/apps/web/stores/index.ts @@ -21,7 +21,7 @@ export function useProject() { const normalizedProjects = selectedProjects.length === 0 ? [defaultTag] : selectedProjects - const selectedProject = normalizedProjects[0] + const selectedProject = normalizedProjects[0] ?? defaultTag const effectiveContainerTags = normalizedProjects diff --git a/packages/memory-graph/src/hooks/use-graph-data.ts b/packages/memory-graph/src/hooks/use-graph-data.ts index aed2d6f01..71fd3a29f 100644 --- a/packages/memory-graph/src/hooks/use-graph-data.ts +++ b/packages/memory-graph/src/hooks/use-graph-data.ts @@ -507,8 +507,7 @@ export function useGraphData( // Golden angle (~137.5 deg) produces optimal packing in a spiral const goldenAngle = Math.PI * (3 - Math.sqrt(5)) - for (let docIdx = 0; docIdx < docCount; docIdx++) { - const doc = documents[docIdx] + for (const [docIdx, doc] of documents.entries()) { const docCluster = getDocumentClusterAssignment(doc, clusterAssignments) const angle = docIdx * goldenAngle const radius = spiralScale * Math.sqrt((docIdx + 1) / docCount) diff --git a/packages/ui/assets/icons.tsx b/packages/ui/assets/icons.tsx index 7aaece871..035328661 100644 --- a/packages/ui/assets/icons.tsx +++ b/packages/ui/assets/icons.tsx @@ -1,3 +1,5 @@ +import type { CSSProperties } from "react" + export const OneDrive = ({ className }: { className?: string }) => ( ( ) -export const SyncLogoIcon = ({ className }: { className?: string }) => { +export const SyncLogoIcon = ({ + className, + style, +}: { + className?: string + style?: CSSProperties +}) => { return ( { fill="none" xmlns="http://www.w3.org/2000/svg" className={className} + style={style} > Sync Logo From 1a5a57aa70fc8889f33710f4ed0b020bc90c698d Mon Sep 17 00:00:00 2001 From: rajashidattapy Date: Thu, 20 Aug 2026 01:08:19 +0530 Subject: [PATCH 2/2] fix(integrations): improve error handling for API key creation --- apps/web/components/settings/integrations.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/components/settings/integrations.tsx b/apps/web/components/settings/integrations.tsx index 77df9a0c2..a04db3838 100644 --- a/apps/web/components/settings/integrations.tsx +++ b/apps/web/components/settings/integrations.tsx @@ -153,7 +153,7 @@ export default function Integrations() { prefix: `sm_${org?.id}_`, }) if (res.error) - throw new Error(res.error.message ?? "Failed to create API key") + throw new Error("Failed to create API key", { cause: res.error }) if (!res.data?.key) throw new Error("API key missing from response") return res.data.key }, @@ -185,7 +185,7 @@ export default function Integrations() { prefix: `sm_${org.id}_`, }) if (res.error) - throw new Error(res.error.message ?? "Failed to create API key") + throw new Error("Failed to create API key", { cause: res.error }) if (!res.data?.key) throw new Error("API key missing from response") return res.data.key },