mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
A copy text had three override subgroups: its letters, any style, and its structure (the number of paragraphs or runs). One style override kept every other style of the main out (F72), letters and a style together kept everything out (F73), a structure change kept the main's styles out unless both texts had one style (F74), which the editors' content never had (F79), and alignment, direction and vertical alignment overrides were lost at the next sync (F76). Now a copy text has :text-content-text for its letters and line breaks and one group per style unit: family, weight (with style and variant), size, line height, spacing, decoration, case, fills, typography, alignment, direction and vertical alignment (cttx/style-units). An edit sets the groups of what it changes: a letter both texts share that changes its style, or a style that was not in the text before, so typed letters touch no style. A token in the content sets the groups of the styles it sets, and token sync of a text follows them. Sync and the keep-touched variant switch share one merge (cttx/merge-overrides): the letters of one side, and each unit from its side placed on them. A unit with one value applies everywhere; else letters both texts share (stretches of three or more) take the source's style, and the rest follow the letter before them. The switch carries an override only when the old and new mains agree on it (F77, F81), and the copy then holds the groups its result differs in. A typography reference stays only when every unit it sets comes from its side. The variant id goes with the weight; where the family comes from the other side, the frontend has to pick the family's closest variant. app.common.types.text reads a content as letters, each with its run and paragraph, and builds the content back from them; it gives the style of one unit for a letter, compares styles by what they show, and finds the letters two texts share with a letter diff (Myers). The component contract memory describes the model. Files older than this keep :text-content-attribute and :text-content-structure, read as every group they may cover until the migration recomputes them. Tests whose expected values the model changes: text-sync-test (the copy's weight, letters or new run no longer keep the main's size out; the letters read whole), text-touched-test (its edits went to the paragraph node, which shows nothing; they now edit the run), variants-switch-test (the copies with two runs keep their letters across a switch, and the size override the mains agree on), the editor output table, case Y and the notation test (new groups). The 1j pins of F72-F79 and F81 go; F79's direct test of equal-attrs? becomes a sync of editor content. A new check holds the production override rule to the 1j reference rule. New tests: the letter view, and a detached, duplicated or reset copy text keeping or clearing its override groups. Benchmark (lower quantile, ms; baseline / now / ratio): deepest-edit-through-three-levels 35.2 / 36.6 / 1.04 edit-through-a-chain-of-dependents 68.9 / 67.2 / 0.98 library-update-into-copies 83.1 / 72.5 / 0.87 main-edit-into-10-copies 1.8 / 1.5 / 0.82 main-edit-into-100-copies 15.4 / 14.9 / 0.97 main-edit-into-1000-copies 153.1 / 153.4 / 1.00 variant-switch-into-copies 32.1 / 27.1 / 0.85 The worlds hold no text. A main text edit synced into 100 copy texts that changed their letters takes 40 ms, against 29 ms before: the merge aligns the letters of each copy with the main's. AI-assisted-by: claude-opus-5-5