Skip to content

Commit c210c93

Browse files
committed
Complete website redesign
1 parent cb51a27 commit c210c93

19 files changed

Lines changed: 1710 additions & 160 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
<h1 align="center">
33
<br>
4-
<a href="https://haptics.jmw.nz">
4+
<a href="https://haptics.jmw.nz" target="_blank">
55
<img src="web-demo/static/og-image.png" alt="HapticWebPlugin" width="600" />
66
</a>
77
</h1>

web-demo/src/lib/components/ApiReference.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
} from "$lib/components/ui/card";
1010
import { Separator } from "$lib/components/ui/separator";
1111
import { API_BASE_URL, API_HOST, API_PORT } from "$lib/config";
12+
import { triggerHaptic } from "$lib/connection.svelte";
1213
import { WAVEFORMS } from "$lib/haptics";
13-
import { triggerHapticWs } from "$lib/haptics.svelte";
1414
import Check from "@lucide/svelte/icons/check";
1515
import Copy from "@lucide/svelte/icons/copy";
1616
import Terminal from "@lucide/svelte/icons/terminal";
@@ -69,7 +69,7 @@
6969
let copiedIndex = $state<number | null>(null);
7070
7171
async function copyToClipboard(text: string, index: number) {
72-
triggerHapticWs("damp_collision");
72+
triggerHaptic("damp_collision");
7373
await navigator.clipboard.writeText(text);
7474
copiedIndex = index;
7575
setTimeout(() => {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script lang="ts">
2+
import Github from "@lucide/svelte/icons/github";
3+
import Scale from "@lucide/svelte/icons/scale";
4+
</script>
5+
6+
<footer class="border-t border-border mt-16">
7+
<div class="max-w-6xl mx-auto px-4 py-8">
8+
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
9+
<p class="text-sm text-muted-foreground">
10+
Haptic Web Plugin - Made by <a
11+
href="https://jmw.nz"
12+
target="_blank"
13+
class="text-primary hover:underline">Jasper M-W</a>
14+
</p>
15+
16+
<div class="flex items-center gap-6">
17+
<a
18+
href="https://github.com/fallstop/HapticWebPlugin/blob/main/LICENSE.md"
19+
target="_blank"
20+
rel="noopener noreferrer"
21+
class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
22+
<Scale class="w-4 h-4" />
23+
<span>MIT License</span>
24+
</a>
25+
26+
<a
27+
href="https://github.com/fallstop/HapticWebPlugin"
28+
target="_blank"
29+
rel="noopener noreferrer"
30+
class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
31+
<Github class="w-4 h-4" />
32+
<span>GitHub</span>
33+
</a>
34+
</div>
35+
</div>
36+
</div>
37+
</footer>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<script lang="ts">
2+
import { page } from "$app/stores";
3+
import { Button } from "$lib/components/ui/button";
4+
import {
5+
connect,
6+
hasInitiatedConnection,
7+
isConnected,
8+
} from "$lib/connection.svelte";
9+
import Github from "@lucide/svelte/icons/github";
10+
import Menu from "@lucide/svelte/icons/menu";
11+
import Radio from "@lucide/svelte/icons/radio";
12+
import Vibrate from "@lucide/svelte/icons/vibrate";
13+
import WifiOff from "@lucide/svelte/icons/wifi-off";
14+
import X from "@lucide/svelte/icons/x";
15+
16+
let mobileMenuOpen = $state(false);
17+
18+
const connected = $derived(isConnected());
19+
const hasInitiated = $derived(hasInitiatedConnection());
20+
21+
const navLinks = [
22+
{ href: "/", label: "Home" },
23+
{ href: "/install", label: "Install" },
24+
{ href: "/integrate", label: "Integrate" },
25+
{ href: "/api", label: "API" },
26+
{ href: "/playground", label: "Playground" },
27+
];
28+
29+
function handleConnect() {
30+
connect();
31+
}
32+
33+
const connectionStatus = $derived.by(() => {
34+
if (connected) {
35+
return { icon: Radio, class: "text-green-400", label: "Connected" };
36+
}
37+
if (hasInitiated) {
38+
return { icon: WifiOff, class: "text-red-400", label: "Disconnected" };
39+
}
40+
return null;
41+
});
42+
</script>
43+
44+
<nav
45+
class="sticky top-0 z-50 border-b border-border bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/60">
46+
<div class="max-w-6xl mx-auto px-4">
47+
<div class="flex h-16 items-center justify-between">
48+
<!-- Logo -->
49+
<a href="/" class="flex items-center gap-2 font-semibold">
50+
<div class="p-1.5 rounded-lg bg-primary/20 border border-primary/30">
51+
<Vibrate class="w-4 h-4 text-primary" />
52+
</div>
53+
<span class="hidden sm:inline">HapticWeb</span>
54+
</a>
55+
56+
<!-- Desktop nav -->
57+
<div class="hidden md:flex items-center gap-1">
58+
{#each navLinks as link}
59+
<a
60+
href={link.href}
61+
class="px-3 py-2 text-sm rounded-md transition-colors {$page.url
62+
.pathname === link.href
63+
? 'text-primary font-medium'
64+
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}">
65+
{link.label}
66+
</a>
67+
{/each}
68+
</div>
69+
70+
<!-- Right side -->
71+
<div class="flex items-center gap-3">
72+
<!-- Connection status/button -->
73+
{#if connectionStatus}
74+
<div
75+
class="hidden sm:flex items-center gap-1.5 text-sm {connectionStatus.class}">
76+
<connectionStatus.icon class="w-4 h-4" />
77+
<span>{connectionStatus.label}</span>
78+
</div>
79+
{:else}
80+
<Button
81+
size="sm"
82+
variant="outline"
83+
onclick={handleConnect}
84+
class="hidden sm:flex">
85+
Connect Mouse
86+
</Button>
87+
{/if}
88+
89+
<!-- GitHub -->
90+
<a
91+
href="https://github.com/fallstop/HapticWebPlugin"
92+
target="_blank"
93+
rel="noopener noreferrer"
94+
class="p-2 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors">
95+
<Github class="w-5 h-5" />
96+
</a>
97+
98+
<!-- Mobile menu button -->
99+
<button
100+
onclick={() => (mobileMenuOpen = !mobileMenuOpen)}
101+
class="md:hidden p-2 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent">
102+
{#if mobileMenuOpen}
103+
<X class="w-5 h-5" />
104+
{:else}
105+
<Menu class="w-5 h-5" />
106+
{/if}
107+
</button>
108+
</div>
109+
</div>
110+
111+
<!-- Mobile menu -->
112+
{#if mobileMenuOpen}
113+
<div class="md:hidden pb-4 space-y-1">
114+
{#each navLinks as link}
115+
<a
116+
href={link.href}
117+
onclick={() => (mobileMenuOpen = false)}
118+
class="block px-3 py-2 text-sm rounded-md transition-colors {$page
119+
.url.pathname === link.href
120+
? 'text-primary font-medium bg-primary/10'
121+
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}">
122+
{link.label}
123+
</a>
124+
{/each}
125+
126+
{#if !connectionStatus}
127+
<Button
128+
size="sm"
129+
variant="outline"
130+
onclick={handleConnect}
131+
class="w-full mt-2">
132+
Connect Mouse
133+
</Button>
134+
{:else}
135+
<div
136+
class="flex items-center gap-1.5 px-3 py-2 text-sm {connectionStatus.class}">
137+
<connectionStatus.icon class="w-4 h-4" />
138+
<span>{connectionStatus.label}</span>
139+
</div>
140+
{/if}
141+
</div>
142+
{/if}
143+
</div>
144+
</nav>

web-demo/src/lib/components/WaveformSelector.svelte

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
<script lang="ts">
22
import { Badge } from "$lib/components/ui/badge";
33
import * as Card from "$lib/components/ui/card";
4-
import { CATEGORY_INFO, WAVEFORMS } from "$lib/haptics";
54
import {
65
getSelectedWaveform,
76
getSelectedWaveformApiName,
7+
isConnected,
88
setSelectedWaveform,
9-
triggerHapticWs,
10-
} from "$lib/haptics.svelte";
9+
triggerHaptic,
10+
} from "$lib/connection.svelte";
11+
import { CATEGORY_INFO, WAVEFORMS } from "$lib/haptics";
1112
import Check from "@lucide/svelte/icons/check";
1213
import WaveformEncoding from "./WaveformEncoding.svelte";
1314
15+
const connected = $derived(isConnected());
16+
1417
function handleSelect(waveformApiName: string) {
1518
setSelectedWaveform(waveformApiName);
16-
triggerHapticWs(waveformApiName);
19+
if (connected) triggerHaptic(waveformApiName);
1720
}
1821
1922
// Group waveforms by category
@@ -71,6 +74,13 @@
7174
{getSelectedWaveform().category}
7275
</Badge>
7376
</h3>
77+
<div class="text-xs text-muted-foreground">
78+
Api Name:
79+
<code
80+
class="font-mono bg-background/50 px-2 py-0.5 rounded mt-1 inline-block">
81+
{getSelectedWaveformApiName()}
82+
</code>
83+
</div>
7484
</div>
7585
<div class="p-4 rounded-lg bg-background/50">
7686
<WaveformEncoding
@@ -112,15 +122,8 @@
112122
encoding={waveform.waveform_encoding}
113123
size="sm" />
114124
</div>
115-
<span
116-
class="text-xs font-medium text-center leading-tight bg-background/50 px-2 py-1 rounded select-text cursor-text font-mono"
117-
role="button"
118-
tabindex="0"
119-
onclick={(e) => e.stopPropagation()}
120-
onkeydown={(e) => {
121-
if (e.key === "Enter" || e.key === " ") e.stopPropagation();
122-
}}>
123-
{waveform.api_name}
125+
<span class="font-medium text-center leading-tight">
126+
{waveform.name}
124127
</span>
125128
</Card.Content>
126129
</Card.Root>

web-demo/src/lib/components/demos/AnimatedDemo.svelte

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
<script lang="ts">
22
import { Button } from "$lib/components/ui/button";
3-
import { triggerHapticWs } from "$lib/haptics.svelte";
3+
import { isConnected, triggerHaptic } from "$lib/connection.svelte";
44
import Play from "@lucide/svelte/icons/play";
55
import Square from "@lucide/svelte/icons/square";
66
77
let isPlaying = $state(false);
88
let ballPosition = $state(0);
99
let animationFrame: number;
1010
let bounceCount = $state(0);
11+
const connected = $derived(isConnected());
1112
1213
const WAVEFORM_ID = "subtle_collision";
1314
1415
function startAnimation() {
15-
if (isPlaying) return;
16+
if (isPlaying || !connected) return;
1617
isPlaying = true;
1718
bounceCount = 0;
1819
animate();
@@ -47,7 +48,7 @@
4748
4849
if (progress >= 1) {
4950
// Ball hit the ground - trigger haptic
50-
triggerHapticWs(WAVEFORM_ID);
51+
triggerHaptic(WAVEFORM_ID);
5152
bounceCount++;
5253
5354
if (bounceCount < 5) {
@@ -72,7 +73,9 @@
7273
</p>
7374

7475
<div
75-
class="relative w-full h-32 bg-linear-to-b from-transparent to-muted/50 rounded-lg overflow-hidden">
76+
class="relative w-full h-32 bg-linear-to-b from-transparent to-muted/50 rounded-lg overflow-hidden {!connected
77+
? 'opacity-50'
78+
: ''}">
7679
<!-- Ground line -->
7780
<div class="absolute bottom-0 left-0 right-0 h-1 bg-primary/50"></div>
7881

@@ -85,15 +88,17 @@
8588
</div>
8689

8790
<div class="flex gap-2">
88-
<Button onclick={startAnimation} disabled={isPlaying}>
91+
<Button onclick={startAnimation} disabled={isPlaying || !connected}>
8992
<Play class="w-4 h-4 mr-1" /> Start
9093
</Button>
9194
<Button variant="outline" onclick={stopAnimation} disabled={!isPlaying}>
9295
<Square class="w-4 h-4 mr-1" /> Stop
9396
</Button>
9497
</div>
9598

96-
{#if isPlaying}
99+
{#if !connected}
100+
<p class="text-xs text-muted-foreground">Connect your mouse to enable</p>
101+
{:else if isPlaying}
97102
<p class="text-xs text-muted-foreground">
98103
Bounce {bounceCount}/5
99104
</p>

web-demo/src/lib/components/demos/CTAButton.svelte

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,34 @@
22
import { Button } from "$lib/components/ui/button";
33
import {
44
getSelectedWaveform,
5-
triggerSelectedHapticWs,
6-
} from "$lib/haptics.svelte";
5+
isConnected,
6+
triggerSelectedHaptic,
7+
} from "$lib/connection.svelte";
78
import WaveformEncoding from "../WaveformEncoding.svelte";
89
910
let isAnimating = $state(false);
11+
const connected = $derived(isConnected());
1012
1113
function handleClick() {
14+
if (!connected) return;
1215
isAnimating = true;
13-
triggerSelectedHapticWs();
16+
triggerSelectedHaptic();
1417
setTimeout(() => {
1518
isAnimating = false;
1619
}, 300);
1720
}
1821
</script>
1922

2023
<div
21-
class="flex flex-col items-center gap-4 p-6 rounded-xl bg-linear-to-br from-primary/20 to-primary/5 border border-primary/20">
24+
class="flex flex-col items-center gap-4 p-6 rounded-xl bg-muted/30 border border-border">
2225
<h3 class="text-lg font-semibold">Click to Feel</h3>
2326
<p class="text-sm text-muted-foreground text-center">
2427
Press the button to trigger <span class="font-semibold text-primary"
2528
>{getSelectedWaveform().name}</span>
2629
</p>
2730
<Button
2831
size="lg"
32+
disabled={!connected}
2933
class="text-lg px-4 py-6 transition-transform {isAnimating
3034
? 'scale-95'
3135
: ''}"
@@ -37,4 +41,7 @@
3741
size="sm" />
3842
</div>
3943
</Button>
44+
{#if !connected}
45+
<p class="text-xs text-muted-foreground">Connect your mouse to enable</p>
46+
{/if}
4047
</div>

0 commit comments

Comments
 (0)