Skip to content

Commit ff8cd4c

Browse files
Refine playground settings and bottom nav
- Panel max-height is now a percentage slider (10% steps, default 70%) driving every panel via the manager value, replacing the px slider and the inert per-bubble override; the snippet always emits it as a percent. - Reorder settings (Accent below Theme), move Reset into the panel header, and relabel the dock-side control to Initial dock side. - Bottom control bar: solid surface instead of a border, sharper inner button corners with concentric outer rounding, tighter padding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0a6fd83 commit ff8cd4c

7 files changed

Lines changed: 56 additions & 53 deletions

File tree

playground/components/app.svelte

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { effectiveTheme, toBubblesOptions } from "$playground/options";
99
import type { Card } from "$playground/types";
1010
import { writeConfig } from "$playground/url";
11-
import { createBubbles, type PanelLength } from "$src/index";
11+
import { createBubbles } from "$src/index";
1212
import { untrack } from "svelte";
1313
1414
// initialState is a live setting (Settings → Initial state). At "open"
@@ -37,11 +37,9 @@
3737
manager.on("dismiss", ({ id }) => drop(id));
3838
3939
// The README panel gets a fixed width — prose needs the room; the rest
40-
// ride the configurable panel width. Every panel caps its height at 70%
41-
// of the viewport so its content scrolls inside a contained panel; the
42-
// "%" tracks resizes on its own.
40+
// ride the configurable panel width. Panel height comes from the settings
41+
// max-height slider (a viewport percentage), shared by every panel.
4342
const DOCS_PANEL_WIDTH = 960;
44-
const PANEL_MAX_HEIGHT: PanelLength = "70%";
4543
4644
const panelWidthFor = (card: Card): number | undefined =>
4745
card.id === "docs" ? DOCS_PANEL_WIDTH : undefined;
@@ -52,8 +50,7 @@
5250
label: card.title,
5351
icon: mountInto(BubbleGlyph, { icon: card.icon }),
5452
content: mountInto(card.panel),
55-
panelWidth: panelWidthFor(card),
56-
panelMaxHeight: PANEL_MAX_HEIGHT
53+
panelWidth: panelWidthFor(card)
5754
});
5855
spawned[card.id] = added;
5956
if (added) order.push(card.id);

playground/components/cards.svelte

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
// tap-away — registering the row keeps a toggle-in from collapsing the
1717
// flock a beat before the new bubble enters.
1818
const trigger = (node: HTMLElement) => ({ destroy: registerTrigger(node) });
19+
20+
// Connected look: each button keeps the row's roundedness on its outer
21+
// corners and goes sharp (xs) on the inner corners facing a neighbor.
22+
const corners = (i: number, total: number): string => {
23+
if (total === 1) return "rounded-xl";
24+
if (i === 0) return "rounded-l-xl rounded-r-xs";
25+
if (i === total - 1) return "rounded-l-xs rounded-r-xl";
26+
return "rounded-xs";
27+
};
1928
</script>
2029

2130
<!-- A floating, bottom-mounted control bar — fixed and centered on every
@@ -26,15 +35,15 @@
2635
>
2736
<div
2837
use:trigger
29-
class="pointer-events-auto flex items-center gap-1 rounded-2xl border border-zinc-800 bg-black/70 p-1.5 shadow-lg backdrop-blur-md light:border-zinc-200 light:bg-white/70"
38+
class="pointer-events-auto flex items-center gap-1 rounded-2xl bg-zinc-900 p-1 shadow-lg light:bg-white"
3039
>
31-
{#each cards as card (card.id)}
40+
{#each cards as card, i (card.id)}
3241
<button
3342
type="button"
3443
onclick={() => toggle(card)}
3544
aria-pressed={spawned[card.id] ? "true" : "false"}
3645
aria-label={card.title}
37-
class="focus-ring flex cursor-pointer items-center gap-2 rounded-xl px-3.5 py-2.5 text-sm font-medium transition-colors {spawned[
46+
class="focus-ring flex cursor-pointer items-center gap-2 {corners(i, cards.length)} px-3.5 py-2.5 text-sm font-medium transition-colors {spawned[
3847
card.id
3948
]
4049
? 'bg-zinc-800 text-white light:bg-zinc-200 light:text-zinc-900'

playground/components/settings-panel.svelte

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import MinusIcon from "$playground/components/icons/minus-icon.svelte";
88
import PlusIcon from "$playground/components/icons/plus-icon.svelte";
99
import { config } from "$playground/config.svelte";
10-
import { AUTO_PANEL_MAX_HEIGHT, defaults, ranges } from "$playground/defaults";
10+
import { defaults, ranges } from "$playground/defaults";
1111
import { configSnippet } from "$playground/snippet";
1212
import type { Swatch } from "$playground/types";
1313
@@ -68,6 +68,15 @@
6868
<span class="truncate text-sm font-semibold">Customize</span>
6969
<span class="truncate text-xs text-zinc-400 light:text-zinc-600">Changes apply live</span>
7070
</div>
71+
{#if dirty}
72+
<button
73+
type="button"
74+
onclick={reset}
75+
class="focus-ring flex h-7 shrink-0 cursor-pointer items-center rounded-md border border-zinc-700 px-2.5 text-xs light:border-zinc-300"
76+
>
77+
Reset
78+
</button>
79+
{/if}
7180
</header>
7281

7382
<div class="panel-scroll mb-4 flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 pb-0">
@@ -82,26 +91,6 @@
8291
onSelect={(theme) => (config.theme = theme)}
8392
/>
8493

85-
<ControlSegmented
86-
label="Initial state"
87-
options={[
88-
{ value: "docked", label: "Docked" },
89-
{ value: "open", label: "Open" }
90-
]}
91-
value={config.initialState}
92-
onSelect={(initialState) => (config.initialState = initialState)}
93-
/>
94-
95-
<ControlSegmented
96-
label="Dock side"
97-
options={[
98-
{ value: "left", label: "Left" },
99-
{ value: "right", label: "Right" }
100-
]}
101-
value={config.side}
102-
onSelect={(side) => (config.side = side)}
103-
/>
104-
10594
<div class="flex flex-col gap-1.5">
10695
<span class="text-xs text-zinc-400 light:text-zinc-600">Accent</span>
10796
<div class="flex flex-wrap items-center gap-2">
@@ -155,6 +144,26 @@
155144
</div>
156145
</div>
157146

147+
<ControlSegmented
148+
label="Initial state"
149+
options={[
150+
{ value: "docked", label: "Docked" },
151+
{ value: "open", label: "Open" }
152+
]}
153+
value={config.initialState}
154+
onSelect={(initialState) => (config.initialState = initialState)}
155+
/>
156+
157+
<ControlSegmented
158+
label="Initial dock side"
159+
options={[
160+
{ value: "left", label: "Left" },
161+
{ value: "right", label: "Right" }
162+
]}
163+
value={config.side}
164+
onSelect={(side) => (config.side = side)}
165+
/>
166+
158167
<ControlSlider
159168
label="Dock vertical"
160169
min={ranges.vertical.min}
@@ -181,7 +190,7 @@
181190
max={ranges.panelMaxHeight.max}
182191
step={ranges.panelMaxHeight.step}
183192
value={config.panelMaxHeight}
184-
format={(v) => (v === AUTO_PANEL_MAX_HEIGHT ? "Auto" : `${v}px`)}
193+
format={(v) => `${v}%`}
185194
onCommit={(v) => (config.panelMaxHeight = v)}
186195
/>
187196

@@ -214,15 +223,6 @@
214223
<span class="flex items-center justify-between">
215224
<span class="text-xs text-zinc-400 light:text-zinc-600">Your config</span>
216225
<span class="flex items-center gap-2">
217-
{#if dirty}
218-
<button
219-
type="button"
220-
onclick={reset}
221-
class="focus-ring flex h-6 cursor-pointer items-center rounded-md border border-zinc-700 px-2 text-xs light:border-zinc-300"
222-
>
223-
Reset
224-
</button>
225-
{/if}
226226
<button
227227
type="button"
228228
aria-label="Copy config"

playground/defaults.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import type { PlaygroundConfig } from "$playground/types";
22

3-
/** Slider ceiling that means "no panel height cap". */
4-
export const AUTO_PANEL_MAX_HEIGHT = 720;
5-
63
/** Slider bounds, shared by the controls and the URL param clamping. */
74
export const ranges = {
85
vertical: { min: 0, max: 1, step: 0.05 },
96
// The top end exceeds any common viewport; the library clamps the
107
// panel inside the screen margins, so max reads as "full width".
118
panelWidth: { min: 280, max: 1600, step: 20 },
12-
panelMaxHeight: { min: 240, max: AUTO_PANEL_MAX_HEIGHT, step: 40 },
9+
// Panel max height as a viewport percentage — the library takes "%".
10+
panelMaxHeight: { min: 10, max: 100, step: 10 },
1311
maxBubbles: { min: 1, max: 5 }
1412
} as const;
1513

@@ -27,6 +25,6 @@ export const defaults: PlaygroundConfig = {
2725
side: "right",
2826
vertical: 0.5,
2927
panelWidth: 480,
30-
panelMaxHeight: AUTO_PANEL_MAX_HEIGHT,
28+
panelMaxHeight: 70,
3129
maxBubbles: 5
3230
};

playground/options.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { AUTO_PANEL_MAX_HEIGHT } from "$playground/defaults";
21
import type { PlaygroundConfig } from "$playground/types";
32
import {
43
bubbleThemes,
@@ -47,7 +46,6 @@ export const toBubblesOptions = (config: PlaygroundConfig): BubblesOptions => ({
4746
side: config.side,
4847
vertical: config.vertical,
4948
panelWidth: config.panelWidth,
50-
panelMaxHeight:
51-
config.panelMaxHeight === AUTO_PANEL_MAX_HEIGHT ? undefined : config.panelMaxHeight,
49+
panelMaxHeight: `${config.panelMaxHeight}%`,
5250
maxBubbles: config.maxBubbles
5351
});

playground/snippet.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ export const configSnippet = (config: PlaygroundConfig): string => {
2525
if (config.panelWidth !== defaults.panelWidth) {
2626
lines.push(`\tpanelWidth: ${config.panelWidth}`);
2727
}
28-
if (config.panelMaxHeight !== defaults.panelMaxHeight) {
29-
lines.push(`\tpanelMaxHeight: ${config.panelMaxHeight}`);
30-
}
28+
// Always emitted: the demo caps panel height while the library leaves it
29+
// uncapped by default, so omitting it would make the copied snippet render
30+
// full-height panels instead of the contained ones on screen.
31+
lines.push(`\tpanelMaxHeight: "${config.panelMaxHeight}%"`);
3132
if (config.maxBubbles !== defaults.maxBubbles) lines.push(`\tmaxBubbles: ${config.maxBubbles}`);
3233

3334
if (lines.length === 0) return "createBubbles();";

playground/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface PlaygroundConfig {
1111
side: BubbleSide;
1212
vertical: number;
1313
panelWidth: number;
14-
/** AUTO_PANEL_MAX_HEIGHT means "no cap" — the slider's top end. */
14+
/** Panel max height as a percentage of the viewport (10–100). */
1515
panelMaxHeight: number;
1616
maxBubbles: number;
1717
}

0 commit comments

Comments
 (0)