Skip to content

Commit eedc171

Browse files
committed
feat: track and display package types in dashboard, return casks in config API
1 parent d304513 commit eedc171

File tree

3 files changed

+91
-35
lines changed

3 files changed

+91
-35
lines changed

src/routes/[username]/[slug]/config/+server.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,24 @@ export const GET: RequestHandler = async ({ platform, params }) => {
2929
return json({ error: 'Config is private' }, { status: 403 });
3030
}
3131

32-
const packages: string[] = JSON.parse(config.packages || '[]');
32+
const rawPackages: any[] = JSON.parse(config.packages || '[]');
33+
const packageNames: string[] = [];
34+
const caskNames: string[] = [];
35+
36+
for (const pkg of rawPackages) {
37+
if (typeof pkg === 'string') {
38+
packageNames.push(pkg);
39+
} else {
40+
packageNames.push(pkg.name);
41+
if (pkg.type === 'cask') {
42+
caskNames.push(pkg.name);
43+
}
44+
}
45+
}
3346

3447
const tapsSet = new Set<string>();
3548

36-
for (const pkg of packages) {
49+
for (const pkg of packageNames) {
3750
const parts = pkg.split('/');
3851
if (parts.length === 3) {
3952
tapsSet.add(`${parts[0]}/${parts[1]}`);
@@ -64,7 +77,8 @@ export const GET: RequestHandler = async ({ platform, params }) => {
6477
slug: config.slug,
6578
name: config.name,
6679
preset: config.base_preset,
67-
packages: packages,
80+
packages: packageNames,
81+
casks: caskNames,
6882
taps: taps,
6983
dotfiles_repo: config.dotfiles_repo || ''
7084
});

src/routes/api/configs/[slug]/+server.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ export const GET: RequestHandler = async ({ platform, cookies, params, request }
1414

1515
const installUrl = config.alias ? `${env.APP_URL}/${config.alias}` : `${env.APP_URL}/${user.username}/${params.slug}/install`;
1616

17+
const rawPkgs = JSON.parse((config.packages as string) || '[]');
18+
const packages = rawPkgs.map((p: any) => typeof p === 'string' ? { name: p, type: 'formula' } : p);
19+
1720
return json({
1821
config: {
1922
...config,
20-
packages: JSON.parse((config.packages as string) || '[]'),
21-
snapshot: config.snapshot ? JSON.parse(config.snapshot) : null
23+
packages,
24+
snapshot: config.snapshot ? JSON.parse(config.snapshot as string) : null
2225
},
2326
install_url: installUrl
2427
});

src/routes/dashboard/+page.svelte

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
base_preset: string;
1515
is_public: number;
1616
alias: string | null;
17-
packages?: string[];
17+
packages?: any[];
1818
custom_script?: string;
1919
dotfiles_repo?: string;
2020
updated_at?: string;
@@ -47,7 +47,7 @@
4747
type: 'formula' | 'cask' | 'tap';
4848
}
4949
50-
let selectedPackages = $state(new Set<string>());
50+
let selectedPackages = $state(new Map<string, string>());
5151
let presetExpanded = $state(false);
5252
let packageSearch = $state('');
5353
let searchResults = $state<SearchResult[]>([]);
@@ -113,14 +113,18 @@
113113
114114
function getExtraPackages(): string[] {
115115
const presetPkgs = new Set(getPresetPackages(formData.base_preset));
116-
return Array.from(selectedPackages).filter((pkg) => !presetPkgs.has(pkg));
116+
return Array.from(selectedPackages.keys()).filter((pkg) => !presetPkgs.has(pkg));
117117
}
118118
119119
120120
121121
function initPackagesForPreset(preset: string) {
122122
const presetPkgs = getPresetPackages(preset);
123-
selectedPackages = new Set(presetPkgs);
123+
const newMap = new Map<string, string>();
124+
for (const pkg of presetPkgs) {
125+
newMap.set(pkg, 'formula');
126+
}
127+
selectedPackages = newMap;
124128
}
125129
126130
function handlePresetChange(newPreset: string) {
@@ -129,13 +133,13 @@
129133
}
130134
131135
function togglePresetPackage(pkg: string) {
132-
const newSet = new Set(selectedPackages);
133-
if (newSet.has(pkg)) {
134-
newSet.delete(pkg);
136+
const newMap = new Map(selectedPackages);
137+
if (newMap.has(pkg)) {
138+
newMap.delete(pkg);
135139
} else {
136-
newSet.add(pkg);
140+
newMap.set(pkg, 'formula');
137141
}
138-
selectedPackages = newSet;
142+
selectedPackages = newMap;
139143
}
140144
141145
onMount(async () => {
@@ -175,12 +179,20 @@
175179
custom_script: config.custom_script || '',
176180
dotfiles_repo: config.dotfiles_repo || ''
177181
};
178-
const savedPkgs = config.packages || [];
179-
if (savedPkgs.length > 0) {
180-
selectedPackages = new Set(savedPkgs);
181-
} else {
182-
initPackagesForPreset(config.base_preset);
182+
const savedPkgs = config.packages || [];
183+
if (savedPkgs.length > 0) {
184+
const newMap = new Map<string, string>();
185+
for (const pkg of savedPkgs) {
186+
if (typeof pkg === 'string') {
187+
newMap.set(pkg, 'formula');
188+
} else {
189+
newMap.set((pkg as any).name, (pkg as any).type || 'formula');
190+
}
183191
}
192+
selectedPackages = newMap;
193+
} else {
194+
initPackagesForPreset(config.base_preset);
195+
}
184196
} else {
185197
editingSlug = '';
186198
formData = {
@@ -203,15 +215,15 @@
203215
showModal = false;
204216
}
205217
206-
function togglePackage(pkg: string) {
207-
const newSet = new Set(selectedPackages);
208-
if (newSet.has(pkg)) {
209-
newSet.delete(pkg);
218+
function togglePackage(pkg: string, type: string = 'formula') {
219+
const newMap = new Map(selectedPackages);
220+
if (newMap.has(pkg)) {
221+
newMap.delete(pkg);
210222
} else {
211-
newSet.add(pkg);
223+
newMap.set(pkg, type);
212224
}
213-
selectedPackages = newSet;
214-
formData.packages = Array.from(newSet);
225+
selectedPackages = newMap;
226+
formData.packages = Array.from(newMap.keys());
215227
}
216228
217229
async function saveConfig() {
@@ -233,7 +245,7 @@
233245
body: JSON.stringify({
234246
...formData,
235247
alias: formData.alias.trim() || null,
236-
packages: Array.from(selectedPackages)
248+
packages: Array.from(selectedPackages.entries()).map(([name, type]) => ({ name, type }))
237249
})
238250
});
239251
@@ -347,7 +359,11 @@
347359
custom_script: '',
348360
dotfiles_repo: ''
349361
};
350-
selectedPackages = new Set(data.packages);
362+
const importMap = new Map<string, string>();
363+
for (const pkg of data.packages) {
364+
importMap.set(pkg, 'formula');
365+
}
366+
selectedPackages = importMap;
351367
showModal = true;
352368
} catch (e) {
353369
importError = 'Failed to parse Brewfile';
@@ -520,12 +536,17 @@
520536
</div>
521537
{#if getExtraPackages().length > 0}
522538
<div class="selected-extras">
523-
{#each getExtraPackages() as pkg}
524-
<button type="button" class="extra-tag" onclick={() => togglePackage(pkg)}>
525-
{pkg}
526-
<span class="remove-icon">×</span>
527-
</button>
528-
{/each}
539+
{#each getExtraPackages() as pkg}
540+
<button type="button" class="extra-tag" onclick={() => togglePackage(pkg, selectedPackages.get(pkg) || 'formula')}>
541+
{pkg}
542+
{#if selectedPackages.get(pkg) === 'cask'}
543+
<span class="type-badge cask">cask</span>
544+
{:else if selectedPackages.get(pkg) === 'tap'}
545+
<span class="type-badge tap">tap</span>
546+
{/if}
547+
<span class="remove-icon">×</span>
548+
</button>
549+
{/each}
529550
</div>
530551
{/if}
531552
<div class="packages-search">
@@ -544,7 +565,7 @@
544565
{:else if packageSearch.length >= 2}
545566
<div class="packages-grid">
546567
{#each searchResults as result}
547-
<button type="button" class="package-item" class:selected={selectedPackages.has(result.name)} onclick={() => togglePackage(result.name)}>
568+
<button type="button" class="package-item" class:selected={selectedPackages.has(result.name)} onclick={() => togglePackage(result.name, result.type)}>
548569
<span class="check-indicator">{selectedPackages.has(result.name) ? '' : ''}</span>
549570
<div class="package-content">
550571
<div class="package-info">
@@ -1090,6 +1111,24 @@
10901111
background: #1a9f4a;
10911112
}
10921113
1114+
.type-badge {
1115+
font-size: 0.6rem;
1116+
padding: 1px 4px;
1117+
border-radius: 3px;
1118+
text-transform: uppercase;
1119+
font-family: 'JetBrains Mono', monospace;
1120+
}
1121+
1122+
.type-badge.cask {
1123+
background: rgba(96, 165, 250, 0.2);
1124+
color: #60a5fa;
1125+
}
1126+
1127+
.type-badge.tap {
1128+
background: rgba(251, 191, 36, 0.2);
1129+
color: #fbbf24;
1130+
}
1131+
10931132
.remove-icon {
10941133
font-size: 0.9rem;
10951134
font-weight: bold;

0 commit comments

Comments
 (0)