main f6d8405

text-style-selector.svelte RawHistoryFolder

1<script lang="ts">
2	import {
3		RETRO_FONTS,
4		DEFAULT_TEXT_STYLE,
5		type TextStyle,
6		type FontFamily,
7		generateCSSStyle
8	} from '../types/text-formatting';
9	import ColorPicker256 from './color-picker-256.svelte';
10	import { formatText, createGradientText } from '../utils/text-formatter';
11
12	// Props
13	let {
14		style = $bindable(DEFAULT_TEXT_STYLE),
15		previewText = 'Sample text preview',
16		showPreview = true,
17		compact = false
18	} = $props<{
19		style?: TextStyle;
20		previewText?: string;
21		showPreview?: boolean;
22		compact?: boolean;
23	}>();
24
25	// Local state
26	let currentColor = $state(style.color || '#000000');
27	let currentGradient = $state(style.gradient);
28	let formattedPreview = $state('');
29	let isProcessing = $state(false);
30
31	// Update preview when style changes
32	$effect(() => {
33		if (!showPreview) return;
34
35		async function updatePreview() {
36			isProcessing = true;
37			try {
38				if (style.gradient && Array.isArray(style.gradient) && style.gradient.length > 1) {
39					formattedPreview = createGradientText(previewText, style.gradient, 'preview-gradient');
40				} else {
41					formattedPreview = await formatText(previewText, style, false);
42				}
43			} catch (error) {
44				console.error('Preview formatting error:', error);
45				formattedPreview = previewText;
46			} finally {
47				isProcessing = false;
48			}
49		}
50
51		updatePreview();
52	});
53
54	// Font family selection
55	function selectFont(fontFamily: FontFamily) {
56		style = { ...style, fontFamily };
57	}
58
59	// Color selection
60	function handleColorChange(color: string) {
61		currentColor = color;
62		style = { ...style, color, gradient: undefined };
63	}
64
65	// Gradient selection
66	function handleGradientChange(gradient: string[]) {
67		currentGradient = gradient;
68		style = { ...style, gradient, color: gradient[0] };
69	}
70
71	// Update internal state when style prop changes
72	$effect(() => {
73		currentColor = style.color || '#000000';
74		currentGradient = style.gradient;
75	});
76
77	// Toggle style options
78	function toggleBold() {
79		style = { ...style, bold: !style.bold };
80	}
81
82	function toggleItalic() {
83		style = { ...style, italic: !style.italic };
84	}
85
86	function toggleUnderline() {
87		style = { ...style, underline: !style.underline };
88	}
89
90	function toggleStrikethrough() {
91		style = { ...style, strikethrough: !style.strikethrough };
92	}
93
94	// Font size adjustment
95	function updateFontSize(size: number) {
96		style = { ...style, fontSize: Math.max(8, Math.min(18, size)) };
97	}
98</script>
99
100<div class="text-style-selector" class:compact>
101	<!-- Font Family Selection -->
102	<div class="style-group">
103		<label for="font-family-select">Font:</label>
104		<select
105			id="font-family-select"
106			bind:value={style.fontFamily}
107			onchange={(e) => selectFont((e.target as HTMLSelectElement).value as FontFamily)}
108		>
109			{#each Object.entries(RETRO_FONTS) as [key, font] (key)}
110				<option value={key} style="font-family: {font.stack}">
111					{font.name}
112				</option>
113			{/each}
114		</select>
115	</div>
116
117	<!-- Font Size -->
118	<div class="style-group">
119		<label for="font-size-range">Size:</label>
120		<input
121			id="font-size-range"
122			type="range"
123			min="8"
124			max="18"
125			bind:value={style.fontSize}
126			oninput={(e) => updateFontSize(parseInt((e.target as HTMLInputElement).value))}
127		/>
128		<span class="size-display">{style.fontSize}px</span>
129	</div>
130
131	<!-- Style Toggles -->
132	<div class="style-group toggles">
133		<button
134			class="style-toggle"
135			class:active={style.bold}
136			onclick={toggleBold}
137			title="Bold (Ctrl+B)"
138		>
139			<strong>B</strong>
140		</button>
141		<button
142			class="style-toggle"
143			class:active={style.italic}
144			onclick={toggleItalic}
145			title="Italic (Ctrl+I)"
146		>
147			<em>I</em>
148		</button>
149		<button
150			class="style-toggle"
151			class:active={style.underline}
152			onclick={toggleUnderline}
153			title="Underline (Ctrl+U)"
154		>
155			<u>U</u>
156		</button>
157		<button
158			class="style-toggle"
159			class:active={style.strikethrough}
160			onclick={toggleStrikethrough}
161			title="Strikethrough"
162		>
163			<s>S</s>
164		</button>
165	</div>
166
167	<!-- Color & Gradient Selection -->
168	<div class="style-group">
169		<span class="label-text">Color:</span>
170		<div class="color-controls">
171			<ColorPicker256
172				bind:selectedColor={currentColor}
173				bind:selectedGradient={currentGradient}
174				onColorChange={handleColorChange}
175				onGradientChange={handleGradientChange}
176			/>
177		</div>
178	</div>
179
180	<!-- Preview -->
181	{#if showPreview}
182		<div class="preview-section">
183			<span class="label-text">Preview:</span>
184			<div class="preview-text" style={generateCSSStyle(style)}>
185				{#if isProcessing}
186					<span class="loading">Processing...</span>
187				{:else}
188					<!-- eslint-disable-next-line svelte/no-at-html-tags -- formattedPreview is sanitized by formatText/createGradientText -->
189					{@html formattedPreview}
190				{/if}
191			</div>
192		</div>
193	{/if}
194</div>
195
196<style>
197	.text-style-selector {
198		background: #f0f0f0;
199		border: 2px inset #dfdfdf;
200		padding: 8px;
201		font-family: 'MS Sans Serif', sans-serif;
202		font-size: 11px;
203	}
204
205	.text-style-selector.compact {
206		padding: 4px;
207	}
208
209	.style-group {
210		display: flex;
211		align-items: center;
212		gap: 8px;
213		margin-bottom: 8px;
214	}
215
216	.style-group label,
217	.style-group .label-text {
218		font-weight: bold;
219		min-width: 50px;
220	}
221
222	.style-group select {
223		flex: 1;
224		padding: 2px;
225		border: 1px inset #dfdfdf;
226		background: white;
227	}
228
229	.style-group input[type='range'] {
230		flex: 1;
231	}
232
233	.size-display {
234		min-width: 35px;
235		font-size: 10px;
236		color: #666;
237	}
238
239	.toggles {
240		gap: 4px;
241	}
242
243	.style-toggle {
244		width: 24px;
245		height: 24px;
246		border: 1px outset #dfdfdf;
247		background: #f0f0f0;
248		cursor: pointer;
249		display: flex;
250		align-items: center;
251		justify-content: center;
252		font-size: 12px;
253		font-weight: bold;
254	}
255
256	.style-toggle:hover {
257		background: #e0e0e0;
258	}
259
260	.style-toggle.active {
261		border: 1px inset #dfdfdf;
262		background: #d0d0d0;
263	}
264
265	.color-controls {
266		display: flex;
267		gap: 4px;
268	}
269
270	.preview-section {
271		margin-top: 12px;
272		padding-top: 8px;
273		border-top: 1px solid #ccc;
274	}
275
276	.preview-section .label-text {
277		display: block;
278		margin-bottom: 4px;
279		font-weight: bold;
280	}
281
282	.preview-text {
283		padding: 8px;
284		border: 1px inset #dfdfdf;
285		background: white;
286		min-height: 24px;
287		line-height: 1.4;
288	}
289
290	.loading {
291		color: #666;
292		font-style: italic;
293	}
294
295	/* Gradient text support */
296	:global(.preview-gradient) {
297		display: inline;
298	}
299
300	/* Compact mode adjustments */
301	.compact .style-group {
302		margin-bottom: 4px;
303	}
304
305	.compact .style-group label,
306	.compact .style-group .label-text {
307		min-width: 40px;
308		font-size: 10px;
309	}
310
311	.compact .style-toggle {
312		width: 20px;
313		height: 20px;
314		font-size: 10px;
315	}
316</style>

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 8

↑↓ moveenter openesc close