diff --git a/src/App.svelte b/src/App.svelte
index 50e502e..427360e 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -99,6 +99,10 @@
// set on open, editable in the header, blank → heading-derived fallback.
let documentName: string = $state(loadDocName());
+ // Width of the hidden mirror span (below), so the title input grows/shrinks
+ // with its text instead of sitting in a fixed-width box.
+ let docNameSizerWidth = $state(0);
+
// Shown in the empty title field: what an actual save would name the file.
// Only computed while the field is blank (otherwise the placeholder is hidden,
// so we skip the per-transaction getJSON).
@@ -625,23 +629,29 @@
{/snippet}
-
+
@@ -1076,14 +1086,18 @@
gap: 2px;
}
- /* Editable document title (Google-Docs style): borderless at rest, subtle
- frame on hover/focus. Drives the suggested save filename. */
+ /* Editable document title: borderless at rest, text auto-sizes to its
+ content (via the hidden .doc-name-sizer mirror), an animated underline
+ grows in on focus, and a pencil hint fades in on hover. Drives the
+ suggested save filename. */
.doc-name {
+ position: relative;
display: inline-flex;
align-items: center;
- gap: 0.35rem;
- padding: 0 0.25rem 0 0.4rem;
+ gap: 0.3rem;
+ padding: 0 0.4rem;
margin-right: 0.25rem;
+ height: var(--toolbar-btn-size);
border-radius: var(--radius);
color: var(--color-text-muted);
transition: background 0.15s;
@@ -1094,18 +1108,38 @@
background: var(--color-btn-hover);
}
+ .doc-name-icon {
+ flex-shrink: 0;
+ transition: color 0.15s;
+ }
+
+ .doc-name:focus-within .doc-name-icon {
+ color: var(--color-primary);
+ }
+
+ /* Invisible twin of the input's text; its measured width drives the
+ input's width so the field grows/shrinks like a title, not a box. */
+ .doc-name-sizer {
+ position: absolute;
+ visibility: hidden;
+ white-space: pre;
+ font-family: var(--font-sans);
+ font-size: 0.85rem;
+ }
+
.doc-name-input {
- width: 13rem;
- height: var(--toolbar-btn-size);
- padding: 0 0.25rem;
- border: 1px solid transparent;
- border-radius: var(--radius);
+ min-width: 3ch;
+ max-width: 15rem;
+ height: 100%;
+ padding: 0 1px;
+ border: none;
+ border-bottom: 1.5px solid transparent;
background: transparent;
color: var(--color-text);
font-family: var(--font-sans);
font-size: 0.85rem;
outline: none;
- transition: border-color 0.15s, background 0.15s;
+ transition: border-color 0.15s ease;
}
.doc-name-input::placeholder {
@@ -1114,8 +1148,33 @@
}
.doc-name-input:focus {
- border-color: var(--color-border);
- background: var(--color-surface);
+ border-bottom-color: var(--color-primary);
+ }
+
+ .doc-name-ext {
+ flex-shrink: 0;
+ font-size: 0.72rem;
+ letter-spacing: 0.01em;
+ color: var(--color-text-muted);
+ opacity: 0.7;
+ }
+
+ .doc-name:not(.has-value) .doc-name-ext {
+ display: none;
+ }
+
+ .doc-name-pencil {
+ flex-shrink: 0;
+ color: var(--color-text-muted);
+ opacity: 0;
+ transform: translateX(-3px);
+ transition: opacity 0.15s ease, transform 0.15s ease;
+ }
+
+ .doc-name:hover .doc-name-pencil,
+ .doc-name:focus-within .doc-name-pencil {
+ opacity: 1;
+ transform: none;
}
.action-separator {