29 Commits

Author SHA1 Message Date
c0dev0id
5bec1a1d1e PaneCP article label + PaneFO editable property rows
Article entries in the CP path list now show the article label
(letter/extension) instead of just "article", with a property count
in the meta column. In the FO pane, each property is rendered as an
editable row: key input, value input (coerced via ast-parser.coerce),
scope toggle, morphing default/overwrite label, and per-row remove
button. A "+ add property" button appends a blank defaults entry.
Every edit marks the score dirty.

`coerce` is now exported from ast-parser.js so the FO pane can apply
the same type-inference rules to user input as the parser does to
AST values.
2026-06-29 08:17:57 +02:00
c0dev0id
b822e4d919 Article toggle: single morphing label (default/overwrite) instead of two flanking labels 2026-06-28 21:39:33 +02:00
c0dev0id
a91b5747a3 Article scope: render an actual sliding toggle, not an ASCII-labelled button
Previous commit took the user's (O-) / (-O) shorthand literally and put
those glyphs as the button label. They were describing the visual states
of a real toggle switch, not text to display.

Now: pill-shaped track with a sliding circular thumb, "default" and
"overwrite" labels flanking it (the active side brightens). The button
carries role="switch" + aria-checked for accessibility. Click anywhere
on the track to flip and mark the score dirty.
2026-06-28 20:37:53 +02:00
c0dev0id
133117922a Articles: key by label across subtypes, scope per property, FO toggle widget
A single article label (e.g. 'f') may emit on multiple `articles.<subtype>`
lines — `defaults` today, `overwrites` planned — with disjoint property
sets that apply at different stages. Model now merges those emissions:

  { name: 'f', properties: [{ name, value, scope: 'defaults'|'overwrites' }] }

Per-label entries in the AR pane show a property-count breakdown
(`f (1 defaults, 2 overwrites)`). The Article FO pane lists each property
with a `(O-) default` / `(-O) overwrite` toggle — click flips the scope
and marks the score dirty. ASCII glyph mimics a physical switch position
so the active scope is visible at a glance.

Fixture tests cover both the existing single-subtype shape and a synthetic
two-subtype merge (177 assertions total).
2026-06-28 19:11:01 +02:00
c0dev0id
913114bb02 Multi-pane Sub-objects driven by SLOT of SLOT.SUBTYPE
The bottom handle bar is now dynamic. Fixed CP + FO handles, then one
handle per kind of sub-object the focused node has. "Kind" is the SLOT
part of the AST's SLOT.SUBTYPE namespace: sub-objects sharing a SLOT
(e.g. `line.stem_note` + `line.motif`, or `articles.<subtypeA>` +
`articles.<subtypeB>`) collapse into one pane; different SLOTs split.

Score-level kinds emit, in this order: TU (tuning), ST (stage cone +
voices), IN (instruments — linked ones listed read-only), AR (articles),
BA (bars). info metadata is folded into the FO pane (was previously
listed as a sub-object).

Handles are 2-letter labels for now, intended as alt-text for icons in
a later release.

- `subobject-kinds.js` new: `getKindGroups(node)` returns ordered
  `[{ kind, items }]`; `KIND_LABEL` maps kind -> 2-letter label.
- `PaneSubObjects.js` now parameterized by `kind` prop; renders one group.
- `AppShell.js` builds the handle bar from `getKindGroups(focusedNode)`;
  drops back to FO when the active sub-pane disappears after a focus change.
- `ObjectShort.js` accepts a `readOnly` flag (used for linked instruments).
2026-06-28 15:42:08 +02:00
c0dev0id
b23e243225 Parser: support sompyler 10cad1f preamble (stage.*, articles.<subtype>, tuning.frequency_factors)
- Top-level dispatch switches on fqSlot (parentSlot.slot) so dotted top-level
  slots (`stage.cone`, `stage.voice`, `articles.<subtype>`) are matched.
- `stage_voice` -> `stage.cone` (new, one per score) + `stage.voice` (list).
- `article` -> `articles.<subtype>` (subtype stored on the article entry;
  first observed subtype is `defaults`).
- New `tuning.frequency_factors` child captured as `{ label, factors }`.
- buildModel un-nests preamble siblings that sompyler emits at depth 01
  under `00 tuning` via `with deeper_level("articles")` / `deeper_level("stage")`.
  Only `tuning`'s children are flattened; `instrument`'s implicit containers
  (`character`, `VOLUMES`, `TIMBRE`, `FM`, `AM`) are left untouched.
- Fixture test extended with 18 preamble assertions (172 total now pass).
2026-06-28 15:41:54 +02:00
c0dev0id
6be4a3297a Revert "Support both old and new sompyler AST slot names in parser"
This reverts commit b56d84befb890c13ad5daafa327032f9ca8c76e8.
2026-06-28 01:53:53 +02:00
c0dev0id
b56d84befb Support both old and new sompyler AST slot names in parser
Pre-c3b51bc sompyler emits offset.stem_note/motif.stem_note/clause.note
while new sompyler emits line.stem_note/seq.note. Parser now accepts both
parentSlot values for stem_note, motif, and clause children so the app
works against both old and updated sompyler installations.

Also falls back to reading chain.clause directly from stem_note's children
when no stem_note.chain node is present (old format had no chain wrapper).
2026-06-27 20:08:31 +02:00
c0dev0id
859e62e143 Adapt parser and tests to new sompyler AST slot names
Sompyler commit c3b51bc renamed slots across the board:
- offset.stem_note → line.stem_note (parentSlot 'offset' → 'line')
- motif.stem_note → line.stem_note (parentSlot 'motif' → 'line')
- clause.note/pause/stack → seq.note/pause/stack (parentSlot 'clause' → 'seq')
- stem_note.chain is now a real hierarchical node (depth 04) between
  line.stem_note and chain.clause; clauses are now its children, not
  direct children of stem_note

Also adds:
- offset.motifs[] for line.motif invocations at tick positions
- stem_note.writeToName from stem_note.write_to positional
- adjacent prop already stored; new tests verify True/False/absent

PaneFO.js: show offset motif invocations inline (label + chord)
test-parser.mjs: update synthetic fixtures to new depths/slot names;
  add tests for line.motif, stem_note.write_to, adjacent prop
2026-06-27 18:03:14 +02:00
c0dev0id
b887341df4 Add motif/stem_note display and offset note hierarchy
- parseAstLog: abort on depth jump >1 or missing slot (depth>0)
- buildStemNote: full clause tree (notes/pauses/stacks), editable fields
- buildClause: replaces buildCluster; filters nested clause.chain silently
- buildMotif: builds from voice.motif; sets isStatic
- buildOffset: uses buildStemNote; drops old clusters/chains arrays
- buildVoice: motifs are now objects, not label strings
- PaneSubObjects: static motifs before offsets; motif/offset→stem_note drill-down
- PaneFO: motif + stem_note FO cases; unknownProps display helper
- PaneCP: motif + stem_note shortView cases
- test-parser.mjs: 21 new synthetic tests for motif/clause/stack/guards
2026-06-27 16:14:56 +02:00
c0dev0id
be338c2de0 Add audiowidget endpoint: Blueprint renders audiowidget.tmpl, PaneCP fetches it on synthesis completion 2026-06-24 18:52:26 +02:00
c0dev0id
c7c246c32d Fix endpoint names in url_for() calls; remove fallback URLs from api.js 2026-06-24 18:24:08 +02:00
c0dev0id
c4804f0b55 Fix hardcoded API paths: inject URLs via url_for() in template, read from window.NEUSICIAN_URLS in api.js 2026-06-24 18:19:56 +02:00
c0dev0id
097d4488ef Export metadata edits (title, composer, source, encrypter) via patchScore 2026-06-24 17:52:25 +02:00
c0dev0id
89198ec37e Fix serializeModulation field names; add mod:base test assertion 2026-06-24 16:41:12 +02:00
c0dev0id
e4b5cf6887 Rename serializeFm to serializeModulation 2026-06-24 16:22:50 +02:00
c0dev0id
923aa5d41a Add AM modulation support to parser and exporter 2026-06-24 16:19:24 +02:00
c0dev0id
1706404b7e Simplify: remove dead code and redundancy across vue app
- Delete ObjectBasic (defined, never imported)
- ObjectShort: remove unused node prop
- exporter: export stressorToString; use in patchBarMeta (removes inline duplicate)
- PaneFO: import stressorToString from exporter; extract H4 style constant,
  shapeSection() helper, instrOnChange() helper; remove apply param from
  makeChangeHandler (was always () => markDirty()); remove unnecessary ?.
  on always-initialized arrays (variations, labelSpecs, subvariations)
- PaneSubObjects: explicit hasChildren on all item kinds; bars at score
  level get hasChildren based on voices; remove stale kind-list fallback
- ast-parser coerce(): remove unreachable s.trim() check ('' handled above)
- PaneCP: fix post-export synthesisStatus reset to use real field names
2026-06-24 16:08:52 +02:00
c0dev0id
9eb4add695 Make bars navigable and editable with _meta export
Navigation:
- SubObjects: bar → voice (with chevron when offsets exist) → offsets
- PaneCP shortView handles voice and offset types

FO pane:
- Bar: editable beats_per_minute, stress_pattern (serialized as
  groups string), upper/lower stress bounds and tempo shape via
  ShapeEditor; sets bar.isDirty on change
- Voice: read-only name, articles, motifs, offset count
- Offset: read-only tick, stem notes and clusters as label strings
  (note re-serialization deferred)

Export:
- exporter.patchScore() now accepts optional bars[] parameter
- Splits rawScoreText by \n---\n; instruments patched in header,
  dirty bar _meta blocks regenerated, voice note lines kept verbatim
- ast-parser buildBar() adds isDirty:false
- PaneCP passes scoreModel.bars to patchScore
2026-06-24 13:25:23 +02:00
c0dev0id
338ea5be49 Fix result display: audio player, error message, correct field names
- Use file_accomplished (not frozen+!error) to gate audio player
- Use errors field (was .error — wrong name, always falsy)
- Render <audio controls> instead of bare download link
- Add audiowidget.tmpl.stub for server-side customization
2026-06-24 13:16:28 +02:00
c0dev0id
c52b0bf9cf Switch to FO pane on any label click in CP or SubObjects panes 2026-06-23 21:36:46 +02:00
c0dev0id
4c392927cf Fix subvariation display, progress bar, and linked-instrument discard
- ast-parser: read for_value= prop for sub-variations (was always null,
  causing fallback to positional "subvariation N" label)
- StatusPoller: calculate progress from currently_rendered_notes /
  notes_in_total; show remaining_time string from status.json
- ShapeEditor/EnvelopeEditor: pass { undo } through onChange so callers
  can revert in-place mutations
- PaneFO: makeChangeHandler stashes undo from info arg; discardEdit
  calls undo() before clearing pendingEdit; depends_on handler provides
  its own undo for the inline node.dependsOn mutation
2026-06-23 21:28:53 +02:00
c0dev0id
2060f109b6 Show numeric sub-variation edge values as bare numbers
Numeric dependsOn values (merge-type variations) are RFC edge values,
not ATTR names. Display them as the number, not as "ATTR: N" or
"subvariation N".
2026-06-23 18:49:17 +02:00
c0dev0id
06b2bab5d3 Redesign PaneCP as vertical focus-path menu; add score metadata to FO pane
PaneCP: replace horizontal breadcrumb with vertical list of short views
for each node in the focus path (type tag + label/identifier). Clicking
any item above current navigates up.

PaneFO: show score info fields (title, composer, source, encrypter) as
editable DL when at score level instead of 'Nothing selected'.
2026-06-23 18:45:13 +02:00
c0dev0id
839ea3f95c Show full score top-level in SubObjects: info, tuning, articles, stage, instruments, bars 2026-06-23 17:38:08 +02:00
c0dev0id
4afabea837 Fix hidden panes: remove duplicate se-pane wrapper from PaneCP and PaneSubObjects
AppShell already wraps each component in div.se-pane; the inner se-pane
had display:none without .active, hiding all content.
Also drop stale movement/part/line/measure bar fields (bar IDs are opaque).
2026-06-23 17:35:29 +02:00
c0dev0id
f318b155cc Fix VOLUMES/TIMBRE/FM placement: variation properties, not instrument-level
Per RFC §3.2.1.3, VOLUMES, TIMBRE and FM are variation properties. The
previous exporter incorrectly appended them to the first ATTR variation;
they now go into a synthetic root variation (no ATTR) as required.
variationLines() handles these fields directly.

Fix test-parser.mjs fixture paths for standalone execution from repo root.
2026-06-22 19:30:45 +02:00
c0dev0id
c8b8d03e3f Apply RFC-compliant ast-parser and exporter
- ast-parser: railsbackCurve moved to variation level, FM shape child
  parsing, buildLabelSpec direct A/S/R handling, bar IDs as opaque strings
- exporter: RFC-compact shape format, correct instrument NAME: key,
  patchScore regex fixed, RAILSBACK_CURVE emitted from variationLines
- .gitignore: exclude test-parser.mjs pending author confirmation
2026-06-22 10:52:32 +02:00
Florian "flowdy" Heß
822e2c9f42 Added files generated by Claude, prompted by sdk based on the md files 2026-06-21 19:23:54 +02:00