Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8203b33
Toolbar redesign with new icons
michael Jul 23, 2026
e947195
Update Toolbar.svelte
michael Jul 23, 2026
7f2cfb7
Update Toolbar.svelte
michael Jul 23, 2026
e567c1c
Improve toolbar
michael Jul 23, 2026
adae46a
Improvements
michael Jul 23, 2026
46f0263
Update Toolbar.svelte
michael Jul 23, 2026
dc2118e
Update Toolbar.svelte
michael Jul 23, 2026
a0b5c9b
Update Toolbar.svelte
michael Jul 23, 2026
1af2b28
Improved icons
michael Jul 23, 2026
f9d94ae
Better alt icon
michael Jul 23, 2026
fd89671
Ellipsis menu for logout and page actions
michael Jul 23, 2026
2785f39
Improved icons for browse and edit
michael Jul 23, 2026
0fc6403
Consistent tool styles
michael Jul 23, 2026
79fb276
Pill-shaped EditLink and LinkPreview
michael Jul 24, 2026
868f3f9
Consolidate CreateLink and EditLink into one
michael Jul 24, 2026
3cdfa45
Create label also for button links
michael Jul 24, 2026
8cf92b2
More padding for variant selector
michael Jul 24, 2026
5975cd0
Consistent styles for EditMedia
michael Jul 24, 2026
261abb9
Consistent style for "Create Link" button
michael Jul 24, 2026
52b4f40
Improve styles of browse button in EditLink
michael Jul 24, 2026
6f7fdd7
Fix margin
michael Jul 24, 2026
d8a94c6
Icons for save/cancel on mobile
michael Jul 24, 2026
f962f5a
Improve PageBrowser styles
michael Jul 24, 2026
8d65dfc
Circular preview image
michael Jul 24, 2026
5a78195
Try medium font weight for display text styles
michael Jul 24, 2026
96d79af
A more stylish page browser
michael Jul 24, 2026
073824f
Lean internal link preview
michael Jul 24, 2026
0044d7e
Tweaks
michael Jul 24, 2026
630de5f
Use larger text in variant selector on desktop
michael Jul 24, 2026
05ff6b5
Polish EditLink dialog
michael Jul 24, 2026
4c58ba8
More spacing for the page metadata
michael Jul 24, 2026
ce8657b
Better UNLISTED label
michael Jul 24, 2026
cdca534
Improve checkbox style
michael Jul 24, 2026
959d2df
Render LinkPreview/EditLink a bit further away from the anchor
michael Jul 24, 2026
1d9c259
Style fixes
michael Jul 24, 2026
7e21260
Update EditMedia.svelte
michael Jul 24, 2026
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
4 changes: 4 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,10 @@ This means the page browser is not a full graph visualization. It is a stable, e

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.

### Internal link previews

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.

### Contextual search in the page browser

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.
Expand Down
25 changes: 24 additions & 1 deletion IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,34 @@ Convert the whole codebase from JS+JSDoc to TypeScript per the "Language: TypeSc
- 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.
- 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.
- Keep the ordinary select-parent command in the existing selection tool group; do not duplicate it inside the variant selector.
- Hide the select-parent tool when it is disabled, and draw its trailing separator only when a variant selector is visible beside it.
- 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.
- On mobile, center the toolbar and let it shrink-wrap its contents until it reaches the viewport-safe maximum width.
- 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.
- 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.
- Keep variant labels on one line at their intrinsic width so the scroll area, rather than the selector itself, absorbs narrow-screen overflow.
- Keep type/layout cycle commands and keyboard shortcuts registered, but hide their redundant toolbar buttons while the variant selector is the primary visible UI.
- Expose the type up/down and layout left/right keyboard shortcuts in the variant selector's hover tooltip rather than as persistent UI chrome.
- 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.
- Do not require a parallel variant-label registry; new schema types and layouts should appear automatically.

## Page browser visual alignment

- 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.
- 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.
- 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.
- On hover-capable devices, use `--background` behind the ellipsis trigger and unlisted marker so they remain distinct from row hover and selection fills.
- 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.
- 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.
- Style the unlisted marker like the toolbar ALT control, while preserving its explanatory button behavior.

## Internal link preview simplification

- Keep external link previews unchanged as a single href row with edit and remove actions.
- 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.
- Make the preview image and title one link target, and omit the raw href from the preview because the link editor exposes it.
- Keep loading, missing-page, and error states inside the same compact row rather than adding a second row.

## Human-readable layout ids

- Replace numeric `layout` values in the schema, renderers, inserters, seed content, Markdown conversion, tests, and documentation with descriptive string ids.
Expand Down Expand Up @@ -478,7 +500,8 @@ Required UI behavior:
- drafts and private sitemap UI are hidden unless authenticated as admin
- link pickers must not expose drafts to unauthenticated users
- toolbar actions that require admin auth must be hidden or disabled when unauthenticated
- authenticated admins get an explicit logout button
- authenticated admins get an ellipsis page-actions menu in the browsing toolbar with Duplicate page, Edit URL, Delete page, and Logout entries
- 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

### Page browser behavior

Expand Down
10 changes: 5 additions & 5 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,27 @@

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

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

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

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

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

/* ParagraphSM, ListingItem.description, ... */
Expand Down
144 changes: 0 additions & 144 deletions src/routes/components/CreateLink.svelte

This file was deleted.

68 changes: 45 additions & 23 deletions src/routes/components/EditLink.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@
const app = get_app_context();
const page_browser = get_page_browser();

let { path }: { path: DocumentPath } = $props();
let { path }: { path?: DocumentPath } = $props();

let edit_link_command = $derived(svedit.session.commands?.edit_link);
let toggle_link_command = $derived(svedit.session.commands?.toggle_link);
let is_creating = $derived(toggle_link_command?.show_prompt === true);
let is_open = $derived(
edit_link_command?.show_prompt === true || toggle_link_command?.show_prompt === true
);
let anchor_name = $derived(path ? `--${serialize_path(path)}` : '--selection-highlight');
let target_node = $derived(get_target_node());
let is_new_link = $derived(is_creating || !target_node?.href);

function get_target_node() {
const selected_node = svedit.session.selected_node;
Expand All @@ -29,7 +36,16 @@
let dialog_ref = $state<HTMLDialogElement>();

function save() {
if (target_node && 'href' in target_node) {
if (is_creating) {
if (href_input_value) {
svedit.session.apply(
svedit.session.tr.toggle_mark('link', {
href: href_input_value,
target: open_in_new_tab ? '_blank' : '_self'
})
);
}
} else if (target_node && 'href' in target_node) {
const tr = svedit.session.tr;
tr.set([target_node.id, 'href'], href_input_value);
tr.set([target_node.id, 'target'], open_in_new_tab ? '_blank' : '_self');
Expand All @@ -42,6 +58,9 @@
if (edit_link_command) {
edit_link_command.show_prompt = false;
}
if (toggle_link_command) {
toggle_link_command.show_prompt = false;
}
svedit.focus_canvas();
}

Expand All @@ -64,10 +83,13 @@
}

$effect(() => {
if (edit_link_command?.show_prompt && dialog_ref) {
href_input_value = target_node?.href || '';
open_in_new_tab = target_node?.target === '_blank';
dialog_ref.showModal();
if (is_open && dialog_ref) {
href_input_value = is_creating ? 'https://' : target_node?.href || '';
open_in_new_tab = is_creating ? false : target_node?.target === '_blank';

if (!dialog_ref.open) {
dialog_ref.showModal();
}

if (href_input_ref) {
href_input_ref.focus();
Expand All @@ -81,28 +103,28 @@

<dialog
bind:this={dialog_ref}
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"
style="position-anchor: --{serialize_path(
path
)}; position-area: block-end span-all; justify-self: anchor-center;"
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)]"
style="position-anchor: {anchor_name}; position-area: block-end span-all; justify-self: anchor-center;"
onclick={handle_backdrop_click}
>
<div class="flex flex-col">
<div class="px-1 pt-1">
<div class="flex items-stretch">
<div
class="flex items-center overflow-hidden rounded-full border border-(--border) focus-within:border-(--svedit-editing-stroke)"
>
<input
id="edit-link-url-input"
bind:this={href_input_ref}
type="url"
bind:value={href_input_value}
placeholder="https://example.com"
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"
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"
onkeydown={handle_keydown}
/>
{#if app.has_backend}
<button
type="button"
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"
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)"
title="Select page"
aria-label="Select page"
onclick={() => {
Expand All @@ -114,36 +136,36 @@
}}
>
<svg
class="size-4"
class="size-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 15 15"
viewBox="0 0 24 24"
fill="none"
aria-hidden="true"
>
<rect x="1.5" y="1.5" width="5" height="5" rx="0.5" stroke="currentColor" />
<rect x="8.5" y="1.5" width="5" height="5" rx="0.5" stroke="currentColor" />
<rect x="1.5" y="8.5" width="5" height="5" rx="0.5" stroke="currentColor" />
<rect x="8.5" y="8.5" width="5" height="5" rx="0.5" stroke="currentColor" />
<rect x="4.5" y="4.5" width="6" height="6" rx="1" stroke="currentColor" />
<rect x="13.5" y="4.5" width="6" height="6" rx="1" stroke="currentColor" />
<rect x="4.5" y="13.5" width="6" height="6" rx="1" stroke="currentColor" />
<rect x="13.5" y="13.5" width="6" height="6" rx="1" stroke="currentColor" />
</svg>
</button>
{/if}
</div>
</div>
<div class="flex items-center justify-between px-3 py-2">
<div class="flex items-center justify-between p-1 pl-4">
<label class="flex cursor-pointer items-center gap-2">
<input
type="checkbox"
bind:checked={open_in_new_tab}
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)"
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)"
/>
<span class="text-sm text-(--foreground)">Open in new tab</span>
</label>
<button
type="button"
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)"
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)"
onclick={save}
>
UPDATE
{is_new_link ? 'Create' : 'Update'}
</button>
</div>
</div>
Expand Down
Loading