+server.ts RawHistoryFolder

1import { json, error } from '@sveltejs/kit';
2import type { RequestHandler } from './$types';
3import convex from '$lib/db/convex.server';
4import { DEFAULT_USER_PREFERENCES, type UserTextPreferences } from '$lib/types/text-formatting';
5import { validateSessionToken } from '$lib/api/session.server';
6import { sanitizeStyleData } from '$lib/validation/text-formatting';
7
8const isKitHttpError = (err: unknown): err is { status: number; body: unknown } => {
9	return typeof err === 'object' && err !== null && 'status' in err && 'body' in err;
10};
11
12// GET - Fetch current user's text preferences
13export const GET: RequestHandler = async ({ cookies }) => {
14	try {
15		// Get session token from cookie
16		const sessionToken = cookies.get('session');
17		if (!sessionToken) {
18			throw error(401, 'No session found');
19		}
20
21		// Validate session token
22		const { user } = await validateSessionToken(sessionToken);
23
24		if (!user) {
25			throw error(401, 'Invalid session');
26		}
27
28		// Get user's text preferences
29		const preferences = await convex.textPreferences.get(user.id);
30
31		if (!preferences) {
32			// Return defaults if no preferences exist
33			return json({
34				success: true,
35				preferences: DEFAULT_USER_PREFERENCES
36			});
37		}
38
39		// Convert Convex format to frontend format
40		const frontendPreferences: UserTextPreferences = {
41			defaultStyle: preferences.defaultStyle as UserTextPreferences['defaultStyle'],
42			allowFormatting: preferences.allowFormatting,
43			maxMessageLength: preferences.maxMessageLength
44		};
45
46		return json({
47			success: true,
48			preferences: frontendPreferences
49		});
50	} catch (err) {
51		console.error('Failed to fetch text preferences:', err);
52		if (err instanceof Response || isKitHttpError(err)) {
53			throw err;
54		}
55		throw error(500, 'Failed to fetch preferences');
56	}
57};
58
59// POST - Save current user's text preferences
60export const POST: RequestHandler = async ({ cookies, request }) => {
61	try {
62		// Get session token from cookie
63		const sessionToken = cookies.get('session');
64		if (!sessionToken) {
65			throw error(401, 'No session found');
66		}
67
68		// Validate session token
69		const { user } = await validateSessionToken(sessionToken);
70
71		if (!user) {
72			throw error(401, 'Invalid session');
73		}
74
75		// Parse request body
76		const body = await request.json();
77		const { allowFormatting, maxMessageLength } = body as {
78			allowFormatting: unknown;
79			maxMessageLength: unknown;
80			defaultStyle: unknown;
81		};
82
83		// Validate types
84		if (typeof allowFormatting !== 'boolean' || typeof maxMessageLength !== 'number') {
85			throw error(400, 'Invalid preferences data');
86		}
87
88		// Validate and sanitize style data through existing Zod schema
89		const validatedStyle = sanitizeStyleData(body.defaultStyle);
90		if (!validatedStyle) {
91			throw error(400, 'Invalid style data');
92		}
93
94		// Save preferences via Convex
95		const result = await convex.textPreferences.save(
96			user.id,
97			validatedStyle,
98			allowFormatting,
99			maxMessageLength
100		);
101
102		return json({
103			success: true,
104			id: result.id
105		});
106	} catch (err) {
107		console.error('Failed to save text preferences:', err);
108		if (err instanceof Response || isKitHttpError(err)) {
109			throw err;
110		}
111		throw error(500, 'Failed to save preferences');
112	}
113};

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 9

↑↓ moveenter openesc close