Imported from zynque/opelan (
AGENTS.md). Install upstream withnpx skills add zynque/opelan. Copyright stays with the author.
AGENTS.md
Code style
- Small files: aim for ~100 lines maximum per file. If a file grows past that, split it by responsibility rather than letting it sprawl.
- Encapsulation: each file should own one concern. Prefer small values,
functions, or classes with a focused responsibility over large classes that
do everything. Example:
ui/editor/DocumentEditor.scalais a thin component delegating to single-responsibility files (update, session, history, doc ops, clipboard, keys, view, row), each in its own file. - Composition over inheritance: the concern is inheritance used to
provide behavior. Reuse behavior by holding and delegating to a value
(e.g.
DocSessionowns aSessionUndovalue) or by passing functions/components as parameters (e.g.Workbench(docPane)) — not by mixing in traits that carry real logic or state, self-types reaching into the host's internals, or subclassing concrete classes. Fine uses ofextends:- implementing an interface trait (
Component,Language,SyncTransport,Cmd), including small convenience/default methods for fluency (Language.defaultView,Component.children = Vector.empty); - sealed ADTs / enum cases;
- test fakes mirroring an external type hierarchy (
FakeNode/FakeElementmimic the DOM); - framework/interop requirements (
munit.FunSuite,js.Object).
- implementing an interface trait (
- Pure logic separated from effects: keep document/tree operations pure
(
foundation/) and DOM/event code thin (ui/). Pure helpers that tests can exercise live on companion objects (e.g.DocumentEditor.parseNodeData). - Scala 3 top-level definitions: stateless helpers that aren't tied to a
type live at package top level — don't wrap them in an
objectjust to hold functions (Scala 2 style). Keepobjectonly for companions of a type, singleton implementations (extends Component,Language), deliberate import namespaces (Detached,Dsl), and JS interop facades. - Immutability by default: prefer
vals,case classes, and immutable collections. Reservevar/mutablefor the effect boundary — DOM nodes, liveInstances, theRuntimequeue — or for local accumulators that don't escape their function (e.g.Vector.newBuilder).
Build & test
- Scala CLI is at
C:\Users\Jeremy\AppData\Local\Coursier\data\bin\scala-cli.bat(not on the default bash PATH). - Test:
test.bator:scala-cli.bat test --server=false --power project.scala foundation ui data collaboration test. The scripts setTEMP/TMPDIRto./.tmpfirst — the Scala.js test runner writes the linked main.js there, and the doc-sync suite needs Node to resolvenode_modules(@automerge/automerge) from it. Without the redirect the tests fail withERR_MODULE_NOT_FOUND. - Browser bundle:
build.bat, which packages Scala.js output toapp.jsand runs webpack to producebundle.js. Packaging to an existingapp.jsrequires--force(build.bat deletes it first).
Git
- The user reviews and commits changes manually — do not commit or push.
Notes
- Public lineage references: prototypes live at
https://github.com/zynque/olw-p1…olw-p4.
