Skip to content

Commit 461b512

Browse files
committed
character limit
1 parent 7394f2b commit 461b512

File tree

8 files changed

+264
-134
lines changed

8 files changed

+264
-134
lines changed

app/(chat)/api/chat/schema.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
import { z } from 'zod/v3';
22

33
const textPartSchema = z.object({
4-
text: z.string().min(1).max(2000),
4+
text: z.string().min(1).max(10000),
55
type: z.enum(['text']),
66
});
77

88
const filePartSchema = z.object({
99
url: z.string().url(),
1010
name: z.string().min(1).max(2000),
11-
mediaType: z.enum([
12-
'image/png',
13-
'image/jpg',
14-
'image/jpeg',
15-
'text/plain',
16-
]),
11+
mediaType: z.enum(['image/png', 'image/jpg', 'image/jpeg', 'text/plain']),
1712
type: z.enum(['file']),
1813
});
1914

@@ -23,7 +18,7 @@ export const postRequestBodySchema = z.object({
2318
id: z.string().uuid(),
2419
createdAt: z.coerce.date(),
2520
role: z.enum(['user']),
26-
content: z.string().min(1).max(2000),
21+
content: z.string().min(1).max(10000),
2722
parts: z.array(z.union([textPartSchema, filePartSchema])),
2823
}),
2924
selectedChatModel: z.enum(['chat-model', 'chat-model-reasoning']),

bun.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"@radix-ui/react-switch": "^1.2.6",
3737
"@radix-ui/react-tooltip": "^1.2.6",
3838
"@radix-ui/react-visually-hidden": "^1.2.2",
39-
"@supermemory/tools": "1.3.2",
39+
"@supermemory/tools": "^1.3.2",
4040
"@tiptap/extension-mention": "^3.6.6",
4141
"@tiptap/extension-placeholder": "^3.6.6",
4242
"@tiptap/pm": "^3.6.6",

lib/ui/memory-graph/graph-webgl-canvas.tsx

Lines changed: 91 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Application, extend } from "@pixi/react";
44
import { Container as PixiContainer, Graphics as PixiGraphics } from "pixi.js";
55
import { memo, useCallback, useEffect, useMemo, useRef } from "react";
66
import { colors } from "./constants";
7+
import { getThemeColors } from "./theme-colors";
78
import type { GraphCanvasProps, MemoryEntry } from "./types";
89

910
// Register Pixi Graphics and Container so they can be used as JSX elements
@@ -32,7 +33,10 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
3233
onTouchMove,
3334
onTouchEnd,
3435
draggingNodeId,
36+
themeColors,
3537
}) => {
38+
// Use theme colors or fallback to dark theme
39+
const colorPalette = themeColors || colors;
3640
const containerRef = useRef<HTMLDivElement>(null);
3741
const isPanningRef = useRef(false);
3842
const currentHoveredRef = useRef<string | null>(null);
@@ -58,32 +62,40 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
5862
// ---------- Zoom bucket (reduces redraw frequency) ----------
5963
const zoomBucket = useMemo(() => Math.round(zoom * 4) / 4, [zoom]);
6064

61-
// Redraw layers only when their data changes ----------------------
62-
useEffect(() => {
63-
if (gridG.current) drawGrid(gridG.current);
64-
}, [panX, panY, zoom, width, height]);
65-
66-
useEffect(() => {
67-
if (edgesG.current) drawEdges(edgesG.current);
68-
}, [edgesG.current, edges, nodes, zoomBucket]);
69-
70-
useEffect(() => {
71-
if (docsG.current) drawDocuments(docsG.current);
72-
}, [docsG.current, nodes, zoomBucket]);
73-
74-
useEffect(() => {
75-
if (memsG.current) drawMemories(memsG.current);
76-
}, [memsG.current, nodes, zoomBucket]);
77-
78-
// Apply pan & zoom via world transform instead of geometry rebuilds
79-
useEffect(() => {
80-
if (worldContainerRef.current) {
81-
worldContainerRef.current.position.set(panX, panY);
82-
worldContainerRef.current.scale.set(zoom);
65+
/* ---------- Color parsing ---------- */
66+
const toHexAlpha = (input: string): { hex: number; alpha: number } => {
67+
if (!input) return { hex: 0xffffff, alpha: 1 };
68+
const str = input.trim().toLowerCase();
69+
// rgba() or rgb()
70+
const rgbaMatch = str
71+
.replace(/\s+/g, "")
72+
.match(/rgba?\((\d+),(\d+),(\d+)(?:,(\d*\.?\d+))?\)/i);
73+
if (rgbaMatch) {
74+
const r = Number.parseInt(rgbaMatch[1] || "0");
75+
const g = Number.parseInt(rgbaMatch[2] || "0");
76+
const b = Number.parseInt(rgbaMatch[3] || "0");
77+
const a =
78+
rgbaMatch[4] !== undefined ? Number.parseFloat(rgbaMatch[4]) : 1;
79+
return { hex: (r << 16) + (g << 8) + b, alpha: a };
8380
}
84-
}, [panX, panY, zoom]);
85-
86-
// No bitmap caching – nothing to clean up
81+
// #rrggbb or #rrggbbaa
82+
if (str.startsWith("#")) {
83+
const hexBody = str.slice(1);
84+
if (hexBody.length === 6) {
85+
return { hex: Number.parseInt(hexBody, 16), alpha: 1 };
86+
}
87+
if (hexBody.length === 8) {
88+
const rgb = Number.parseInt(hexBody.slice(0, 6), 16);
89+
const aByte = Number.parseInt(hexBody.slice(6, 8), 16);
90+
return { hex: rgb, alpha: aByte / 255 };
91+
}
92+
}
93+
// 0xRRGGBB
94+
if (str.startsWith("0x")) {
95+
return { hex: Number.parseInt(str, 16), alpha: 1 };
96+
}
97+
return { hex: 0xffffff, alpha: 1 };
98+
};
8799

88100
/* ---------- Helpers ---------- */
89101
const getNodeAtPosition = useCallback(
@@ -128,8 +140,9 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
128140
(g: PixiGraphics) => {
129141
g.clear();
130142

131-
const gridColor = 0x94a3b8; // rgb(148,163,184)
132-
const gridAlpha = 0.03;
143+
// Use theme-aware grid color
144+
const gridColorRGBA = colorPalette.grid?.line || "rgba(148, 163, 184, 0.03)";
145+
const { hex: gridColor, alpha: gridAlpha } = toHexAlpha(gridColorRGBA);
133146
const gridSpacing = 100 * zoom;
134147

135148
// panning offsets
@@ -153,44 +166,9 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
153166
// Stroke to render grid lines
154167
g.stroke();
155168
},
156-
[panX, panY, zoom, width, height],
169+
[panX, panY, zoom, width, height, colorPalette],
157170
);
158171

159-
/* ---------- Color parsing ---------- */
160-
const toHexAlpha = (input: string): { hex: number; alpha: number } => {
161-
if (!input) return { hex: 0xffffff, alpha: 1 };
162-
const str = input.trim().toLowerCase();
163-
// rgba() or rgb()
164-
const rgbaMatch = str
165-
.replace(/\s+/g, "")
166-
.match(/rgba?\((\d+),(\d+),(\d+)(?:,(\d*\.?\d+))?\)/i);
167-
if (rgbaMatch) {
168-
const r = Number.parseInt(rgbaMatch[1] || "0");
169-
const g = Number.parseInt(rgbaMatch[2] || "0");
170-
const b = Number.parseInt(rgbaMatch[3] || "0");
171-
const a =
172-
rgbaMatch[4] !== undefined ? Number.parseFloat(rgbaMatch[4]) : 1;
173-
return { hex: (r << 16) + (g << 8) + b, alpha: a };
174-
}
175-
// #rrggbb or #rrggbbaa
176-
if (str.startsWith("#")) {
177-
const hexBody = str.slice(1);
178-
if (hexBody.length === 6) {
179-
return { hex: Number.parseInt(hexBody, 16), alpha: 1 };
180-
}
181-
if (hexBody.length === 8) {
182-
const rgb = Number.parseInt(hexBody.slice(0, 6), 16);
183-
const aByte = Number.parseInt(hexBody.slice(6, 8), 16);
184-
return { hex: rgb, alpha: aByte / 255 };
185-
}
186-
}
187-
// 0xRRGGBB
188-
if (str.startsWith("0x")) {
189-
return { hex: Number.parseInt(str, 16), alpha: 1 };
190-
}
191-
return { hex: 0xffffff, alpha: 1 };
192-
};
193-
194172
const drawDocuments = useCallback(
195173
(g: PixiGraphics) => {
196174
g.clear();
@@ -208,16 +186,16 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
208186

209187
// Choose colors similar to canvas version
210188
const fill = node.isDragging
211-
? colors.document.accent
189+
? colorPalette.document.accent
212190
: node.isHovered
213-
? colors.document.secondary
214-
: colors.document.primary;
191+
? colorPalette.document.secondary
192+
: colorPalette.document.primary;
215193

216194
const strokeCol = node.isDragging
217-
? colors.document.glow
195+
? colorPalette.document.glow
218196
: node.isHovered
219-
? colors.document.accent
220-
: colors.document.border;
197+
? colorPalette.document.accent
198+
: colorPalette.document.border;
221199

222200
const { hex: fillHex, alpha: fillAlpha } = toHexAlpha(fill);
223201
const { hex: strokeHex, alpha: strokeAlpha } = toHexAlpha(strokeCol);
@@ -255,7 +233,7 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
255233
}
256234
});
257235
},
258-
[nodes, zoom],
236+
[nodes, zoom, colorPalette],
259237
);
260238

261239
/* ---------- Memories layer ---------- */
@@ -290,27 +268,27 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
290268
Date.now() - 1000 * 60 * 60 * 24;
291269

292270
// colours
293-
let fillColor = colors.memory.primary;
294-
let borderColor = colors.memory.border;
295-
let glowColor = colors.memory.glow;
271+
let fillColor = colorPalette.memory.primary;
272+
let borderColor = colorPalette.memory.border;
273+
let glowColor = colorPalette.memory.glow;
296274

297275
if (isForgotten) {
298-
fillColor = colors.status.forgotten;
276+
fillColor = colorPalette.status.forgotten;
299277
borderColor = "rgba(220,38,38,0.3)";
300278
glowColor = "rgba(220,38,38,0.2)";
301279
} else if (expiringSoon) {
302-
borderColor = colors.status.expiring;
303-
glowColor = colors.accent.amber;
280+
borderColor = colorPalette.status.expiring;
281+
glowColor = colorPalette.accent.amber;
304282
} else if (isNew) {
305-
borderColor = colors.status.new;
306-
glowColor = colors.accent.emerald;
283+
borderColor = colorPalette.status.new;
284+
glowColor = colorPalette.accent.emerald;
307285
}
308286

309287
if (node.isDragging) {
310-
fillColor = colors.memory.accent;
288+
fillColor = colorPalette.memory.accent;
311289
borderColor = glowColor;
312290
} else if (node.isHovered) {
313-
fillColor = colors.memory.secondary;
291+
fillColor = colorPalette.memory.secondary;
314292
}
315293

316294
const { hex: fillHex, alpha: fillAlpha } = toHexAlpha(fillColor);
@@ -360,7 +338,7 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
360338
g.stroke();
361339
} else if (isNew) {
362340
const { hex: dotHex, alpha: dotAlpha } = toHexAlpha(
363-
colors.status.new,
341+
colorPalette.status.new,
364342
);
365343
// Dot scales with node (GraphCanvas behaviour)
366344
const dotRadius = Math.max(2, nodeSize * 0.15);
@@ -374,7 +352,7 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
374352
}
375353
});
376354
},
377-
[nodes, zoom],
355+
[nodes, zoom, colorPalette],
378356
);
379357

380358
/* ---------- Edges layer ---------- */
@@ -489,21 +467,21 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
489467
let lineWidth = Math.max(1, edge.visualProps?.thickness ?? 1);
490468
// Use opacity exactly as provided to match GraphCanvas behaviour
491469
let opacity = edge.visualProps.opacity;
492-
let col = edge.color || colors.connection.weak;
470+
let col = edge.color || colorPalette.connection.weak;
493471

494472
if (edge.edgeType === "doc-memory") {
495473
lineWidth = 1;
496474
opacity = 0.9;
497-
col = colors.connection.memory;
475+
col = colorPalette.connection.memory;
498476

499477
if (useSimplified && opacity < 0.3) return;
500478
} else if (edge.edgeType === "doc-doc") {
501479
opacity = Math.max(0, edge.similarity * 0.5);
502480
lineWidth = Math.max(1, edge.similarity * 2);
503-
col = colors.connection.medium;
504-
if (edge.similarity > 0.85) col = colors.connection.strong;
481+
col = colorPalette.connection.medium;
482+
if (edge.similarity > 0.85) col = colorPalette.connection.strong;
505483
} else if (edge.edgeType === "version") {
506-
col = edge.color || colors.relations.updates;
484+
col = edge.color || colorPalette.relations.updates;
507485
opacity = 0.8;
508486
lineWidth = 2;
509487
}
@@ -584,7 +562,7 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
584562
}
585563
});
586564
},
587-
[edges, nodes, zoom, width, drawDashedQuadratic],
565+
[edges, nodes, zoom, width, drawDashedQuadratic, colorPalette],
588566
);
589567

590568
/* ---------- pointer handlers (unchanged) ---------- */
@@ -727,6 +705,31 @@ export const GraphWebGLCanvas = memo<GraphCanvasProps>(
727705
};
728706
}, []);
729707

708+
// Redraw layers only when their data changes ----------------------
709+
useEffect(() => {
710+
if (gridG.current) drawGrid(gridG.current);
711+
}, [panX, panY, zoom, width, height, drawGrid]);
712+
713+
useEffect(() => {
714+
if (edgesG.current) drawEdges(edgesG.current);
715+
}, [edges, nodes, zoomBucket, drawEdges]);
716+
717+
useEffect(() => {
718+
if (docsG.current) drawDocuments(docsG.current);
719+
}, [nodes, zoomBucket, drawDocuments]);
720+
721+
useEffect(() => {
722+
if (memsG.current) drawMemories(memsG.current);
723+
}, [nodes, zoomBucket, drawMemories]);
724+
725+
// Apply pan & zoom via world transform instead of geometry rebuilds
726+
useEffect(() => {
727+
if (worldContainerRef.current) {
728+
worldContainerRef.current.position.set(panX, panY);
729+
worldContainerRef.current.scale.set(zoom);
730+
}
731+
}, [panX, panY, zoom]);
732+
730733
return (
731734
<div
732735
className="absolute inset-0"

0 commit comments

Comments
 (0)