Skip to content

Commit 9173095

Browse files
jiaweingclaude
andcommitted
chore: apply pending changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 461eb41 commit 9173095

6 files changed

Lines changed: 337 additions & 425 deletions

File tree

apps/desktop/.env.example

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
# Polar License Validation
22

3-
# API URL (use https://sandbox-api.polar.sh for sandbox, or omit for production)
4-
VITE_POLAR_API_URL=https://api.polar.sh
5-
6-
# URL of your deployed Hono server (used for customer portal session creation)
7-
# Local dev: http://localhost:3000 | Production: https://your-api.example.com
8-
VITE_POLAR_SERVER_URL=http://localhost:3000
3+
# Environment: "production" or "sandbox"
4+
VITE_POLAR_ENV=production
95

106
# Get your Organization ID from https://polar.sh
117
VITE_POLAR_ORGANIZATION_ID=your-polar-organization-id
128

139
# Organization slug for customer portal (e.g., "my-org")
1410
VITE_POLAR_ORG_SLUG=your-org-slug
1511

16-
# Customer portal URL (use https://sandbox.polar.sh/{org}/portal for sandbox)
17-
VITE_POLAR_CUSTOMER_PORTAL_URL=https://polar.sh/your-org-slug/portal
1812

1913
# Purchase URL for the license (your Polar product page)
2014
VITE_POLAR_PURCHASE_URL=https://polar.sh/your-org/products/your-product

apps/desktop/src/components/CommandPalette.tsx

Lines changed: 239 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,34 @@ import { openUrl } from "@tauri-apps/plugin-opener";
22
import { Command } from "cmdk";
33
import {
44
ArchiveIcon,
5+
BellIcon,
6+
ChevronDownIcon,
7+
DownloadIcon,
58
FileImageIcon,
9+
FlaskConicalIcon,
610
FolderIcon,
711
FolderPlusIcon,
812
GalleryThumbnailsIcon,
13+
HistoryIcon,
914
Loader2Icon,
1015
MonitorIcon,
1116
MoonIcon,
1217
PlusIcon,
18+
RefreshCwIcon,
19+
RotateCcwIcon,
1320
SearchIcon,
1421
SettingsIcon,
1522
SparklesIcon,
1623
SunIcon,
1724
TrashIcon,
1825
TvIcon,
26+
UploadIcon,
1927
VideoIcon,
28+
Volume2Icon,
2029
} from "lucide-react";
2130
import { useCallback, useEffect, useState } from "react";
2231
import { sileo } from "sileo";
32+
import { checkForUpdate, useUpdateStore } from "@/hooks/use-app-updater";
2333
import * as sounds from "@/lib/sounds";
2434
import { searchVideos, type YoutubeVideo } from "@/lib/youtube-api";
2535
import { getYoutubeApiKey } from "@/lib/youtube-store";
@@ -30,6 +40,8 @@ import {
3040
} from "@/stores/use-gallery-store";
3141
import { useTabsStore } from "@/stores/use-tabs-store";
3242

43+
const PROJECTS_PAGE_SIZE = 8;
44+
3345
type Page =
3446
| "gallery"
3547
| "ai-generate"
@@ -59,13 +71,34 @@ export function CommandPalette({
5971
}: CommandPaletteProps) {
6072
const thumbnails = useGalleryStore((s) => s.thumbnails);
6173
const setTheme = useAppSettingsStore((s) => s.setTheme);
74+
const autoCheckForUpdates = useAppSettingsStore((s) => s.autoCheckForUpdates);
75+
const setAutoCheckForUpdates = useAppSettingsStore(
76+
(s) => s.setAutoCheckForUpdates
77+
);
78+
const saveSearchHistory = useAppSettingsStore((s) => s.saveSearchHistory);
79+
const setSaveSearchHistory = useAppSettingsStore(
80+
(s) => s.setSaveSearchHistory
81+
);
82+
const experimentalFeaturesEnabled = useAppSettingsStore(
83+
(s) => s.experimentalFeaturesEnabled
84+
);
85+
const setExperimentalFeaturesEnabled = useAppSettingsStore(
86+
(s) => s.setExperimentalFeaturesEnabled
87+
);
88+
const soundsEnabled = useAppSettingsStore((s) => s.soundsEnabled);
89+
const setSoundsEnabled = useAppSettingsStore((s) => s.setSoundsEnabled);
90+
const setOnboardingCompleted = useAppSettingsStore(
91+
(s) => s.setOnboardingCompleted
92+
);
6293
const openTab = useTabsStore((s) => s.openTab);
6394
const setEditorVisible = useTabsStore((s) => s.setEditorVisible);
95+
const checking = useUpdateStore((s) => s.checking);
6496

6597
const [search, setSearch] = useState("");
6698
const [ytApiKey, setYtApiKey] = useState<string | null>(null);
6799
const [ytResults, setYtResults] = useState<YoutubeVideo[]>([]);
68100
const [ytLoading, setYtLoading] = useState(false);
101+
const [projectPage, setProjectPage] = useState(1);
69102

70103
useEffect(() => {
71104
getYoutubeApiKey()
@@ -77,9 +110,14 @@ export function CommandPalette({
77110
if (!open) {
78111
setSearch("");
79112
setYtResults([]);
113+
setProjectPage(1);
80114
}
81115
}, [open]);
82116

117+
useEffect(() => {
118+
setProjectPage(1);
119+
}, [search]);
120+
83121
useEffect(() => {
84122
const handler = (e: KeyboardEvent) => {
85123
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
@@ -147,6 +185,40 @@ export function CommandPalette({
147185
onOpenChange(false);
148186
};
149187

188+
const handleCheckForUpdates = () => {
189+
sounds.click();
190+
onOpenChange(false);
191+
checkForUpdate();
192+
};
193+
194+
const handleToggle = (
195+
current: boolean,
196+
setter: (v: boolean) => Promise<void>,
197+
label: string
198+
) => {
199+
sounds.click();
200+
onOpenChange(false);
201+
setter(!current).then(() => {
202+
sileo.success({ title: `${label}: ${current ? "Off" : "On"}` });
203+
});
204+
};
205+
206+
const handleResetOnboarding = () => {
207+
sounds.click();
208+
onOpenChange(false);
209+
setOnboardingCompleted(false).then(() => {
210+
sileo.success({ title: "Onboarding reset" });
211+
});
212+
};
213+
214+
const isSearching = search.trim().length > 0;
215+
const visibleThumbnails = isSearching
216+
? thumbnails
217+
: thumbnails.slice(0, projectPage * PROJECTS_PAGE_SIZE);
218+
const remainingProjects =
219+
thumbnails.length - projectPage * PROJECTS_PAGE_SIZE;
220+
const hasMoreProjects = !isSearching && remainingProjects > 0;
221+
150222
if (!open) return null;
151223

152224
return (
@@ -158,7 +230,7 @@ export function CommandPalette({
158230
/>
159231

160232
{/* palette */}
161-
<div className="relative z-10 w-full max-w-[560px] overflow-hidden rounded-xl border border-border bg-popover shadow-2xl">
233+
<div className="relative z-10 w-full max-w-[580px] overflow-hidden rounded-xl border border-border bg-popover shadow-2xl">
162234
<Command
163235
className="flex flex-col"
164236
filter={commandFilter}
@@ -167,19 +239,19 @@ export function CommandPalette({
167239
}}
168240
>
169241
{/* input */}
170-
<div className="flex items-center gap-2 border-border border-b px-3">
171-
<SearchIcon className="size-4 shrink-0 text-muted-foreground" />
242+
<div className="flex items-center gap-3 border-border border-b px-4">
243+
<SearchIcon className="size-5 shrink-0 text-muted-foreground" />
172244
<Command.Input
173245
autoFocus
174-
className="h-11 w-full bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground"
246+
className="h-14 w-full bg-transparent py-3 text-base outline-none placeholder:text-muted-foreground"
175247
onValueChange={setSearch}
176248
placeholder="Search commands, projects, YouTube..."
177249
value={search}
178250
/>
179251
</div>
180252

181253
{/* list */}
182-
<Command.List className="max-h-[420px] overflow-y-auto overflow-x-hidden p-1">
254+
<Command.List className="max-h-[500px] overflow-y-auto overflow-x-hidden p-1.5">
183255
<Command.Empty className="py-8 text-center text-muted-foreground text-sm">
184256
No results found.
185257
</Command.Empty>
@@ -312,12 +384,140 @@ export function CommandPalette({
312384
</Item>
313385
</Group>
314386

387+
<Separator />
388+
389+
{/* System */}
390+
<Group heading="System">
391+
<Item
392+
icon={
393+
checking ? (
394+
<Loader2Icon className="animate-spin" />
395+
) : (
396+
<RefreshCwIcon />
397+
)
398+
}
399+
keywords={[
400+
"check",
401+
"update",
402+
"version",
403+
"upgrade",
404+
"new version",
405+
]}
406+
onSelect={handleCheckForUpdates}
407+
value="check-updates"
408+
>
409+
Check for Updates
410+
</Item>
411+
<Item
412+
icon={<BellIcon />}
413+
keywords={["auto", "automatic", "updates", "notify"]}
414+
onSelect={() =>
415+
handleToggle(
416+
autoCheckForUpdates,
417+
setAutoCheckForUpdates,
418+
"Auto Updates"
419+
)
420+
}
421+
suffix={<StateBadge enabled={autoCheckForUpdates} />}
422+
value="toggle-auto-updates"
423+
>
424+
Auto Updates
425+
</Item>
426+
<Item
427+
icon={<FlaskConicalIcon />}
428+
keywords={[
429+
"experimental",
430+
"beta",
431+
"features",
432+
"labs",
433+
"preview",
434+
]}
435+
onSelect={() =>
436+
handleToggle(
437+
experimentalFeaturesEnabled,
438+
setExperimentalFeaturesEnabled,
439+
"Experimental Features"
440+
)
441+
}
442+
suffix={<StateBadge enabled={experimentalFeaturesEnabled} />}
443+
value="toggle-experimental"
444+
>
445+
Experimental Features
446+
</Item>
447+
<Item
448+
icon={<HistoryIcon />}
449+
keywords={["search", "history", "save", "privacy"]}
450+
onSelect={() =>
451+
handleToggle(
452+
saveSearchHistory,
453+
setSaveSearchHistory,
454+
"Search History"
455+
)
456+
}
457+
suffix={<StateBadge enabled={saveSearchHistory} />}
458+
value="toggle-search-history"
459+
>
460+
Search History
461+
</Item>
462+
<Item
463+
icon={<Volume2Icon />}
464+
keywords={["sound", "audio", "effects", "mute", "sfx"]}
465+
onSelect={() =>
466+
handleToggle(soundsEnabled, setSoundsEnabled, "Sound Effects")
467+
}
468+
suffix={<StateBadge enabled={soundsEnabled} />}
469+
value="toggle-sounds"
470+
>
471+
Sound Effects
472+
</Item>
473+
<Item
474+
icon={<RotateCcwIcon />}
475+
keywords={[
476+
"reset",
477+
"onboarding",
478+
"tutorial",
479+
"restart",
480+
"walkthrough",
481+
]}
482+
onSelect={handleResetOnboarding}
483+
value="reset-onboarding"
484+
>
485+
Reset Onboarding
486+
</Item>
487+
<Item
488+
icon={<DownloadIcon />}
489+
keywords={["export", "backup", "save", "zip", "storage"]}
490+
onSelect={() => run(() => onPageChange("settings"))}
491+
suffix={
492+
<span className="shrink-0 text-muted-foreground text-xs">
493+
Settings → Storage
494+
</span>
495+
}
496+
value="export-backup"
497+
>
498+
Export Backup
499+
</Item>
500+
<Item
501+
icon={<UploadIcon />}
502+
keywords={["import", "backup", "restore", "zip", "storage"]}
503+
onSelect={() => run(() => onPageChange("settings"))}
504+
suffix={
505+
<span className="shrink-0 text-muted-foreground text-xs">
506+
Settings → Storage
507+
</span>
508+
}
509+
value="import-backup"
510+
>
511+
Import Backup
512+
</Item>
513+
</Group>
514+
315515
{/* Projects */}
316516
{thumbnails.length > 0 && (
317517
<>
318518
<Separator />
319519
<Group heading="Projects">
320-
{thumbnails.slice(0, 50).map((t) => (
520+
{visibleThumbnails.map((t) => (
321521
<Item
322522
icon={<FileImageIcon />}
323523
key={t.id}
@@ -333,6 +533,23 @@ export function CommandPalette({
333533
{t.name}
334534
</Item>
335535
))}
536+
{hasMoreProjects && (
537+
<Command.Item
538+
className="mt-0.5 flex cursor-default select-none items-center gap-2 rounded-lg px-2 py-2.5 text-muted-foreground text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground"
539+
onSelect={() => setProjectPage((p) => p + 1)}
540+
value="load-more-projects"
541+
>
542+
<ChevronDownIcon className="size-4 shrink-0" />
543+
<span>
544+
Show {Math.min(PROJECTS_PAGE_SIZE, remainingProjects)}{" "}
545+
more projects
546+
</span>
547+
<span className="ml-auto text-xs">
548+
{projectPage * PROJECTS_PAGE_SIZE} of{" "}
549+
{thumbnails.length}
550+
</span>
551+
</Command.Item>
552+
)}
336553
</Group>
337554
</>
338555
)}
@@ -344,7 +561,7 @@ export function CommandPalette({
344561
<Group heading="YouTube">
345562
{ytLoading ? (
346563
<Command.Item
347-
className="flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-muted-foreground text-sm"
564+
className="flex cursor-default items-center gap-2 rounded-lg px-2 py-2.5 text-muted-foreground text-sm"
348565
disabled
349566
value="yt-loading"
350567
>
@@ -400,6 +617,20 @@ function Separator() {
400617
return <Command.Separator className="my-1 h-px bg-border" />;
401618
}
402619

620+
function StateBadge({ enabled }: { enabled: boolean }) {
621+
return (
622+
<span
623+
className={`shrink-0 rounded-full px-1.5 py-0.5 font-medium text-xs ${
624+
enabled
625+
? "bg-green-500/15 text-green-500"
626+
: "bg-muted text-muted-foreground"
627+
}`}
628+
>
629+
{enabled ? "On" : "Off"}
630+
</span>
631+
);
632+
}
633+
403634
function Item({
404635
value,
405636
keywords,
@@ -419,7 +650,7 @@ function Item({
419650
}) {
420651
return (
421652
<Command.Item
422-
className="flex cursor-default select-none items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-40 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground data-[selected=true]:[&_svg]:text-accent-foreground"
653+
className="flex cursor-default select-none items-center gap-2.5 rounded-lg px-2 py-2.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-40 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground data-[selected=true]:[&_svg]:text-accent-foreground"
423654
keywords={keywords}
424655
onSelect={onSelect}
425656
value={value}

0 commit comments

Comments
 (0)