Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions apps/web/index.html
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Create a screenshot showcasing your app's transition from light to dark theme."
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="preload"
href="https://fonts.googleapis.com/css2?family=Geist:wght@300;500;600;700&display=swap" rel="stylesheet"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
/>
<title>Shadowave</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Create a screenshot showcasing your app's transition from light to dark theme." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload"
href="https://fonts.googleapis.com/css2?family=Geist:wght@300;500;600;700&family=Geist+Mono:wght@500&display=swap"
rel="stylesheet" as="style" onload="this.onload=null;this.rel='stylesheet'" />
<title>Shadowave</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
21 changes: 11 additions & 10 deletions apps/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,38 @@ export function App() {
return (
<AppProvider>
<div className="bg-grid pointer-events-none absolute inset-0 [mask-image:linear-gradient(180deg,#fff_5%,transparent_80%)] select-none"></div>
<div className="relative grid h-dvh w-dvw grid-rows-[auto_1fr_auto] overflow-hidden">
<header className="grid grid-cols-[1fr_2fr_1fr] items-center p-2.5 sm:p-5">
<div className="relative grid h-dvh w-dvw grid-rows-[auto_1fr_auto] overflow-hidden p-5">
<header className="grid grid-cols-[1fr_2fr_1fr] items-center">
<section className="col-start-2 justify-self-center">
<div className="bg-card flex items-center rounded-md border p-1 shadow-xs sm:gap-1">
<ImportImages variant="ghost" />
<ExportImage />
<CopyImage />
<Separator orientation="vertical" className="h-4" />
<ReverseImages variant="ghost" />
<OptimizeWaveform />
<ChangeWaveFunction />
<ReverseImages variant="ghost" />
<Separator orientation="vertical" className="h-4" />
<CopyImage />
<ExportImage />
<Separator orientation="vertical" className="h-4" />
<RemoveImages variant="ghost" />
</div>
</section>
<ChangeTheme className="justify-self-end" />
<ChangeTheme className="justify-self-end" variant="outline" />
</header>

<main className="grid place-items-center overflow-auto p-20">
<main className="relative grid place-items-center overflow-auto">
<Suspense
fallback={<LoaderCircle className="size-12 animate-spin" />}
>
<Graphics
className="h-full w-full drop-shadow-2xl"
className="h-full w-full p-16 drop-shadow-xl md:drop-shadow-2xl"
fallback={<NoImages />}
/>
</Suspense>
</main>

<footer className="p-5 pb-8 sm:pb-12">
<section className="mx-auto grid max-w-4xl grid-cols-1 gap-12 sm:grid-cols-3">
<footer className="pb-3 lg:pb-7">
<section className="mx-auto grid max-w-4xl grid-cols-1 gap-12 lg:grid-cols-3">
<Waveform />
</section>
</footer>
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/atoms/waveAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import { atom } from 'jotai';
import { unwrappedLargestImageAtom } from '@/atoms/largestImageAtom';
import { waveFunctionAtom } from '@/atoms/waveFunctionAtom';
import { amplitudeAtom, wavelengthAtom } from '@/atoms/waveformAtoms';
import { sin, heartbeat } from '@/utils/waveFunctions';
import { WaveFunction } from '@/types';

function round(num: number) {
return Math.round((num + Number.EPSILON) * 100) / 100;
}

const waveFunctionMapping: Record<WaveFunction, (x: number) => number> = {
sin: Math.sin,
cos: Math.cos
sin,
heartbeat
};

export const waveAtom = atom(get => {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/atoms/waveFunctionAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { WaveFunction } from '@/types';

export const waveFunctionAtom = atomWithStorage<WaveFunction>(
'waveFunction',
WAVE_FUNCTION.SIN,
WAVE_FUNCTION.HEARTBEAT,
undefined,
{ getOnInit: true }
);
5 changes: 2 additions & 3 deletions apps/web/src/components/AtomSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { LabelProps } from '@radix-ui/react-label';
import { cn } from '@workspace/ui/lib/utils';
import { Label } from '@workspace/ui/components/label';
import { Slider } from '@workspace/ui/components/slider';
import { Shortcut, ShortcutProps } from '@/components/Shortcut';
import { Shortcut, ShortcutProps } from '@/components/Shortcut/Shortcut';

export interface AtomSliderProps
extends Omit<React.ComponentProps<typeof Slider>, 'value' | 'onValueChange'> {
Expand Down Expand Up @@ -46,7 +46,6 @@ export interface AtomSliderProps
export function AtomSlider(props: AtomSliderProps) {
const {
atom,
className,
label,
labelProps,
shortcutProps,
Expand Down Expand Up @@ -78,7 +77,7 @@ export function AtomSlider(props: AtomSliderProps) {
);

return (
<div className={cn('grid gap-4', className)}>
<div className="grid gap-4">
<div className="flex items-center justify-between">
<Label
{...labelProps}
Expand Down
11 changes: 7 additions & 4 deletions apps/web/src/components/ChangeWaveFunction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ToggleGroupItem
} from '@workspace/ui/components/toggle-group';
import { Sin } from '@/components/Sin';
import { Cos } from '@/components/Cos';
import { Heartbeat } from '@/components/Heartbeat';
import { WAVE_FUNCTION } from '@/constants';
import { WaveFunction } from '@/types';
import { unwrappedImagesAtom } from '@/atoms/imagesAtom';
Expand Down Expand Up @@ -40,12 +40,15 @@ export function ChangeWaveFunction(props: ChanveWaveFunctionProps) {
disabled={!images.length}
{...props}
>
<ToggleGroupItem
value={WAVE_FUNCTION.HEARTBEAT}
aria-label={t`Heartbeat`}
>
<Heartbeat />
</ToggleGroupItem>
<ToggleGroupItem value={WAVE_FUNCTION.SIN} aria-label={t`Sine`}>
<Sin />
</ToggleGroupItem>
<ToggleGroupItem value={WAVE_FUNCTION.COS} aria-label={t`Cosine`}>
<Cos />
</ToggleGroupItem>
</ToggleGroup>
);
}
4 changes: 2 additions & 2 deletions apps/web/src/components/CopyImage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAtomValue } from 'jotai';
import { Clipboard } from 'lucide-react';
import { Copy } from 'lucide-react';
import { useLingui } from '@lingui/react/macro';
import { cn } from '@workspace/ui/lib/utils';
import { Button } from '@workspace/ui/components/button';
Expand Down Expand Up @@ -43,7 +43,7 @@ export function CopyImage(props: CopyImageProps) {
>
<LoadableIcon
state={state}
fallback={Clipboard}
fallback={Copy}
className={cn({
'animate-spin': isCopying
})}
Expand Down
6 changes: 0 additions & 6 deletions apps/web/src/components/Cos.tsx

This file was deleted.

6 changes: 4 additions & 2 deletions apps/web/src/components/ExportImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@/atoms/exportAtoms';
import { Button } from '@workspace/ui/components/button';
import { LoadableIcon } from '@/components/LoadableIcon';
import { Shortcut } from '@/components/Shortcut';
import { Shortcut } from '@/components/Shortcut/Shortcut';
import { HOTKEYS, LOADABLE_STATE } from '@/constants';

export function ExportImage() {
Expand Down Expand Up @@ -48,7 +48,6 @@ export function ExportImage() {
<DropdownMenuTrigger asChild disabled={!exportFileHandle}>
<Button
variant="ghost"
size="icon"
onClick={createExportClickHandler(null)}
disabled={disabled}
aria-label={t`Choose file`}
Expand All @@ -60,6 +59,9 @@ export function ExportImage() {
'animate-spin': isExporting
})}
/>
<div className="hidden sm:block">
<Trans>Export</Trans>
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-66">
Expand Down
22 changes: 11 additions & 11 deletions apps/web/src/components/Graphics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ export function Graphics(props: GraphicsProps) {
</clipPath>
))}
</defs>

{orderedImages.map((image, i) => (
<image
key={image.id}
href={image.src}
style={{ zIndex: i + 1 }}
clipPath={
i >= 1 ? `url(#${waveId}${orderedImages.length - i})` : undefined
}
/>
))}
<g>
{orderedImages.map((image, i) => (
<image
key={image.id}
href={image.src}
clipPath={
i >= 1 ? `url(#${waveId}${orderedImages.length - i})` : undefined
}
/>
))}
</g>
</svg>
);
}
4 changes: 4 additions & 0 deletions apps/web/src/components/Heartbeat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createGraphIcon } from '@/utils/createGraphIcon';
import { heartbeat } from '@/utils/waveFunctions';

export const Heartbeat = createGraphIcon('Heartbeat', x => heartbeat(x) * 8);
12 changes: 6 additions & 6 deletions apps/web/src/components/ImportImages.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAtom, useAtomValue } from 'jotai';
import { Camera, Folder, Monitor, Smartphone, Tablet } from 'lucide-react';
import { Camera, Monitor, Smartphone, Tablet, Upload } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { Trans, Plural, useLingui } from '@lingui/react/macro';
Expand Down Expand Up @@ -86,11 +86,11 @@ export function ImportImages(props: ImportImagesProps) {
<Dialog open={Boolean(importSignal)} onOpenChange={toggleImportSignal}>
<DialogTrigger asChild>
<Button {...props}>
<Folder />
<Upload />
<div className="hidden sm:block">
<Plural
value={images.length}
_0="Import"
_0="Open"
one="# image"
other="# images"
/>
Expand All @@ -100,7 +100,7 @@ export function ImportImages(props: ImportImagesProps) {
<DialogContent>
<DialogHeader>
<DialogTitle>
<Trans>Import</Trans>
<Trans>Import Images</Trans>
</DialogTitle>
<DialogDescription>
<Trans>
Expand All @@ -125,7 +125,7 @@ export function ImportImages(props: ImportImagesProps) {
<span className="text-destructive">*</span>
</FormLabel>
<FormControl>
<Input required {...field} />
<Input required type="url" {...field} />
</FormControl>
{fieldState.invalid ? (
<FormMessage />
Expand Down Expand Up @@ -204,7 +204,7 @@ export function ImportImages(props: ImportImagesProps) {
'animate-spin': isImporting
})}
/>
<Trans>Import</Trans>
<Trans>Capture</Trans>
</Button>
</DialogFooter>
</form>
Expand Down
32 changes: 22 additions & 10 deletions apps/web/src/components/NoImages.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { useSetAtom } from 'jotai';
import { ImageOff, Folder } from 'lucide-react';
import { ImageOff, Upload } from 'lucide-react';
import { Trans } from '@lingui/react/macro';
import { cn } from '@workspace/ui/lib/utils';
import { Button } from '@workspace/ui/components/button';
import { Shortcut } from '@/components/Shortcut';
import { Shortcut } from '@/components/Shortcut/Shortcut';
import { importAtom, importSignalAtom } from '@/atoms/importAtoms';
import { siteConfig } from '@/config/site';
import { HOTKEYS } from '@/constants';

export function NoImages() {
export type NoImagesProps = React.ComponentProps<'div'>;

export function NoImages(props: NoImagesProps) {
const importImage = useSetAtom(importAtom);
const toggleImportSignal = useSetAtom(importSignalAtom);

Expand All @@ -20,25 +23,34 @@ export function NoImages() {
};

return (
<div className="flex max-w-md flex-col items-center gap-6 text-center font-light">
<ImageOff className="size-24 text-gray-400" strokeWidth={1.5} />
<h1 className="text-4xl">
<div
{...props}
className={cn(
'flex max-w-md flex-col items-center gap-6 text-center font-light',
props.className
)}
>
<ImageOff
className="size-16 text-gray-400 md:size-24"
strokeWidth={1.5}
/>
<h1 className="text-3xl">
<Trans>No images</Trans>
</h1>
<p>
<Trans>
Get started by importing your images or check out the example to see
Get started by choosing your images or check out the example to see
how it works.
</Trans>
</p>
<div className="flex w-full flex-col justify-center gap-4 sm:flex-row">
<Button onClick={handleImportClick}>
<Folder />
Import
<Upload />
Open
<Shortcut keys={HOTKEYS.IMPORT} />
</Button>
<Button onClick={handleClick} variant="secondary">
<Trans>See example</Trans>
<Trans>See Example</Trans>
</Button>
</div>
</div>
Expand Down
Loading