Commit Graph
627 Commits
Author SHA1 Message Date
Steffen Becker 6bbf4286c3 style: the floating-island chrome is dev-only in the menu
The appearance menu's "Modern (beta)" entry sits behind
import.meta.env.DEV, so a production build offers no way into the
island chrome until it catches up with the ribbon. The chrome itself
and a stored 'modern' pref keep working.
2026-08-20 14:56:48 +02:00
Steffen Becker a7522547b7 fix: the table dialog flips below a table it would cover
The formula/sort/split popover hangs above the table's top-left corner;
with the table at the top of the page the keep-in-view clamp slid it
down over the table itself. With no room above, the dialog now opens
below the table when that fits the viewport, and only a table filling
the whole window keeps the old clamp.
2026-08-20 14:37:18 +02:00
Steffen Becker de7f63f269 fix: Enter frees the caret from a leading table
At the very first position of a table with no block before it, Enter
inserts a paragraph above the table and moves the caret there, as both
word processors do — previously the caret could never get before such
a table. Generic first-child check, so it also covers a table leading
a cell, text box or columns section.
2026-08-20 14:26:57 +02:00
Steffen Becker 8537fd0106 fix: zooming the page grid keeps the view anchored
The grid snapped the scroll to the top of the first row on every zoom,
losing the reader's place whenever it sat mid-row and ignoring the
pointer anchor of a trackpad zoom. It now uses the same anchor logic as
a single pane, measured against the grid canvas: the pointer for a
wheel zoom, else the viewport top; firstRow is re-read after the write
so the page cells re-aim immediately. Measured drift < 1px across a
92% -> 194% wheel burst.
2026-08-20 12:26:03 +02:00
Steffen Becker fb59a30b63 fix: defer the toolbar tick past Svelte's flush
Tearing down the focused pane's DOM on a layout switch fires blur
synchronously inside Svelte's commit; TipTap answers with a focus-meta
transaction, and onTransaction's tick write threw state_unsafe_mutation
there — aborting the handler before the other panes got the new state.
The tick now bumps in a microtask, after the flush, before paint.
2026-08-20 12:25:27 +02:00
Steffen Becker c6aae13a9e fix: the page count survives switching the pane layout
The pm-pagecount listener sat on the hosts[0] element, which Svelte
replaces when the pane layout changes (multi-page grid, split view).
The editor view moves to the new host, the listener died with the old
one — numPages froze, so no new page ever appeared in the grid.
Listen on the view's own DOM instead, which is only ever re-parented.
2026-08-20 11:57:20 +02:00
Steffen Becker ab79740787 style: the inline-wrap icon shows the object in the line
Three bare lines said nothing and read the same at any size. It now draws
the box on the text baseline like the floating toolbar's glyph, with the
outlined object the other three wrap icons use.
2026-08-20 00:24:04 +02:00
Steffen Becker 2436deb3bf style: Shape Format follows the caret, and carries vertical text
The tab only showed while the box was node-selected, so it vanished the
moment you clicked in to type — the floating toolbar stayed. It now reads
findTextBox(), the same box that toolbar reads; an image selected inside a
box still shows Picture Format alone. Vertical text was the one control the
tab was missing, now a Text group of its own.
2026-08-20 00:23:46 +02:00
Steffen Becker 7b3348ca6e docs: add contributer list 2026-08-19 18:56:17 +02:00
Steffen Becker a87e7a4ada fix: a heading's style name never lands on a plain paragraph
styleName was kept on split, so Enter at the end of a heading left a paragraph
carrying it: drawn like a heading, absent from the navigator's outline, exported
as text:p. It is no longer kept there. setParagraphStyle also missed the name
whenever it switched the node type — a following updateAttributes looks for the
new type in the pre-chain state — so it now rides along with setNode. Levels 2–6
never reached that command at all: the digit class built from MAX_HEADING_LEVEL
(10) read as [0-1], so Heading's own Mod-Alt-N took the key and set a level with
no style; that keymap is dropped, running ahead of every plugin binding.
2026-08-19 18:41:21 +02:00
Steffen Becker 78b732ac44 update link to custom domain 2026-08-19 09:03:21 +02:00
Steffen Becker fda166103f ci: 20-minute job timeouts
A hung apt mirror on the runner otherwise holds a job until the
6-hour default limit; every job normally finishes in minutes.
2026-08-18 22:30:37 +02:00
Steffen Becker fc20e0a89c fix: improve saving where the File System Access API is unavailable
The fallback download shows a one-time hint about the browser's
ask-where-to-save setting, keeps its anchor in the DOM and the blob
URL alive past the click (Gecko). Failed saves and exports now report
the underlying error, with a clear message when a script blocker
prevented a lazy chunk from loading. A full or blocked localStorage
no longer fails the save when recording the recent-files entry.
2026-08-18 19:56:32 +02:00
Steffen Becker f1b63c722c feat: the build ships its dependency licenses and its commit
The minifier strips every legal comment, so the bundle reached users without
the notice MIT and Apache-2.0 require to travel with a copy. collect-licenses
quotes all 97 bundled packages into public/licenses.txt, which deploys like the
font and dictionary licenses do. The About dialog's version now carries the
short commit, so a user of the hosted app can identify the source it was built
from - what AGPL 13 asks for and a fixed 0.1.0 could not answer.
2026-08-18 16:44:42 +02:00
Steffen Becker 946b7e44be chore: package.json names the real repo, homepage and contact
author, homepage and repository.url were still the scaffolding placeholders.
2026-08-18 10:33:16 +02:00
Steffen Becker 7e0f9ca617 docs: the font licenses ship with the fonts, the notices name them all
public/ is copied verbatim into the build, src/assets/ is not - so the fonts
were deployed without the license texts that OFL-1.1 and Bitstream Vera require
to travel with them. Moving the texts to public/font-licenses/ fixes that with
no build step. The notices covered only Liberation and missed five bundled
dependencies; the commercial license now states what it cannot relicense, the
copyleft German dictionary and thesaurus above all.
2026-08-18 10:33:10 +02:00
Steffen Becker 2f9dd3cdf7 docs: the CLA asks for the phrase its bot actually accepts
The signing instructions named a phrase the workflow ignores, and told
contributors to put it in the PR description rather than a comment - following
them left a PR blocked forever. Both files now quote the action's own phrase.
Drops the note that CLA Assistant still had to be set up; it is running.
2026-08-18 10:33:03 +02:00
Steffen Becker 36ffd6e768 docs: CONTRIBUTING lists the test commands instead of denying a suite
The setup section still claimed no test suite existed. It now names check,
test, test:lo and test:smoke, and what CI runs of them; the linter note stays,
since none is configured.
2026-08-18 09:21:16 +02:00
Steffen Becker 1f039990d1 docs: the changelog names both chromes, the ribbon as default 2026-08-17 23:12:20 +02:00
Steffen Becker 810846b00c test: the smoke test exports its PDF through the ribbon's File menu
The save chevron it clicked lives in the modern island, which a fresh
profile no longer mounts since the ribbon became the default.
2026-08-17 23:10:08 +02:00
Steffen Becker 10b650c64c refactor: the floating island's mode id is 'modern', matching its label
The id said 'classic' while the UI called the ribbon Classic and the
island Modern (beta). Type, i18n keys, comments and docs now agree;
a stored legacy 'classic' still selects the island.
2026-08-17 20:05:15 +02:00
Steffen Becker 501bfe501f fix: a first visit opens the ribbon, not the floating island
The tabbed chrome is the layout users know; the stored choice still wins.
2026-08-17 20:03:06 +02:00
Steffen Becker 95530c6f07 docs: README pitch sharpened, beta status called out
Beta badge and note: actively developed, occasional bugs expected,
keep backups.
2026-08-17 19:43:12 +02:00
Steffen Becker 6bec446dfe ci: deploy to GitHub Pages on push to main 2026-08-17 18:59:48 +02:00
Steffen Becker 3ae8d27b26 fix: the build runs under a subpath, not only at the domain root
base './' emits relative asset URLs; the manifest, the service-worker
registration and sw.js's precache resolve against their own location;
the four hardcoded /image literals turn relative. Verified headless
under a simulated /edentext/ subpath: boots, types, SW scope correct.
2026-08-17 18:59:48 +02:00
Steffen Becker b37261375a docs: 0.1.0 release date moves to the Pages launch day 2026-08-17 18:59:48 +02:00
Steffen Becker c9ab7d7ab7 docs: public README
Centred logo header, badges, the live-app link, feature overview,
licensing and CLA pointers. README.md leaves .gitignore — it used to
be a private scratch file, now it is the repo's front page.
2026-08-17 18:59:48 +02:00
Steffen Becker 834ffda931 chore: CLA contact address, workflow URL follows the repo rename 2026-08-17 18:59:48 +02:00
Steffen Becker 9d3d120b57 feat: the About dialog pitches the app in two lines
Tagline plus one line of key points in both languages, rendered as a
centred list; the GitHub button points at the real repository.
2026-08-17 18:59:42 +02:00
Steffen Becker 04b2bba5db docs: a block ending at the page bottom can break on the other side
The fit test is a step function on a block chain that agrees with
LibreOffice to well under the harness's millimetre. Measured: a paragraph
whose flow box ends 0.62mm past the content end is pushed over, where the
reference keeps it, and the block behind it then draws its space-before in
full — 14.8mm on every line of that page.
A tolerance on the fit test was tried and reverted, corpus 1074 → 1085:
the error carries no sign, so the slack that fixes one fixture keeps two
lines another one's reference pushes.
2026-08-17 17:35:00 +02:00
Steffen Becker 6c96175182 docs: the frame limitation is wrapping around a free point, not placing one 2026-08-17 13:47:09 +02:00
Steffen Becker 847fe59d3d feat: a frame the text runs through reserves no space at all
Word's in-front-of / behind-text (wp:wrapNone + behindDoc) and ODF's
style:wrap="run-through" both read as a side float, so a near-column-wide
picture reserved its whole height and pushed the text 23-52mm down. They
are a wrap mode of their own now: out of flow at the static position it
was anchored at, z-order from the file (inFront). Both exports carry it
back; tests/unit/wrap-through.test.ts covers the round trip.
The image fixture 72 -> 38 over both formats and 7 pages -> 6, matching
the reference; corpus 1108 -> 1074, no other fixture moved.
2026-08-17 13:45:39 +02:00
Steffen Becker 9c4b768982 fix: a page is the paper's exact height, not the pixel it rounds to
The sheet boxes rounded cm × PX_PER_CM where --user-page-height keeps it
exact, and pageBreaks builds its grid from the rounded value they publish
as --pb-section-page: A4 came out 1123px against 1122.52, so every page
ran 0.127mm long and the text crept down the flow — 1.8mm by the
sixteenth page, 6.0mm by the forty-eighth, against a reference whose
footer sits at the same height on every page.
Corpus 1772 -> 1108, eight fixtures improved, none rose.
2026-08-17 13:27:46 +02:00
Steffen Becker b1b8b9de63 docs: the ODF import yardstick is the default style, and its spacing exception 2026-08-17 09:18:16 +02:00
Steffen Becker b3c52e53fc feat: an ODF paragraph reads the contextual spacing its style declares
style:contextual-spacing drops a paragraph's own spacing towards a neighbour of
the same style; LibreOffice's List styles carry it, and only the docx path read
its w:contextualSpacing counterpart. Unread, every list point kept the 8pt its
style declares and a long document accumulated a page every dozen items.

The editor has no such mode, so the suppressed side becomes an explicit 0. In a
list each item wraps its own paragraph, so the neighbour is the next item's.
2026-08-17 09:17:33 +02:00
Steffen Becker b65920f7b7 fix: a cell or list paragraph is measured against the default style
The ODF importer's yardstick for a block that can carry no style name was the
editor's own fallback, not the default paragraph style — which is what
`p:not([data-style])` re-applies. So a cell paragraph overriding its 115% line
height back to 100% was dropped as "same as the default" and rendered at 115%
anyway: 0.7mm too much on every table row. Its spacing is the one exception,
zeroed by a rule that outranks the style's, for a paragraph but not a heading.

Two fixtures' table rows: pitch 5.8mm against LibreOffice's 5.1, then 5.1.
2026-08-17 09:17:05 +02:00
Steffen Becker 7767a61b95 feat: the references tab groups its buttons the way both word processors do
Captions, index entries and citations each get their own group instead of
riding under the table-of-contents label, which said nothing about them.

The index options menu names the index it edits and lays its ten levels out
as one strip of keys — as rows they were the whole menu.
2026-08-17 00:31:27 +02:00
Steffen Becker a62fa11cc1 feat: the index options menu switches its page numbers
References ▸ Options, where the depth already lives: Word's "Show page numbers"
as a checkbox, over the pageNumbers attr both formats now carry. The menu is no
longer only levels, so its button says Options.
Its node view repainted on the entries alone, so nothing an attr changes ever
reached the rows — leader, title and level styles included. Keyed on the look
as well now, minus the cached entries the pass writes back itself.
2026-08-17 00:09:48 +02:00
Steffen Becker ac5b05e0fe feat: an index the file writes without page numbers keeps its rows text-only
Word's TOC \n and an ODF entry template naming no index-entry-page-number both
say the rows are text alone; we drew leaders and live numbers over them either
way. Carried as the node's own pageNumbers attr, off only — the node view had
the page-less row already, for the bibliography. The exports write the switch
back out: \n over the whole level range, and the ODF template drops the tab
stop with the number, which is the pair LibreOffice's dialog switches together.
2026-08-16 23:49:53 +02:00
Steffen Becker 8a9b391d76 fix: a heading keeps the bold its own run declares
Both importers drop a mark the block's style supplies, and a heading's bold was
taken as given. The registry style that renders it is the file's, though —
re-parented to Standard wherever the file declares a basedOn / parent-style-name
— so nothing supplied what the mark had been deleted for, and a 23pt title line
came out 149.7mm against LibreOffice's 161.7. Gated on that parent now; a
LibreOffice file, which writes fo:font-weight on every Heading_20_N, is
untouched. Parity corpus 1892 → 1889.
2026-08-16 23:12:34 +02:00
Steffen Becker eb3a9f95c1 feat: Word's horizontal line imports as the paragraph's rule
A v:rect flagged o:hr is the line itself, not a shape: its height and
fillcolor become the paragraph's borderBottom (1.5pt/#A0A0A0 where the file
names none), a real w:pBdr keeping precedence — only one line can be drawn.
It was dropped with the "Drawings were removed" warning until now, which the
changelog's gap list did not admit to either.
2026-08-16 22:44:40 +02:00
Steffen Becker 6c731b9233 feat: insert caption is in the picture and table tabs too
References keeps it, as in both products, but that is a tab away from the
object being captioned. The contextual tabs mount their own CaptionDialog —
it reads the caret for category and position, so the copies take no props.
2026-08-16 22:22:29 +02:00
Steffen Becker 53317bf88b i18n: the phonetic guide is called ruby text
Word and LibreOffice hide theirs unless an East Asian language is on; ours is
always in the Insert tab, so the label says what it inserts. The reading field
no longer repeats the dialog's own name.
2026-08-16 22:04:26 +02:00
Steffen Becker 14444b7169 i18n: a button that fills a library says Add, not Insert
Both the AutoText entry and the tab stop go into a list the user inserts from
later; only the cross-reference and ruby dialogs really write to the document.
2026-08-16 21:57:14 +02:00
Steffen Becker ad0df9df05 fix: the logo's stem is visible on every background
An <img> sees neither data-theme nor the document's color, so the ink stem and
its prefers-color-scheme flip left it dark on the dark chrome. One slate stem
instead: 5.1:1 on white, 3.5:1 on the dark surfaces.
2026-08-16 14:46:38 +02:00
Steffen Becker 383436439d i18n: the chromes are named Classic and Modern (beta)
The ribbon is the look users know as classic, the floating island the modern
one; beta because it has no entry point yet for comments, revisions, line
numbering or page design. Mode ids keep naming the shape.
2026-08-16 14:26:30 +02:00
Steffen Becker 2d3395137f style: the status bar follows the chrome above it
Under the ribbon it takes the same --w-* remap .ribbon gives its pickers: the
app's dark neutrals are bluer than Word's, so the two ends of the window did
not match. Classic keeps the themed set. --color-primary is the brand slate
now, so both chromes share one accent.
2026-08-16 14:19:17 +02:00
Steffen Becker b3e5a1c8a9 style: the ribbon takes the logo's colours
--w-accent is the logo's slate instead of Word's blue (5.1:1 on the chrome,
carries white), and the clay marks the contextual tabs — the pair meets where
those sit beside the normal ones. Both defined once from favicon.svg; the
label tone is darkened, the clay itself only 3.3:1 on white.
2026-08-16 14:12:30 +02:00
Steffen Becker 9b14d1654f feat: the style gallery lists heading levels 1–5 until asked for the rest
Both reference products define ten levels but only offer the first five;
visibleStyles() filters styleOrder for the gallery in either chrome, with a
session-only 'show all styles' toggle in the menu. A deeper level a block
already uses stays listed, so the caret always has an active style.
2026-08-16 14:01:30 +02:00
Steffen Becker bc98446198 docs: table-style import re-derives regions and unbakes the region text 2026-08-16 13:43:08 +02:00