feat: searchable multi-select with all 217 panproto languages for repo creation

Author: Aaron Steven White
Commit 78cf24bca91f3d7456d842e45306390f1279506a
Parent: 8769703583
Structural diff unavailable

These commits were pushed via plain git push, so no pre-parsed schemas are available. Install git-remote-cospan and re-push via panproto:// to see scope-level changes, breaking change detection, and semantic diffs.

brew install panproto/tap/git-remote-cospan
3 files changed +314 -32
@@ -436,7 +436,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
436436 
437437 [[package]]
438438 name = "cospan-appview"
439-version = "0.3.0"
439+version = "0.3.1"
440440 dependencies = [
441441  "anyhow",
442442  "async-trait",
@@ -479,7 +479,7 @@ dependencies = [
479479 
480480 [[package]]
481481 name = "cospan-codegen"
482-version = "0.3.0"
482+version = "0.3.1"
483483 dependencies = [
484484  "anyhow",
485485  "panproto-core",
@@ -493,7 +493,7 @@ dependencies = [
493493 
494494 [[package]]
495495 name = "cospan-node"
496-version = "0.3.0"
496+version = "0.3.1"
497497 dependencies = [
498498  "anyhow",
499499  "async-trait",
@@ -0,0 +1,220 @@
1+// Generated from panproto grammars.toml
2+export const ALL_LANGUAGES = [
3+  { value: 'actionscript', label: 'Actionscript' },
4+  { value: 'ada', label: 'Ada' },
5+  { value: 'agda', label: 'Agda' },
6+  { value: 'al', label: 'Al' },
7+  { value: 'apex', label: 'Apex' },
8+  { value: 'arduino', label: 'Arduino' },
9+  { value: 'asciidoc', label: 'Asciidoc' },
10+  { value: 'asm', label: 'Asm' },
11+  { value: 'astro', label: 'Astro' },
12+  { value: 'awk', label: 'Awk' },
13+  { value: 'bash', label: 'Bash' },
14+  { value: 'bass', label: 'Bass' },
15+  { value: 'batch', label: 'Batch' },
16+  { value: 'beancount', label: 'Beancount' },
17+  { value: 'bibtex', label: 'Bibtex' },
18+  { value: 'bicep', label: 'Bicep' },
19+  { value: 'bitbake', label: 'Bitbake' },
20+  { value: 'blade', label: 'Blade' },
21+  { value: 'brightscript', label: 'Brightscript' },
22+  { value: 'bsl', label: 'Bsl' },
23+  { value: 'c', label: 'C' },
24+  { value: 'caddy', label: 'Caddy' },
25+  { value: 'cairo', label: 'Cairo' },
26+  { value: 'capnp', label: 'Capnp' },
27+  { value: 'cedar', label: 'Cedar' },
28+  { value: 'cedarschema', label: 'Cedarschema' },
29+  { value: 'chatito', label: 'Chatito' },
30+  { value: 'circom', label: 'Circom' },
31+  { value: 'clarity', label: 'Clarity' },
32+  { value: 'clojure', label: 'Clojure' },
33+  { value: 'cmake', label: 'Cmake' },
34+  { value: 'cobol', label: 'Cobol' },
35+  { value: 'commonlisp', label: 'Commonlisp' },
36+  { value: 'cooklang', label: 'Cooklang' },
37+  { value: 'corn', label: 'Corn' },
38+  { value: 'cpon', label: 'Cpon' },
39+  { value: 'cpp', label: 'Cpp' },
40+  { value: 'crystal', label: 'Crystal' },
41+  { value: 'csharp', label: 'Csharp' },
42+  { value: 'css', label: 'Css' },
43+  { value: 'csv', label: 'Csv' },
44+  { value: 'cuda', label: 'Cuda' },
45+  { value: 'cue', label: 'Cue' },
46+  { value: 'cylc', label: 'Cylc' },
47+  { value: 'd', label: 'D' },
48+  { value: 'dart', label: 'Dart' },
49+  { value: 'desktop', label: 'Desktop' },
50+  { value: 'devicetree', label: 'Devicetree' },
51+  { value: 'diff', label: 'Diff' },
52+  { value: 'djot', label: 'Djot' },
53+  { value: 'dockerfile', label: 'Dockerfile' },
54+  { value: 'dot', label: 'Dot' },
55+  { value: 'dtd', label: 'Dtd' },
56+  { value: 'ebnf', label: 'Ebnf' },
57+  { value: 'eds', label: 'Eds' },
58+  { value: 'eex', label: 'Eex' },
59+  { value: 'elisp', label: 'Elisp' },
60+  { value: 'elixir', label: 'Elixir' },
61+  { value: 'elm', label: 'Elm' },
62+  { value: 'elsa', label: 'Elsa' },
63+  { value: 'embedded_template', label: 'Embedded Template' },
64+  { value: 'enforce', label: 'Enforce' },
65+  { value: 'erlang', label: 'Erlang' },
66+  { value: 'facility', label: 'Facility' },
67+  { value: 'faust', label: 'Faust' },
68+  { value: 'fennel', label: 'Fennel' },
69+  { value: 'fidl', label: 'Fidl' },
70+  { value: 'firrtl', label: 'Firrtl' },
71+  { value: 'fish', label: 'Fish' },
72+  { value: 'forth', label: 'Forth' },
73+  { value: 'fortran', label: 'Fortran' },
74+  { value: 'fsharp', label: 'Fsharp' },
75+  { value: 'fsharp_signature', label: 'Fsharp Signature' },
76+  { value: 'func', label: 'Func' },
77+  { value: 'gdscript', label: 'Gdscript' },
78+  { value: 'gitattributes', label: 'Gitattributes' },
79+  { value: 'gitignore', label: 'Gitignore' },
80+  { value: 'gleam', label: 'Gleam' },
81+  { value: 'glsl', label: 'Glsl' },
82+  { value: 'gn', label: 'Gn' },
83+  { value: 'go', label: 'Go' },
84+  { value: 'godot_resource', label: 'Godot Resource' },
85+  { value: 'gomod', label: 'Gomod' },
86+  { value: 'graphql', label: 'Graphql' },
87+  { value: 'groovy', label: 'Groovy' },
88+  { value: 'hack', label: 'Hack' },
89+  { value: 'hare', label: 'Hare' },
90+  { value: 'haskell', label: 'Haskell' },
91+  { value: 'haxe', label: 'Haxe' },
92+  { value: 'hcl', label: 'Hcl' },
93+  { value: 'heex', label: 'Heex' },
94+  { value: 'hlsl', label: 'Hlsl' },
95+  { value: 'html', label: 'Html' },
96+  { value: 'http', label: 'Http' },
97+  { value: 'hurl', label: 'Hurl' },
98+  { value: 'idris', label: 'Idris' },
99+  { value: 'ini', label: 'Ini' },
100+  { value: 'ispc', label: 'Ispc' },
101+  { value: 'janet', label: 'Janet' },
102+  { value: 'java', label: 'Java' },
103+  { value: 'javascript', label: 'Javascript' },
104+  { value: 'jinja2', label: 'Jinja2' },
105+  { value: 'jq', label: 'Jq' },
106+  { value: 'json', label: 'Json' },
107+  { value: 'jsonnet', label: 'Jsonnet' },
108+  { value: 'julia', label: 'Julia' },
109+  { value: 'just', label: 'Just' },
110+  { value: 'kdl', label: 'Kdl' },
111+  { value: 'kotlin', label: 'Kotlin' },
112+  { value: 'latex', label: 'Latex' },
113+  { value: 'lean', label: 'Lean' },
114+  { value: 'ledger', label: 'Ledger' },
115+  { value: 'less', label: 'Less' },
116+  { value: 'linkerscript', label: 'Linkerscript' },
117+  { value: 'liquid', label: 'Liquid' },
118+  { value: 'llvm', label: 'Llvm' },
119+  { value: 'lua', label: 'Lua' },
120+  { value: 'luau', label: 'Luau' },
121+  { value: 'magik', label: 'Magik' },
122+  { value: 'make', label: 'Make' },
123+  { value: 'markdown', label: 'Markdown' },
124+  { value: 'matlab', label: 'Matlab' },
125+  { value: 'mermaid', label: 'Mermaid' },
126+  { value: 'meson', label: 'Meson' },
127+  { value: 'mojo', label: 'Mojo' },
128+  { value: 'move', label: 'Move' },
129+  { value: 'netlinx', label: 'Netlinx' },
130+  { value: 'nginx', label: 'Nginx' },
131+  { value: 'nickel', label: 'Nickel' },
132+  { value: 'nim', label: 'Nim' },
133+  { value: 'ninja', label: 'Ninja' },
134+  { value: 'nix', label: 'Nix' },
135+  { value: 'norg', label: 'Norg' },
136+  { value: 'nqc', label: 'Nqc' },
137+  { value: 'nushell', label: 'Nushell' },
138+  { value: 'objc', label: 'Objc' },
139+  { value: 'ocaml', label: 'Ocaml' },
140+  { value: 'ocaml_interface', label: 'Ocaml Interface' },
141+  { value: 'odin', label: 'Odin' },
142+  { value: 'org', label: 'Org' },
143+  { value: 'pascal', label: 'Pascal' },
144+  { value: 'pem', label: 'Pem' },
145+  { value: 'perl', label: 'Perl' },
146+  { value: 'pgn', label: 'Pgn' },
147+  { value: 'php', label: 'Php' },
148+  { value: 'pkl', label: 'Pkl' },
149+  { value: 'po', label: 'Po' },
150+  { value: 'pony', label: 'Pony' },
151+  { value: 'postscript', label: 'Postscript' },
152+  { value: 'powershell', label: 'Powershell' },
153+  { value: 'prisma', label: 'Prisma' },
154+  { value: 'prolog', label: 'Prolog' },
155+  { value: 'promql', label: 'Promql' },
156+  { value: 'properties', label: 'Properties' },
157+  { value: 'protobuf', label: 'Protobuf' },
158+  { value: 'psv', label: 'Psv' },
159+  { value: 'pug', label: 'Pug' },
160+  { value: 'puppet', label: 'Puppet' },
161+  { value: 'purescript', label: 'Purescript' },
162+  { value: 'python', label: 'Python' },
163+  { value: 'ql', label: 'Ql' },
164+  { value: 'qml', label: 'Qml' },
165+  { value: 'r', label: 'R' },
166+  { value: 'racket', label: 'Racket' },
167+  { value: 're2c', label: 'Re2C' },
168+  { value: 'rego', label: 'Rego' },
169+  { value: 'rescript', label: 'Rescript' },
170+  { value: 'robot', label: 'Robot' },
171+  { value: 'ron', label: 'Ron' },
172+  { value: 'rst', label: 'Rst' },
173+  { value: 'ruby', label: 'Ruby' },
174+  { value: 'rust', label: 'Rust' },
175+  { value: 'scala', label: 'Scala' },
176+  { value: 'scheme', label: 'Scheme' },
177+  { value: 'scss', label: 'Scss' },
178+  { value: 'smali', label: 'Smali' },
179+  { value: 'smithy', label: 'Smithy' },
180+  { value: 'solidity', label: 'Solidity' },
181+  { value: 'sparql', label: 'Sparql' },
182+  { value: 'sql', label: 'Sql' },
183+  { value: 'squirrel', label: 'Squirrel' },
184+  { value: 'starlark', label: 'Starlark' },
185+  { value: 'svelte', label: 'Svelte' },
186+  { value: 'swift', label: 'Swift' },
187+  { value: 'tablegen', label: 'Tablegen' },
188+  { value: 'tcl', label: 'Tcl' },
189+  { value: 'teal', label: 'Teal' },
190+  { value: 'templ', label: 'Templ' },
191+  { value: 'terraform', label: 'Terraform' },
192+  { value: 'textproto', label: 'Textproto' },
193+  { value: 'thrift', label: 'Thrift' },
194+  { value: 'tlaplus', label: 'Tlaplus' },
195+  { value: 'todotxt', label: 'Todotxt' },
196+  { value: 'toml', label: 'Toml' },
197+  { value: 'tsv', label: 'Tsv' },
198+  { value: 'tsx', label: 'Tsx' },
199+  { value: 'turtle', label: 'Turtle' },
200+  { value: 'twig', label: 'Twig' },
201+  { value: 'typescript', label: 'Typescript' },
202+  { value: 'typst', label: 'Typst' },
203+  { value: 'uxntal', label: 'Uxntal' },
204+  { value: 'v', label: 'V' },
205+  { value: 'vb', label: 'Vb' },
206+  { value: 'verilog', label: 'Verilog' },
207+  { value: 'vhdl', label: 'Vhdl' },
208+  { value: 'vim', label: 'Vim' },
209+  { value: 'vue', label: 'Vue' },
210+  { value: 'wast', label: 'Wast' },
211+  { value: 'wat', label: 'Wat' },
212+  { value: 'wgsl', label: 'Wgsl' },
213+  { value: 'wit', label: 'Wit' },
214+  { value: 'wolfram', label: 'Wolfram' },
215+  { value: 'xml', label: 'Xml' },
216+  { value: 'yaml', label: 'Yaml' },
217+  { value: 'yuck', label: 'Yuck' },
218+  { value: 'zig', label: 'Zig' },
219+  { value: 'zsh', label: 'Zsh' },
220+];
@@ -1,31 +1,43 @@
11 <script lang="ts">
22 	import { getAuth } from '$lib/stores/auth.svelte';
33 	import { goto } from '$app/navigation';
4+	import { ALL_LANGUAGES } from '$lib/data/languages';
45 
56 	let auth = $derived(getAuth());
67 
78 	let name = $state('');
89 	let description = $state('');
9-	let protocol = $state('typescript');
10+	let selectedProtocols = $state<string[]>(['typescript']);
11+	let protocolSearch = $state('');
12+	let showProtocolDropdown = $state(false);
1013 	let visibility = $state('public');
1114 	let creating = $state(false);
1215 	let error = $state('');
1316 
14-	const protocols = [
15-		{ value: 'typescript', label: 'TypeScript' },
16-		{ value: 'python', label: 'Python' },
17-		{ value: 'rust', label: 'Rust' },
18-		{ value: 'java', label: 'Java' },
19-		{ value: 'go', label: 'Go' },
20-		{ value: 'swift', label: 'Swift' },
21-		{ value: 'kotlin', label: 'Kotlin' },
22-		{ value: 'csharp', label: 'C#' },
23-		{ value: 'protobuf', label: 'Protocol Buffers' },
24-		{ value: 'graphql', label: 'GraphQL' },
25-		{ value: 'json_schema', label: 'JSON Schema' },
26-		{ value: 'sql', label: 'SQL' },
27-		{ value: 'atproto', label: 'ATProto Lexicon' },
28-	];
17+	let filteredProtocols = $derived(
18+		protocolSearch.trim()
19+			? ALL_LANGUAGES.filter((p) =>
20+					p.label.toLowerCase().includes(protocolSearch.toLowerCase()) ||
21+					p.value.toLowerCase().includes(protocolSearch.toLowerCase())
22+				)
23+			: ALL_LANGUAGES
24+	);
25+
26+	function toggleProtocol(value: string) {
27+		if (selectedProtocols.includes(value)) {
28+			selectedProtocols = selectedProtocols.filter((p) => p !== value);
29+		} else {
30+			selectedProtocols = [...selectedProtocols, value];
31+		}
32+	}
33+
34+	function removeProtocol(value: string) {
35+		selectedProtocols = selectedProtocols.filter((p) => p !== value);
36+	}
37+
38+	function protocolLabel(value: string): string {
39+		return ALL_LANGUAGES.find((p) => p.value === value)?.label ?? value;
40+	}
2941 
3042 	async function handleCreate(event: Event) {
3143 		event.preventDefault();
@@ -42,7 +54,7 @@
4254 					did: auth.did,
4355 					name: name.trim(),
4456 					description: description.trim() || undefined,
45-					protocol,
57+					protocol: selectedProtocols.join(','),
4658 					visibility,
4759 				}),
4860 			});
@@ -105,19 +117,69 @@
105117 				</div>
106118 
107119 				<div>
108-					<label for="protocol" class="mb-1 block text-xs font-medium text-text-secondary">
109-						Protocol
120+					<label class="mb-1 block text-xs font-medium text-text-secondary">
121+						Languages ({selectedProtocols.length} selected)
110122 					</label>
111-					<select
112-						id="protocol"
113-						bind:value={protocol}
114-						class="w-full rounded-md border border-border bg-surface-0 px-3 py-2 text-sm text-text-primary focus:border-accent focus:outline-none"
115-					>
116-						{#each protocols as p}
117-							<option value={p.value}>{p.label}</option>
118-						{/each}
119-					</select>
120-					<p class="mt-1 text-xs text-text-secondary">The schema protocol this repository tracks. Determines structural diff and merge behavior.</p>
123+
124+					<!-- Selected tags -->
125+					{#if selectedProtocols.length > 0}
126+						<div class="mb-2 flex flex-wrap gap-1.5">
127+							{#each selectedProtocols as p}
128+								<button
129+									type="button"
130+									onclick={() => removeProtocol(p)}
131+									class="inline-flex items-center gap-1 rounded-full bg-accent/15 px-2.5 py-0.5 text-xs font-medium text-accent transition-colors hover:bg-accent/25"
132+								>
133+									{protocolLabel(p)}
134+									<svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
135+										<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
136+									</svg>
137+								</button>
138+							{/each}
139+						</div>
140+					{/if}
141+
142+					<!-- Search input -->
143+					<div class="relative">
144+						<input
145+							type="text"
146+							bind:value={protocolSearch}
147+							onfocus={() => showProtocolDropdown = true}
148+							onblur={() => setTimeout(() => showProtocolDropdown = false, 200)}
149+							placeholder="Search 217 languages..."
150+							autocomplete="off"
151+							class="w-full rounded-md border border-border bg-surface-0 px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:border-accent focus:outline-none"
152+						/>
153+
154+						{#if showProtocolDropdown}
155+							<ul class="absolute left-0 top-full z-50 mt-1 max-h-48 w-full overflow-y-auto rounded-lg border border-border bg-surface-1 shadow-lg">
156+								{#each filteredProtocols as p}
157+									<li>
158+										<button
159+											type="button"
160+											onmousedown={() => toggleProtocol(p.value)}
161+											class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors hover:bg-surface-2
162+												{selectedProtocols.includes(p.value) ? 'text-accent' : 'text-text-secondary'}"
163+										>
164+											{#if selectedProtocols.includes(p.value)}
165+												<svg class="h-3 w-3 shrink-0 text-accent" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3">
166+													<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
167+												</svg>
168+											{:else}
169+												<span class="h-3 w-3 shrink-0"></span>
170+											{/if}
171+											{p.label}
172+										</button>
173+									</li>
174+								{/each}
175+								{#if filteredProtocols.length === 0}
176+									<li class="px-3 py-2 text-xs text-text-secondary">No matches</li>
177+								{/if}
178+							</ul>
179+						{/if}
180+					</div>
181+
182+					<p class="mt-1 text-xs text-text-secondary">Languages this repository uses. Determines structural diff and merge behavior.</p>
121183 				</div>
122184 
123185 				<div>
cospan · schematic version control on atproto built on AT Protocol