Skip to content

Commit 961e2ef

Browse files
authored
Toolbar, popover, and page browser styles (#150)
* Toolbar redesign with new icons * Update Toolbar.svelte * Update Toolbar.svelte * Improve toolbar * Improvements * Update Toolbar.svelte * Update Toolbar.svelte * Update Toolbar.svelte * Improved icons * Better alt icon * Ellipsis menu for logout and page actions * Improved icons for browse and edit * Consistent tool styles * Pill-shaped EditLink and LinkPreview * Consolidate CreateLink and EditLink into one * Create label also for button links * More padding for variant selector * Consistent styles for EditMedia * Consistent style for "Create Link" button * Improve styles of browse button in EditLink * Fix margin * Icons for save/cancel on mobile * Improve PageBrowser styles * Circular preview image * Try medium font weight for display text styles * A more stylish page browser * Lean internal link preview * Tweaks * Use larger text in variant selector on desktop * Polish EditLink dialog * More spacing for the page metadata * Better UNLISTED label * Improve checkbox style * Render LinkPreview/EditLink a bit further away from the anchor * Style fixes * Update EditMedia.svelte
1 parent f16550f commit 961e2ef

12 files changed

Lines changed: 1021 additions & 931 deletions

ARCHITECTURE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,10 @@ This means the page browser is not a full graph visualization. It is a stable, e
10981098

10991099
If a page is linked from multiple places, later occurrences are ignored for placement. This keeps the page browser compact and avoids crowded duplicates. If needed in the future, secondary references can be surfaced separately (for example as “also linked from…” metadata), but they are not duplicated in the primary forest.
11001100

1101+
### Internal link previews
1102+
1103+
When an internal link is selected in the editor, its preview uses a single compact row rather than separate URL and metadata rows. The optional page preview image and resolved page title form the link target; edit and remove remain separate trailing actions. The raw internal href is intentionally omitted from the preview and remains available through the link editor. External links continue to show their href because they do not have page metadata to summarize them.
1104+
11011105
### Contextual search in the page browser
11021106

11031107
The page browser supports a client-side contextual search over the already loaded drawer data. No dedicated server-side search endpoint is required for the initial implementation.

IMPLEMENTATION_PLAN.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,34 @@ Convert the whole codebase from JS+JSDoc to TypeScript per the "Language: TypeSc
2020
- When no ancestor has multiple variants, fall back to the closest selected node and render its `Type (layout)` identity without dropdown behavior or a chevron.
2121
- Flatten valid type/layout combinations into a native select for direct choice and keyboard type-ahead; do not add a misleading combined next/previous control when keyboard cycling remains dimension-specific.
2222
- Keep the ordinary select-parent command in the existing selection tool group; do not duplicate it inside the variant selector.
23+
- Hide the select-parent tool when it is disabled, and draw its trailing separator only when a variant selector is visible beside it.
2324
- Split the variant selector to the left and the existing toolbar actions to the right when space permits, collapsing the flexible gap before horizontal overflow on narrow screens.
25+
- On mobile, center the toolbar and let it shrink-wrap its contents until it reaches the viewport-safe maximum width.
26+
- On mobile overflow, use one horizontal scroll area for the variant selector and ordinary editing actions; keep the select-parent button fixed at the leading edge and the cancel/save actions fixed at the trailing edge, with separators and opaque edge surfaces visually containing the scrolling region.
27+
- On mobile, render the trailing cancel and save actions as accessible icons instead of their text labels to preserve toolbar space; keep the text labels on larger screens.
28+
- Keep variant labels on one line at their intrinsic width so the scroll area, rather than the selector itself, absorbs narrow-screen overflow.
2429
- Keep type/layout cycle commands and keyboard shortcuts registered, but hide their redundant toolbar buttons while the variant selector is the primary visible UI.
2530
- Expose the type up/down and layout left/right keyboard shortcuts in the variant selector's hover tooltip rather than as persistent UI chrome.
2631
- Pulse the selector when its displayed type-switch target is still empty and every alternative type remains safely available, hinting at type choice immediately after insertion.
2732
- Do not require a parallel variant-label registry; new schema types and layouts should appear automatically.
2833

34+
## Page browser visual alignment
35+
36+
- Style the page browser search field like the EditLink URL input: a toolbar-height pill-shaped `--border` outline that switches directly to `--svedit-editing-stroke` on focus, without a focus shadow.
37+
- Render hovered and keyboard-selected page rows as compact, toolbar-height pill surfaces using the shared `--muted`, `--svedit-editing-fill`, and `--svedit-editing-stroke` colors, with circular preview images sized to fit the row shape and only the shared checkerboard background when no preview is available.
38+
- Render each page ellipsis trigger as the same round tool button used in the editing toolbar, inset from its row pill by the toolbar surface spacing.
39+
- On hover-capable devices, use `--background` behind the ellipsis trigger and unlisted marker so they remain distinct from row hover and selection fills.
40+
- On mobile, disable hover-only treatments and give the ellipsis trigger and unlisted marker `--muted` backgrounds on ordinary rows and `--background` backgrounds on the selected row.
41+
- Style the page action menu like the top-level toolbar page-actions menu, including its border, rounded menu surface, shadow, item spacing, and hover treatment.
42+
- Style the unlisted marker like the toolbar ALT control, while preserving its explanatory button behavior.
43+
44+
## Internal link preview simplification
45+
46+
- Keep external link previews unchanged as a single href row with edit and remove actions.
47+
- Render internal link previews as one pill row containing an optional circular preview image, the resolved page title, and the existing edit and remove actions.
48+
- Make the preview image and title one link target, and omit the raw href from the preview because the link editor exposes it.
49+
- Keep loading, missing-page, and error states inside the same compact row rather than adding a second row.
50+
2951
## Human-readable layout ids
3052

3153
- Replace numeric `layout` values in the schema, renderers, inserters, seed content, Markdown conversion, tests, and documentation with descriptive string ids.
@@ -478,7 +500,8 @@ Required UI behavior:
478500
- drafts and private sitemap UI are hidden unless authenticated as admin
479501
- link pickers must not expose drafts to unauthenticated users
480502
- toolbar actions that require admin auth must be hidden or disabled when unauthenticated
481-
- authenticated admins get an explicit logout button
503+
- authenticated admins get an ellipsis page-actions menu in the browsing toolbar with Duplicate page, Edit URL, Delete page, and Logout entries
504+
- keep the initial page-management entries presentational and retain the existing working Logout action; wire Duplicate page, Edit URL, and Delete page in a follow-up task
482505

483506
### Page browser behavior
484507

src/app.css

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,27 +79,27 @@
7979

8080
/* E.g. Heading1XL */
8181
@utility display-1 {
82-
@apply font-sans text-6xl leading-tight tracking-tight text-balance lg:text-7xl;
82+
@apply font-sans font-medium text-6xl leading-tight tracking-tight text-balance lg:text-7xl;
8383
}
8484

8585
/* E.g. Heading1 */
8686
@utility display-2 {
87-
@apply font-sans text-5xl leading-tight tracking-tight text-balance lg:text-6xl;
87+
@apply font-sans font-medium text-5xl leading-tight tracking-tight text-balance lg:text-6xl;
8888
}
8989

9090
/* E.g. Heading2 */
9191
@utility display-3 {
92-
@apply font-sans text-4xl leading-tight tracking-tight text-balance lg:text-5xl;
92+
@apply font-sans font-medium text-4xl leading-tight tracking-tight text-balance lg:text-5xl;
9393
}
9494

9595
/* E.g. Heading3 */
9696
@utility display-4 {
97-
@apply font-sans text-3xl leading-tight tracking-tight text-balance lg:text-4xl;
97+
@apply font-sans font-medium text-3xl leading-tight tracking-tight text-balance lg:text-4xl;
9898
}
9999

100100
/* E.g. Heading4, TitleGallery.title, ... */
101101
@utility display-5 {
102-
@apply font-sans text-2xl leading-tight tracking-tight text-balance lg:text-3xl;
102+
@apply font-sans font-medium text-2xl leading-tight tracking-tight text-balance lg:text-3xl;
103103
}
104104

105105
/* ParagraphSM, ListingItem.description, ... */

src/routes/components/CreateLink.svelte

Lines changed: 0 additions & 144 deletions
This file was deleted.

src/routes/components/EditLink.svelte

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
const app = get_app_context();
1010
const page_browser = get_page_browser();
1111
12-
let { path }: { path: DocumentPath } = $props();
12+
let { path }: { path?: DocumentPath } = $props();
1313
1414
let edit_link_command = $derived(svedit.session.commands?.edit_link);
15+
let toggle_link_command = $derived(svedit.session.commands?.toggle_link);
16+
let is_creating = $derived(toggle_link_command?.show_prompt === true);
17+
let is_open = $derived(
18+
edit_link_command?.show_prompt === true || toggle_link_command?.show_prompt === true
19+
);
20+
let anchor_name = $derived(path ? `--${serialize_path(path)}` : '--selection-highlight');
1521
let target_node = $derived(get_target_node());
22+
let is_new_link = $derived(is_creating || !target_node?.href);
1623
1724
function get_target_node() {
1825
const selected_node = svedit.session.selected_node;
@@ -29,7 +36,16 @@
2936
let dialog_ref = $state<HTMLDialogElement>();
3037
3138
function save() {
32-
if (target_node && 'href' in target_node) {
39+
if (is_creating) {
40+
if (href_input_value) {
41+
svedit.session.apply(
42+
svedit.session.tr.toggle_mark('link', {
43+
href: href_input_value,
44+
target: open_in_new_tab ? '_blank' : '_self'
45+
})
46+
);
47+
}
48+
} else if (target_node && 'href' in target_node) {
3349
const tr = svedit.session.tr;
3450
tr.set([target_node.id, 'href'], href_input_value);
3551
tr.set([target_node.id, 'target'], open_in_new_tab ? '_blank' : '_self');
@@ -42,6 +58,9 @@
4258
if (edit_link_command) {
4359
edit_link_command.show_prompt = false;
4460
}
61+
if (toggle_link_command) {
62+
toggle_link_command.show_prompt = false;
63+
}
4564
svedit.focus_canvas();
4665
}
4766
@@ -64,10 +83,13 @@
6483
}
6584
6685
$effect(() => {
67-
if (edit_link_command?.show_prompt && dialog_ref) {
68-
href_input_value = target_node?.href || '';
69-
open_in_new_tab = target_node?.target === '_blank';
70-
dialog_ref.showModal();
86+
if (is_open && dialog_ref) {
87+
href_input_value = is_creating ? 'https://' : target_node?.href || '';
88+
open_in_new_tab = is_creating ? false : target_node?.target === '_blank';
89+
90+
if (!dialog_ref.open) {
91+
dialog_ref.showModal();
92+
}
7193
7294
if (href_input_ref) {
7395
href_input_ref.focus();
@@ -81,28 +103,28 @@
81103

82104
<dialog
83105
bind:this={dialog_ref}
84-
class="edit-link-dialog absolute z-40 m-0 mt-1 max-h-90 overflow-visible border border-(--border) bg-(--background) p-0 text-(--foreground) shadow-xl"
85-
style="position-anchor: --{serialize_path(
86-
path
87-
)}; position-area: block-end span-all; justify-self: anchor-center;"
106+
class="edit-link-dialog absolute z-40 m-0 mt-3 max-h-90 overflow-hidden rounded-[23px] border border-(--border) bg-(--background) p-0 text-(--foreground) shadow-[0_1px_2px_rgb(0_0_0/0.12),0_4px_16px_rgb(0_0_0/0.08)]"
107+
style="position-anchor: {anchor_name}; position-area: block-end span-all; justify-self: anchor-center;"
88108
onclick={handle_backdrop_click}
89109
>
90110
<div class="flex flex-col">
91111
<div class="px-1 pt-1">
92-
<div class="flex items-stretch">
112+
<div
113+
class="flex items-center overflow-hidden rounded-full border border-(--border) focus-within:border-(--svedit-editing-stroke)"
114+
>
93115
<input
94116
id="edit-link-url-input"
95117
bind:this={href_input_ref}
96118
type="url"
97119
bind:value={href_input_value}
98120
placeholder="https://example.com"
99-
class="edit-link-input w-72 min-w-0 flex-1 border border-(--border) bg-(--background) px-3 py-2 text-sm text-(--foreground) focus:border-(--svedit-editing-stroke) focus:shadow-none focus:ring-0 focus:outline-none"
121+
class="edit-link-input w-72 min-w-0 flex-1 border-0 bg-(--background) px-3 py-2 text-sm text-(--foreground) focus:shadow-none focus:ring-0 focus:outline-none"
100122
onkeydown={handle_keydown}
101123
/>
102124
{#if app.has_backend}
103125
<button
104126
type="button"
105-
class="shrink-0 cursor-pointer border border-(--border) border-l-transparent px-3 text-(--svedit-editing-stroke) hover:bg-(--muted) focus:border-(--svedit-editing-stroke) focus:shadow-none focus:ring-0 focus:outline-none"
127+
class="mr-0.5 flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full border-0 bg-transparent p-0 text-(--foreground) outline-none transition-all duration-150 hover:bg-(--muted) focus-visible:shadow-[inset_0_0_0_1px_var(--svedit-editing-stroke)] active:translate-y-px active:scale-95 active:bg-(--muted)"
106128
title="Select page"
107129
aria-label="Select page"
108130
onclick={() => {
@@ -114,36 +136,36 @@
114136
}}
115137
>
116138
<svg
117-
class="size-4"
139+
class="size-6"
118140
xmlns="http://www.w3.org/2000/svg"
119-
viewBox="0 0 15 15"
141+
viewBox="0 0 24 24"
120142
fill="none"
121143
aria-hidden="true"
122144
>
123-
<rect x="1.5" y="1.5" width="5" height="5" rx="0.5" stroke="currentColor" />
124-
<rect x="8.5" y="1.5" width="5" height="5" rx="0.5" stroke="currentColor" />
125-
<rect x="1.5" y="8.5" width="5" height="5" rx="0.5" stroke="currentColor" />
126-
<rect x="8.5" y="8.5" width="5" height="5" rx="0.5" stroke="currentColor" />
145+
<rect x="4.5" y="4.5" width="6" height="6" rx="1" stroke="currentColor" />
146+
<rect x="13.5" y="4.5" width="6" height="6" rx="1" stroke="currentColor" />
147+
<rect x="4.5" y="13.5" width="6" height="6" rx="1" stroke="currentColor" />
148+
<rect x="13.5" y="13.5" width="6" height="6" rx="1" stroke="currentColor" />
127149
</svg>
128150
</button>
129151
{/if}
130152
</div>
131153
</div>
132-
<div class="flex items-center justify-between px-3 py-2">
154+
<div class="flex items-center justify-between p-1 pl-4">
133155
<label class="flex cursor-pointer items-center gap-2">
134156
<input
135157
type="checkbox"
136158
bind:checked={open_in_new_tab}
137-
class="h-4 w-4 cursor-pointer text-(--svedit-editing-stroke) ring-0 focus:ring-0 focus-visible:ring-0 focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-(--svedit-editing-stroke)"
159+
class="h-4 w-4 cursor-pointer rounded-full !border-(--border) !bg-(--muted) text-(--svedit-editing-stroke) ring-0 checked:!border-transparent checked:!bg-(--svedit-editing-stroke) focus:ring-0 focus:ring-offset-0 focus:shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-(--svedit-editing-stroke)"
138160
/>
139161
<span class="text-sm text-(--foreground)">Open in new tab</span>
140162
</label>
141163
<button
142164
type="button"
143-
class="shrink-0 cursor-pointer text-sm text-(--svedit-editing-stroke) outline-1 outline-transparent hover:opacity-80 focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-(--svedit-editing-stroke)"
165+
class="inline-flex h-9 shrink-0 cursor-pointer items-center justify-center rounded-full border-0 bg-transparent px-3 text-sm font-medium text-(--svedit-editing-stroke) outline-1 outline-transparent hover:bg-(--svedit-editing-fill) focus-visible:outline-1 focus-visible:outline-offset-1 focus-visible:outline-(--svedit-editing-stroke)"
144166
onclick={save}
145167
>
146-
UPDATE
168+
{is_new_link ? 'Create' : 'Update'}
147169
</button>
148170
</div>
149171
</div>

0 commit comments

Comments
 (0)