Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2f39419
fix(library): keep floating editor surfaces visible in fullscreen
FelixTJDietrich Jul 31, 2026
15a30ca
fix(library): make assessment interactions reliable
FelixTJDietrich Aug 6, 2026
86b3de9
fix(library): scope keyboard shortcuts to the focused editor
FelixTJDietrich Aug 6, 2026
1d825bb
feat(library): add a labeled chrome action for host controls
FelixTJDietrich Aug 6, 2026
4d43c06
fix(library): contain the offscreen export renderer
FelixTJDietrich Aug 6, 2026
4206841
fix(library): measure placed chrome controls at their settled size
FelixTJDietrich Aug 6, 2026
2687217
fix(library): keep every surface off stale rasters, and unify highlig…
FelixTJDietrich Aug 6, 2026
7481597
fix(library): hand the wheel back to the page when scroll lock is on
FelixTJDietrich Aug 7, 2026
0f44ccc
fix(library): stop the canvas grid landing a pixel off the snap step
FelixTJDietrich Aug 11, 2026
4478948
fix(library): keep the canvas grid off half pixels so Safari draws it
FelixTJDietrich Aug 11, 2026
c2715f2
fix(library): centre every diagram label, and name the note element p…
FelixTJDietrich Aug 11, 2026
6b025c5
fix(library): reveal resize handles on selection and give them a real…
FelixTJDietrich Aug 11, 2026
93017eb
perf(library): stop re-rendering the canvas on every frame of a zoom
FelixTJDietrich Aug 11, 2026
1d2d4e0
fix(library): mark elements amber only where feedback is being given
FelixTJDietrich Aug 11, 2026
28bf787
fix(library): let a tutor step through elements that are not assessed…
FelixTJDietrich Aug 11, 2026
93d74b7
fix(library): draw the palette drag ghost sharp instead of scaling it up
FelixTJDietrich Aug 11, 2026
3824ade
perf(library): mount only the connection handles a node actually needs
FelixTJDietrich Aug 11, 2026
e5bcdbd
perf(library): take idle connection arcs out of layout and paint
FelixTJDietrich Aug 11, 2026
101277b
chore(library): cut comment and test bloat from the integration work
FelixTJDietrich Aug 11, 2026
4a2bc5b
fix(library): restore connection targeting on a loaded diagram
FelixTJDietrich Aug 11, 2026
2def791
refactor(library): one helper for the on-screen affordance scale
FelixTJDietrich Aug 11, 2026
9b582e6
fix(library): anchor the drag ghost and the dropped node to one point
FelixTJDietrich Aug 11, 2026
6408a7e
chore(library): add changesets for the two integration fixes
FelixTJDietrich Aug 11, 2026
a7f93f4
fix(library): repair the regressions this branch left in the e2e suite
FelixTJDietrich Aug 13, 2026
9b2f193
chore(library): cut comment bloat, and fix what the comments were hiding
FelixTJDietrich Aug 13, 2026
0354e28
chore(library): add changesets for the two behavioural repairs
FelixTJDietrich Aug 13, 2026
9bae14b
chore(webapp): account for integration UI in CSS budget
FelixTJDietrich Aug 19, 2026
9d364e7
chore(visual): refresh Playwright baselines
github-actions[bot] Aug 19, 2026
e2f0d76
chore(ci): validate refreshed visual baselines
FelixTJDietrich Aug 19, 2026
c5d808e
perf(library): keep route previews within frame budget
FelixTJDietrich Aug 20, 2026
bfedd52
test(webapp): preserve routing perf workload
FelixTJDietrich Aug 20, 2026
73b3a7a
test(webapp): benchmark visible routing interactions
FelixTJDietrich Aug 20, 2026
32c92dc
test(webapp): establish visible Firefox baseline
FelixTJDietrich Aug 20, 2026
7c920e6
feat(webapp): add playground fullscreen control
FelixTJDietrich Aug 20, 2026
9719e6d
chore(release): consolidate integration changesets
FelixTJDietrich Aug 20, 2026
1b6dd5f
fix(release): align library release note wording
FelixTJDietrich Aug 20, 2026
5684407
fix(library): restore assessment node highlights
FelixTJDietrich Aug 20, 2026
67cd8f7
fix(library): keep revealed selection state consistent
FelixTJDietrich Aug 20, 2026
63aa5d1
fix(library): keep assessment selection local
FelixTJDietrich Aug 20, 2026
70e9eb2
refactor(library): centralize local selection updates
FelixTJDietrich Aug 20, 2026
5e0644b
fix(library): guard read-only assessment popovers
FelixTJDietrich Aug 20, 2026
2c2e8c2
perf(library): keep element callbacks stable
FelixTJDietrich Aug 20, 2026
26a33bb
fix(library): guard read-only assessment reveals
FelixTJDietrich Aug 20, 2026
43e42a8
fix(library): preserve compatibility and drag anchors
FelixTJDietrich Aug 20, 2026
e4a8cc6
fix(library): reveal nested assessments accurately
FelixTJDietrich Aug 20, 2026
900edf9
fix(library): reveal routed assessments accurately
FelixTJDietrich Aug 20, 2026
3fd9f18
fix(library): keep exported labels vertically centred
FelixTJDietrich Aug 20, 2026
6a49d57
fix(library): keep export normalization within budget
FelixTJDietrich Aug 20, 2026
68d755b
chore(visual): refresh Playwright baselines
github-actions[bot] Aug 20, 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
5 changes: 5 additions & 0 deletions .changeset/free-squids-like.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tumaet/webapp": minor
---

Test embedded-editor overlays and drag interactions directly in the playground with its new fullscreen workspace action.
6 changes: 6 additions & 0 deletions .changeset/loose-hounds-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@tumaet/apollon": minor
---

Embed Apollon reliably in host pages: fullscreen overlays stay visible, scrolling and shortcuts remain host-safe, assessment feedback is easier to open and navigate through `revealAssessment(id)`, host controls gain a labeled chrome action, exports no longer expand the page, and diagram interactions remain aligned, sharp, and responsive.
Labels now also stay vertically centered in SVG, PNG, and PDF exports exactly as they appear in the editor.
34 changes: 20 additions & 14 deletions docs/library/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,14 @@ regions, the `<ApollonControl>` React component, and the "make room" model.

### Assessment

| Member | Type | Purpose |
| ----------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addOrUpdateAssessment(assessment)` | `(Assessment) => void` | Attach or update a score/feedback assessment on an element. |
| `setElementHighlights(highlights)` | `(Map<string, string> \| Record<string, string> \| null) => void` | Paint a translucent highlight overlay over the given element ids (id → CSS color) — e.g. to flag elements missing feedback or carrying suggestions. Host-driven and ephemeral: never written to the model, serialized, or shared with collaborators. Each call replaces the previous set; pass `null` or an empty map to clear. |
| `getElementHighlights()` | `() => Record<string, string>` | The current highlight map (element id → CSS color). |
| `getElementIdsByTag(tag)` | `(string) => string[]` | Ids of every element carrying the host-defined `tag` — a node, or one of its members (class attribute, method, SFC action row). Exact and case-sensitive apart from surrounding whitespace; `[]` for an unknown or blank tag. Pair with `setElementHighlights` to color a group — see [Element tags](/library/api/element-tags). |
| `getInteractiveForSerialization()` | `InteractiveElements \| undefined` | Interactive-element flags for inclusion in a saved model. |
| Member | Type | Purpose |
| ----------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addOrUpdateAssessment(assessment)` | `(Assessment) => void` | Attach or update a score/feedback assessment on an element. |
| `setElementHighlights(highlights)` | `(Map<string, string> \| Record<string, string> \| null) => void` | Ring the given element ids (id → CSS color) — e.g. to flag elements missing feedback or carrying suggestions. Drawn as an outline, never a fill, so an element's own text and its assessment badge stay legible. Host-driven and ephemeral: never written to the model, serialized, or shared with collaborators. Each call replaces the previous set; pass `null` or an empty map to clear. |
| `revealAssessment(id, options?)` | `(string \| null, { reveal?: boolean }) => void` | Select one element, open its feedback popover, and pan the canvas to it at the current zoom. Lets a host's feedback list drive the canvas — click an entry, the diagram answers where it applies. `null` clears the selection and closes the popover; `{ reveal: false }` skips the pan. The popover only opens in assessment mode. |
| `getElementHighlights()` | `() => Record<string, string>` | The current highlight map (element id → CSS color). |
| `getElementIdsByTag(tag)` | `(string) => string[]` | Ids of every element carrying the host-defined `tag` — a node, or one of its members (class attribute, method, SFC action row). Exact and case-sensitive apart from surrounding whitespace; `[]` for an unknown or blank tag. Pair with `setElementHighlights` to color a group — see [Element tags](/library/api/element-tags). |
| `getInteractiveForSerialization()` | `InteractiveElements \| undefined` | Interactive-element flags for inclusion in a saved model. |

## Subscriptions

Expand Down Expand Up @@ -267,7 +268,11 @@ SVG/PNG/PDF over HTTP via the standalone server.

`Mod` is Ctrl on Windows/Linux and Cmd on macOS; combos marked _view_ work on
read-only diagrams too. Nothing fires while the user is typing in a field or
while a dialog or menu is open.
while a dialog or menu is open. Shortcuts belong to the editor that has focus:
click or tap the canvas to activate it, then move focus outside the editor to
return every shortcut to the surrounding page. Pointer-acquired canvas focus is
released when the pointer leaves, so browser zoom remains available around an
embedded editor. Keyboard users keep ownership until they move focus normally.

| Combo | Action |
| ------------------------------ | ---------------------------------------- |
Expand All @@ -286,17 +291,18 @@ whose keys produce a printable character fails
[WCAG 2.1.4](https://www.w3.org/WAI/WCAG21/Understanding/character-key-shortcuts)
unless it can be turned off, remapped, or scoped to focus.

Pass `keyboardShortcuts: false` to keep the editor's hands off every key above
for a host that binds them itself, or that mounts more than one editor (they
listen on `document`, so two would both answer).
Pass `keyboardShortcuts: false` to keep the editor's hands off every key above
when the host binds them itself. Multiple editors need no special coordination:
only the focused editor answers.

`APOLLON_SHORTCUTS` is the list the editor runs, so a host can render a sheet
that tracks it, or check it before binding a key of its own. Each entry's
**first** combo is the primary one — a sheet should render only that; the rest
are aliases (`Mod+Y` redo, layout variants of `Mod+=`). Entries flagged
`canvasHandled` are React Flow's, not the editor's own handler. `shortcutKeyName`
turns a combo into the key it names, so a sheet renders "1" rather than the
`Digit1` code that combo matches on.
`canvasHandled` are handled directly by React Flow on a focused canvas element,
not by the editor-root dispatcher. `shortcutKeyName` turns a combo into
the key it names, so a sheet renders "1" rather than the `Digit1` code that combo
matches on.

`matchesShortcutCombo`, `isTypingTarget` and `isInsideOverlay` are the
primitives that handler matches and stands down with, exported so a host's own
Expand Down
48 changes: 43 additions & 5 deletions docs/library/api/assessment.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,49 @@ const all: Record<string, Assessment> = editor.model.assessments
| `labelColor` | `string?` | CSS color for the label. |
| `correctionStatus` | `{ status: "CORRECT" \| "INCORRECT" \| "NOT_VALIDATED"; description?: string }?` | For automated/suggested feedback review. |

## Drive the canvas from a feedback list

A host that lists feedback beside the diagram has a problem the list alone
cannot solve: an entry says _what_ a tutor wrote, never _where_ it applies. The
reader is left matching text against boxes by eye.

`revealAssessment` closes that loop. It selects the element, opens its feedback
popover, and pans the canvas to it — keeping the reader's zoom, because the zoom
is theirs.

```ts
import { ApollonEditor } from "@tumaet/apollon"

function showFeedbackFor(
editor: ApollonEditor,
feedback: { elementId: string }
) {
editor.revealAssessment(feedback.elementId)
}

// Closing the list, or deselecting, puts the canvas back:
function clearFeedback(editor: ApollonEditor) {
editor.revealAssessment(null)
}
```

Pass `{ reveal: false }` to select without panning — useful when the element is
already on screen and moving the canvas would be disorienting.

Pair it with `subscribeToAssessmentSelection` for the other direction, so
clicking an element marks its entry in the list. Together the two halves explain
each other instead of sitting side by side.

The popover only opens in `ApollonMode.Assessment`. In assessment **with**
`readonly` — a student reading a graded diagram — it opens the read-only
feedback popover; without `readonly`, the tutor's editable one.

## Highlight elements

`setElementHighlights` tints elements by id with a CSS color — e.g. to mark the
elements that still need feedback. Use **translucent** colors so the element's
own text stays readable. Highlights are ephemeral: they are not serialized into
`setElementHighlights` rings elements by id with a CSS color — e.g. to mark the
elements that still need feedback. It draws an outline rather than a fill, so use
**opaque** colors: the element's own content, including its assessment badge,
stays fully visible underneath. Highlights are ephemeral: they are not serialized into
the model and not shared over collaboration. Pass `null` or an empty map to clear.

To color a whole **group** of attributes/methods at once — e.g. by a build
Expand All @@ -93,8 +131,8 @@ import { ApollonEditor } from "@tumaet/apollon"
function highlightMissing(editor: ApollonEditor) {
editor.setElementHighlights(
new Map([
["node-2", "rgba(0, 123, 255, 0.35)"],
["node-5", "rgba(0, 123, 255, 0.35)"],
["node-2", "#0d6efd"],
["node-5", "#0d6efd"],
])
)
}
Expand Down
24 changes: 24 additions & 0 deletions docs/library/api/overlay-controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,30 @@ function HostRail({ editor }) {
}
```

Host controls can use the same framework-agnostic chrome classes as Apollon's
built-ins:

```html no-check
<div
class="apollon-glass apollon-chrome-cluster"
role="group"
aria-label="Editor actions"
>
<button class="apollon-chrome-iconbtn" type="button" aria-label="Undo">
<!-- icon -->
</button>
<button class="apollon-chrome-actionbtn" type="button">
<!-- icon -->
<span>Fullscreen</span>
</button>
</div>
```

Use `.apollon-chrome-iconbtn` only when the icon has an unambiguous accessible
name. Use `.apollon-chrome-actionbtn` when a visible label makes a host action
clearer. Both use neutral hover/active layers and the same 32px chrome geometry;
reserve `.apollon-chrome-accent-btn` for a genuine primary action.

`getRegionElement` registers a host control under the reserved id
`apollon:host:<region>` with `inset: "auto"`, so the diagram makes room for
whatever you mount. The host mount is pointer-transparent by default so empty
Expand Down
40 changes: 21 additions & 19 deletions docs/library/theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,25 +156,27 @@ drop shadow, and a few state tints. They are **not** on `ApollonTheme` (the type
surface stays focused on color), but the names are part of the public contract —
set them directly through `style` or a stylesheet rule.

| CSS variable | Used for |
| ------------------------------------ | ------------------------------------------------------------------------------------ |
| `--apollon-radius-sm` | Small radius — node hover/selection rings, chips, pills (default `4px`). |
| `--apollon-radius-md` | Control radius — inputs, toggles. Routes through `--apollon-radius` (default `6px`). |
| `--apollon-radius-lg` | Panel / popover / menu radius (default `8px`). |
| `--apollon-shadow` | Drop shadow for floating surfaces — menus, popovers, select listboxes. |
| `--apollon-interactive-selection` | Accent ring/fill marking interactive (quiz-pickable) elements (default amber). |
| `--apollon-hover-neutral` | Neutral hover wash for quiet controls, derived off `--apollon-foreground`. |
| `--apollon-dropzone-accent` | Ring/stroke shown on an assessment feedback drop target on hover (default blue). |
| `--apollon-dropzone-accent-fill` | Translucent fill (40% of `--apollon-dropzone-accent`) outlining box/div targets. |
| `--apollon-on-collaboration-cursor` | Ink drawn on a collaborator's colored cursor/avatar (default white). |
| `--apollon-assessment-positive-text` | Glyph/text for a positive (rewarded) score — popover pill AND on-canvas badge. |
| `--apollon-assessment-positive-bg` | Soft tint behind a positive score (popover pill + canvas badge). |
| `--apollon-assessment-negative-text` | Glyph/text for a negative (penalty) score. |
| `--apollon-assessment-negative-bg` | Soft tint behind a negative score. |
| `--apollon-assessment-zero-text` | Glyph/text for a zero score. |
| `--apollon-assessment-zero-bg` | Soft tint behind a zero score. |
| `--apollon-assessment-ungraded-text` | Text for the "Not graded" pill (popover only — ungraded elements get no badge). |
| `--apollon-assessment-ungraded-bg` | Soft tint behind the "Not graded" pill. |
| CSS variable | Used for |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------- |
| `--apollon-radius-sm` | Small radius — node hover/selection rings, chips, pills (default `4px`). |
| `--apollon-radius-md` | Control radius — inputs, toggles. Routes through `--apollon-radius` (default `6px`). |
| `--apollon-radius-lg` | Panel / popover / menu radius (default `8px`). |
| `--apollon-shadow` | Drop shadow for floating surfaces — menus, popovers, select listboxes. |
| `--apollon-interactive-selection` | Accent ring/fill marking highlighted elements — the picker and assessment (default amber). |
| `--apollon-scroll-lock-veil` | Veil over the canvas while a scroll-locked editor explains the zoom gesture. Darkens in both themes. |
| `--apollon-scroll-lock-text` | Ink for that message, paired with the veil above. |
| `--apollon-hover-neutral` | Neutral hover wash for quiet controls, derived off `--apollon-foreground`. |
| `--apollon-dropzone-accent` | Ring/stroke shown on an assessment feedback drop target on hover (default blue). |
| `--apollon-dropzone-accent-fill` | Translucent fill (40% of `--apollon-dropzone-accent`) outlining box/div targets. |
| `--apollon-on-collaboration-cursor` | Ink drawn on a collaborator's colored cursor/avatar (default white). |
| `--apollon-assessment-positive-text` | Glyph/text for a positive (rewarded) score — popover pill AND on-canvas badge. |
| `--apollon-assessment-positive-bg` | Soft tint behind a positive score (popover pill + canvas badge). |
| `--apollon-assessment-negative-text` | Glyph/text for a negative (penalty) score. |
| `--apollon-assessment-negative-bg` | Soft tint behind a negative score. |
| `--apollon-assessment-zero-text` | Glyph/text for a zero score. |
| `--apollon-assessment-zero-bg` | Soft tint behind a zero score. |
| `--apollon-assessment-ungraded-text` | Text for the "Not graded" pill (popover only — ungraded elements get no badge). |
| `--apollon-assessment-ungraded-bg` | Soft tint behind the "Not graded" pill. |

```css
/* Sharpen the editor's corners and lift its menus a touch more. */
Expand Down
Loading
Loading