Skip to main content

File inventory

An index of the tree: find the file here, then follow it into Class map for what the class does, and into Lifecycle for when it runs.

Every non-test file in src/, with the one thing it is for. Barrels (index.ts) are listed only where they do something beyond re-export.

Derived from src/** (excluding *.test.ts).

model/ — pure — types + brand helpers, zero deps​

fileexportswhat it is for
model/ids.tsEntryId, RowId, BarId, ChangeSetId and their helpersBranded string ids. barId(entry, partIndex) is the deterministic `${entryId}:${partIndex}` rule — the one place that format is written. The *FromDataset helpers are the guarded way in from a DOM dataset attribute.
model/time.tsInstant, TimeUnit, TimeSpan, DurationInstant is branded epoch-ms so a naked number cannot pass as a date. TimeSpan is half-open [start, end). Also holds the input twins — InstantInput and TimeSpanInput: what a consumer may write, as opposed to what the library stores.
model/entry.tsEntryThe authored record, and it answers questions about itself: read(key), hasChildren, children(), parent(), descendants(), depth, toInput(). It carries no stored classification — an Entry derives when it has children. toInput() gives the loose twin entries.add() takes, which is how a row is copied.
model/tree-place.tsTreePlaceWhere one Entry sits in the tree — its parent and its rank among that parent's children (#425). view/row-drop.ts computes it; an entryMove payload carries it once a drop changes the tree.
model/dataset.tsDatasetThe structural contract (entries + timeZone) that api/dataset.ts's class implements. Lives here so layout/ can bind against it without importing view/ or api/.
model/geometry.tsPoint, Size, PixelSpan, RectOne vocabulary for pixels shared by layout, render, view — instead of four private {x, y} shapes.
model/errors.tsFreeGanttError and the catchable subclassesThe public error base, carrying a stable code. Subclasses name failures a consumer can hit: dates, fields, plugins, commands, and mutation.
model/change-set.tsChangeSet, FieldUpdatedThe one write shape: { added, removed, updated }, where an update is { entryId, field: FieldKey, from, to } per field. Emitted on change; the shape undo and redo replay.
model/field.tsField, ColumnAlignWhat a value is. A Field key is the whole address: a core key reads and writes the Entry directly, a compute Field runs on read and owns no home, and everything else lives in entry.props under its own key. There is no more source to declare.
model/command.tsKeyChord, TargetKindZero-dep command primitives. The bound Command types live in api/command.ts, which may name Gantt.
model/error-report.tsErrorReport, ReportCode, RaiseErrorWhat the error event carries on Dataset and Gantt. Types only; the catchable class is FreeGanttError.
model/plugin.tsPluginId, Disposer, PluginStorePlugin primitives that name nothing outside model/. ChromePlugin lives in api/plugin.ts. ExtenderWrapper moved to edit-request.ts — it wraps EditExtender, which already lives there.
model/render.tsElementDescriptionThe reconciler's vocabulary as plain data. A plugin returns this instead of a live node; render/dom/element-description.ts is the only place that turns it into DOM.
model/stored-entry.tsStoredEntry, EntryInput, EntryEdit, EntryEdits, ProposedEdit, ProposedEdits, spansTime()What storage owns, and the loose shapes that reach it. spansTime() is the one place the span invariant is written: an Entry spans when it holds both start and end. EditRequest/EditExtender moved to edit-request.ts — see that row for why.
model/field-key.tsCoreFieldKey, FieldKey, CoreFieldValues, CoreFieldValue, FieldValueA Field key names a Field and is the changeset's field. Derives from StoredEntry, so it sits one file below field.ts — the live Entry names Field keys too, and the shared leaf keeps the two out of an import ring.
model/edit-request.tsEditRequest, EditExtender, ExtenderWrapperThe extension hook's own vocabulary. A leaf on purpose: EditRequest.writeTarget names both FieldKey and WriteTarget, and write-verdict.ts already reaches field-key.ts through error-report.ts → field.ts — so this sits below all three and nothing in model/ depends on it.
model/hierarchy-source.tsHierarchySource, HierarchySourceWrapperWhich Entry is the parent of this one. A plugin states the answer; core owns everything downstream of it — child index, depth, the descendant walk, and the Rollup.
model/field-lock.tsFieldLockQuery, FieldLockRule, FieldLockRuleWrapperThe per-entry lock rule (#473): a plugin opens one locked Field on one Entry, or a whole subtree, sharper than Field.editable alone. data/write-rule.ts holds the resolver every write door reads (I14).
model/place-rule.tsPlaceQuery, PlaceRule, PlaceRuleWrapperAnswers whether an Entry may land under a given parent — a cross-parent drop's own seam, sharper than a lock rule's per-cell answer. data/write-rule.ts holds the resolver every write door reads (ADR 0038, I14).
model/remove-rule.tsRemoveQuery, RemoveRule, RemoveRuleWrapperAnswers whether entries.remove() may take an Entry, asked once per member of its subtree — narrowest answer wins. data/write-rule.ts holds the resolver EntryStore.remove() reads (#611).
model/bar-move-rule.tsBarMoveRule, BarMoveRuleWrapperAnswers whether the bar of an Entry moves — the seam a summary bar needs, since no lock rule on a derived cell can stop it. data/write-rule.ts holds the open bottom occupant.
model/write-verdict.tsWriteRefusalReason, WriteVerdictThe verdict pair a plugin author reads off ctx.interaction.canWrite. Declared here, not in data/, because only api/ and model/ types are public — data/write-rule.ts computes it and view/ republishes it.
model/capabilities.tsCapabilities, CapabilityRule, WriteRule, GestureCapabilityWhat a consumer, or a Variant, may say about a gesture. Declared here so layout/variants.ts can name capabilities?: Capabilities without reaching above model/; view/capability.ts is still the one file that resolves them, and it owns the separate ResolvedCapabilities those rules produce.
model/index.tsbarrelPublic types only; brands and error helpers re-exported.
fileexportswhat it is for
time/instant.tsinstant(), now(), toISO(), addMs(), diffMs(), overlap(), MSEpoch-ms primitives and the MS constant table. instant("…") takes absolute values only and rejects a zoneless ISO string — a wall-clock reading names no instant until a zone resolves it. It refuses rather than answering NaN, and speaks instant-fault.ts's vocabulary, so null is never reported as a zone problem. overlap(a, b) clips one TimeSpan to another, undefined when they do not touch — the public form of the clip the library already did three times internally (issue #472).
time/instant-fault.tsinvalidInstant()The one door onto InvalidInstantError for the two readers that produce it — instant() and toInstant() — so one mistake never gets two vocabularies. reason is a closed InvalidInstantReason and operation is required (#242); InvalidInstantError itself builds the message. It sits below both readers because instant.ts cannot import input.ts, which already imports it.
time/zone.tstoPlain(), fromPlain(), startOfDay(), addDays(), addMonths(), addYears(), diffDays(), stepBy(), startOf(), SUPPORTED_TIME_UNITSAll zone-aware arithmetic, on temporal-polyfill's tree-shaken /fns API. The private UNITS table is the single source of truth for which units can step and floor — stepBy and startOf both dispatch through it so they cannot disagree.
time/input.tstoInstant(), toEndInstant()The one place a loose InstantInput becomes a stored Instant. It lives in time/ rather than at the api/ boundary because resolving a Plain time needs the zone and the DST fold/gap policy only zone.ts has — and because advancing a date-only end by one day is itself zone-aware arithmetic, confined to this layer.
time/date-only-end.tsstartOfNextDay(), startOfLastCoveredDay()The calendar arithmetic behind a date-only end: where the next local day starts, and where the last day a span covers starts. Both walk a calendar day through zone.ts, never a fixed millisecond count, so a DST-shortened or -lengthened day still lands correctly.
time/zoned-time.tsZonedTime, createZonedTime()The zone-bound façade a plugin author reaches through Dataset.time. Every method forwards to zone.ts; no extra arithmetic.
time/scale.tscreateTimeScale(), TimeScale, ViewPreset, pxPerMsForPreset(), pxPerMsForUnitWidth()Instants ⇄ pixels, plus the shipped zoom presets as frozen config objects. ticks() walks calendar boundaries; MAX_TICKS guards a misconfigured step from looping forever.
time/format.tsformatDate(), dateFormatter(), formatDateTime(), formatInclusiveDate(), formatStartAndEnd(), joinStartAndEnd(), lastCoveredInstant(), dropRepeatedGranularity()The display-side formatting helpers — inclusive ends, locale-bound labels, a start-and-end pair joined one way. Kept small and pure so any consumer-facing text goes through one path.
time/presets.tsthe shipped view presets, ZOOM_PRESETSDeep-frozen preset ladder from finest hour to coarsest year, plus single-band ids. A preset is a value, never a shared mutable singleton.
time/snap.tssnapInstant(), SnapUnit, stepsBetween()Snap resolution for gestures: where a drag's committed value lands. Pure — the gesture pipeline calls this, never arithmetic of its own. A preset's 'tick' is resolved to a unit and increment before this file sees it.
time/index.tsbarrelPublic entry points; the façade that keeps the deps confined.

data/ — pure — live state, transactions, fields — no DOM​

fileexportswhat it is for
data/dataset-state.tsDatasetState, DatasetStateOptionsThe live state one Dataset instance owns — a façade holding the entry store, event bus, field registry, history and edit-extender. Exposes transaction() as the sole commit entry point.
data/entry-store.tsEntryStoreCommitted entry map plus per-transaction write-set overlay. Exposes add/update/remove/load/syncAll/syncChanges and a token-gated staging/apply surface so a transaction can stage edits it does not want to leak mid-flight.
data/entry-lock.tslockedEntryLockRule(), lockedEntryRemoveRule(), lockedEntryBarMoveRule()core lock (ADR 0038), built on same public FieldLockRuleWrapper/RemoveRuleWrapper/BarMoveRuleWrapper seams plugin uses. api/dataset.ts installs all three first, so a plugin can narrow or widen the lock. A dependency-cruiser rule holds file to model/ imports only.
data/entry-batch.tsassertEntryBatchIsSound(), readEntryBatch(), assertNoOpenTransaction(), assertNoRunningExtensionHook()The checks and the placement a whole-list write (entries.load(), and entries.syncAll(), #517) runs before it stages anything: no duplicate id, every parentId known, no cycle, each entry's rank among its siblings from list position, and no open transaction. Pure over the list a caller hands in, so load and syncAll share it without either reading the store.
data/entry-batch-changes.tschangesToMatchBatch()The pure diff behind entries.syncAll() (#517): reads a committed batch and a target batch, hands back a ChangeSet's own three lists (added, removed, updated). No store, no transaction, no commit — entry-store.ts applies what it returns.
data/entry-delta.tsreadBatchAfterDelta(), CommittedEntries, BatchAfterDeltaThe batch entries.syncChanges() checks, places and diffs (#527): the committed rows with a delta laid on top. Pure over what the caller hands in — no store, no transaction, no commit.
data/parent-cycle.tsfindParentCycleMembers()One colouring walk over a parentId chain, shared by entry-batch.ts's whole-list check and replay-changes.ts's replay: every id whose chain loops back onto itself.
data/entry-reader.tstoEntries(), toEntry(), toEntryAfterUpsert(), toEditReading()Maps EntryInput/EntryEdit through time/'s zone conversion into stored Entry/StoredEdit shapes — the single place field reading and date normalization meet. toEntryAfterUpsert() folds an entries.syncChanges() row's partial edit onto the entry it kept.
data/change-set.tsdiffEdit(), foldChangeSet(), invertChangeSet()Field-aware changeset building: diffs a StoredEdit against committed state, folds a transaction's edits into one ChangeSet, and inverts it for undo.
data/transaction.tsrunTransaction(), commitChangeSet(), applyConstructionRollUp(), rollUpFreshBatch()The transaction runner. Mints a TxToken, runs the body, calls the edit-extender once, runs hierarchy promotion, runs rollup, folds the changeset, and emits beforeChange/change. rollUpFreshBatch is construction's own Rollup shape, opened to entries.load() (#496) as its one door onto rollUpFields.
data/build-commit-change-set.tsbuildCommitChangeSet()The four-stage commit pipeline: body edits → extension hook → rollup → fold. The only module that imports rollup.ts on the commit path (rollup-is-removable).
data/entry-tree.tsbuildEffectiveEntries(), childIdsByParent(), parentIdIn(), commitMovesNoRow()Shared entry-tree helpers for the rollup pass and edit-request.ts: overlays proposed edits on committed state so a pass reads an effective tree. commitMovesNoRow is the shortcut both files share, rather than each re-deriving it.
data/edit-request.tscreateEditRequest()The one place an EditRequest is built. build-commit-change-set.ts (commit path) and api/dataset.ts's extraEditsFor (preview path) both call this, so the two paths cannot answer hasChildren/writeTarget two different ways. Builds the effective child index lazily — only on the first call that needs it.
data/history.tsHistory, HistoryOptionsThe undo/redo stack. Subscribes to change, records user changesets, and replays inverted/copied changesets through replayChangeSet.
data/rollup.tsrollUpFields()Gives every roll-up-kind parent every rolling-up field from its children, bottom-up, on every commit. Yields to the body-proposed field — a user value is never overwritten.
data/computed-cache.tsComputedFieldCacheRevision-keyed memo for compute-sourced fields; cleared once per committed changeset.
data/edit-extension.tsEditRequest, EditExtender, identityExtenderThe extension hook shape. identityExtender returns an empty map (the unoccupied default); a Dataset plugin or a scheduling plugin occupies this slot by wrapping.
data/plugin-store.tsPluginStores, pluginStoreName()Per-plugin per-entry rows, staged through the same write-set a transaction already uses for entries. Not Entry.props.
data/error-reporting.tsraiseErrorOn(), createErrorRaiser(), buildRefusalReport()Stamps an Error report with now() and raises it on a bus. Builds the one shape a refused before* veto reports. render/ and extensions/ take a RaiseError by injection instead.
data/dev-mode.tsisDevMode()One home for the Vite/dev-mode flag. The named leaf render/ and extensions/ may import without opening a data/ edge.
data/event-bus.tsEventBus, RefusalNoteGeneric typed pub/sub with sync veto. The beforeChange/change fan-out channel shared by dataset and view. RefusalNote holds the first refuse(reason) words from one emit.
data/reactivity.tssignal(), computed(), batch()The façade over alien-signals — the only file touching the reactive dependency. One of exactly two runtime deps, each confined to one façade file.
data/replay.tsreplayChangeSet()Computes what an undo/redo changeset still has to write (changesToReplay) and commits it through commitChangeSet, with no extension hook — changesToReplay itself re-rolls the parents it touches.
data/replay-changes.tschangesToReplay()The pure diff behind undo, redo and dataset.replay(): writes a recorded changeset onto the store's current values instead of blind, skipping a row a sync has already settled, cascading a removal to the committed children it did not name, and renumbering each sibling group it touches dense from 0.
data/fields/aggregators.tsSHIPPED_AGGREGATORSBuilt-in aggregator functions — min, max, sum, count, none, weightedMeanByDuration — registered by name so a function reference can serialize into a document.
data/fields/core-fields.tsCORE_FIELDSDeclares the six core fields — name, start, end, parentId, duration, hierarchyParentId — as Field declarations of the same shape a consumer writes.
data/fields/field-access.tscreateFieldAccess(), mergeProposedEdits(), ambientFieldContext(), createComputeContext()The one reader behind every by-key value: a core key, a props key, or a compute Field. It also merges and overlays proposed edits, so a rule reads the row an edit would produce.
data/fields/field-registry.tsFieldRegistryOne registry per dataset. Resolves field types, merges core + consumer declarations, validates uniqueness, and provides lookup by key.
data/live-entry.tsLiveEntries, EntrySourceBuilds the live Entry: model/ declares the interface, data/ builds it, layout/ names the type but never the file. Every read goes back to the store, so a row read inside an open transaction sees the write set overlaid on committed state.
data/hierarchy-source.tsstoredParentSource, parentIdFrom(), ParentIndex, CheckedHierarchy, checkHierarchyAnswers()Core's own hierarchy source, plus the check core runs over any source's answers. An unknown parent id reads as a root, and a cycle's closing link breaks. Both are refused answers, and neither throws — the check reports nothing itself; the caller raises.
data/sibling-order.tssiblingIndexesInListOrder(), siblingIndexesAfterDelta(), renumberSiblingGroups(), siblingBlockMove(), SiblingChange, SiblingGroupKey, SiblingBlockMoveThe pure math behind the sibling-order Field: a rank per id from list order at ingest, a rank per id from a write's ordered log of moves, replayed over each touched group's committed order, and a rank per id from an entries.syncChanges() delta laid over the committed order. buildCommitChangeSet calls it for a live write, changesToReplay for undo and redo, entry-delta.ts for a sync delta. siblingBlockMove is the vertical-drag case: one or more Entries land together at one drop index, keeping their own row order.
data/write-rule.tsresolveWriteTarget(), libraryWriteRule(), editableAnswerFor(), fieldEditableRule(), assertFieldTakesWrite(), WriteTargetThe one write resolver where three questions meet: does the Field exist, is it editable (the Field's own default, or a plugin's per-entry lock rule, #473), is it derived here. view/capability.ts asks before it opens a cell; entry-store.ts's update() and an EditExtender cascade ask the same resolver before they stage a write.
data/fields/column-sizing.tssizingOfColumn(), ColumnSizingCandidateThe width/flex pair merges as one pair, never key by key. The Field-column merge and the Gantt-column merge each fixed that bug independently once; this is the one function both now call.
data/fields/field-types.tstext, number, percent, date, duration, currency(), SHIPPED_FIELD_TYPESThe shipped Field types: text, number, percent, date, duration, plus currency(). currency({ code }) is a factory, not a seeded name. percent formats through Intl.NumberFormat's own 'percent' style, so a stored 35 divides by 100 first and locale spacing is right — a hand-rolled ${value}% gets French and Arabic wrong.
data/fields/format-field-value.tsformatFieldValue(), createFormatContext()The one read for a Field's displayed text: its own formatValue, or stringifyPrimitive of a plain value. The Grid, the bar label and gantt.formatFieldValue all call it, so none can disagree. createFormatContext is the one FormatContext builder they all share.
data/index.tsbarrelRe-exports DatasetState, DatasetStateOptions, HistoryOptions, EntryStore; everything else internal.

layout/ — pure — headless geometry — rows, bars, frame​

fileexportswhat it is for
layout/frame.tscomputeFrame(), GeometryFrame, LayoutInput, FrameRow/Bar/Header*, Overscan, DEFAULT_OVERSCANThe full pure layout pass: resolves rows, produces bars per row, culls to the visible window, and computes header ticks and date-line decorations. One call in → one GeometryFrame out.
layout/frame-layout.tsFrameLayoutThe stateful wrapper that keeps a RowHeightIndex and per-row bar memo alive across renders. One instance per Gantt.
layout/frame-memory.tsFrameMemoryHolds one layout pass's cross-render memory — the RowHeightIndex plus a Map of per-row bar memos — so a later frame reuses geometry where the inputs did not change.
layout/row-height-index.tsRowHeightIndex, PrefixSumHeightIndexO(log n) prefix sums with binary search for indexAtY. Behind an interface so variable row heights can swap the implementation without touching computeFrame.
layout/row-drop-zone.tsrowDropZoneAt(), RowDropZone, RowDropSide, ROW_CHANGE_THRESHOLD_PX, DROP_ZONE_HYSTERESIS_PXTurns a vertical drag's pointer y into which row it sits over, and which third (before/into/after), with a dead zone around the source row and hysteresis at every boundary so the answer does not flicker.
layout/row-drop-target.tsdropPlaceFor(), RowsForDrop, DropPlace, DropPlaceAnswerTurns a resolved RowDropZone into where the drop lands in the tree — a parent, a rank among its current children, the Insertion line's depth and y — or a refusal for a group header row.
layout/column.tsFrameColumn, ResolvedColumn, FieldComparePure data types for the grid-column paint shape and its locale-bound formatter.
layout/column-renderers.tsmeter(), image()Shipped Grid-column renderers as DOM-free description trees. meter() paints a percent as a track; image() paints a stored URL as an img. The look lives in the always-on sheet — a Column renderer cannot carry a css string the way a variant can.
layout/date-line.tsresolveDateLines()Resolves the today-line and authored date lines into positioned DateLine decorations.
layout/gesture-draft.tsdraftForMove(), draftForResize(), previewOffsets(), cursorLabelForX()Pure gesture math for drag previews. All date computation stays here so interaction/ performs no arithmetic.
layout/decoration.tsDecorationLayer, DecorationProvider, RangeBand, RowStripeThe decoration seam's own types — range bands and row stripes as pixel-resolved shapes.
layout/decorations.tsDecorationRunnerRuns registered decoration providers into the frame's under-bar and over-bar layers.
layout/frame-row.tsFrameRowThe painted-row shape computeFrame emits and a cell renderer reads.
layout/pick-defined.tspickDefined()Copies only defined keys from a patch onto a settings object.
layout/entry-rule.tscompileEntryRule(), EntryRule, EntryPredicate, FieldMatch, EntryRulePortsOne match syntax for "which Entry does this rule claim?" — a variant's when and a row source's childrenAsSegments both compile through this, so bars/ and rows/ never import each other over it.
layout/registration-table.tscreateRegistrationTable()Stack-per-key registration with a disposer that removes exactly its own entry. Named leaf that extensions/ may import.
layout/renderer.tsBarRenderer, GridCellRenderer, HeaderRenderer, TooltipRendererRenderer callback vocabulary. Plugin and consumer options share these types.
layout/bars/produce-bars.tsproduceBarsForRow(), resolveBars()Turns a row's entries into Bars. Nothing dispatches on a type tag: the variant registry answers what one Entry draws, and that variant's producer builds the Bars. A header row produces none.
layout/rows/resolve-rows.tsresolveRows()Dispatches to the correct row source based on source.source, stamping each row with a sequential index.
layout/rows/row-source.tsRowSource, EntriesRowSource, GroupRowSource, CustomRowSource, PlannedRow, etc.Pure data types defining the three row-source configs and their shared options (filter, sort, filterPolicy).
layout/rows/entries-source.tsresolveEntriesSource()Flat mode maps each entry one-to-one; tree mode does a depth-first walk using parentId.
layout/rows/group-source.tsresolveGroupSource()Buckets entries by the consumer-supplied groupBy function, emitting header rows then member rows.
layout/rows/custom-source.tsresolveCustomSource()Adapts a consumer-supplied resolve() callback's CustomRow[] into UnindexedRow[].
layout/rows/collapse.tsapplyCollapse()Drops descendants of collapsed row ids from the unindexed row list.
layout/rows/filter.tsapplyFilter(), visibleRowIds()Applies the row-source filter, with hide vs. keep-ancestors policy.
layout/rows/sort.tsapplySort()Applies the row-source sort inside each sibling group.
layout/viewport/batched-notifier.tsBatchedNotifierDepth counter + pending flag + finally flush. Several writes, at most one notification, no observer ever sees an intermediate state.
layout/viewport/bound-value.tsBoundValue, BoundValueContract, BoundValueHandleThe binding side of a shareable model: one Map<Binding, onChange> serving both membership and notification, a memoized resolved value, and the notify-iff-changed rule.
layout/viewport/time-scale-model.tsTimeScaleModel, TimeScaleIntent, ScaleBinding, ScaleBindingHandleShareable x-axis. Takes intent (preset, range) and resolves zone/span/zoom from the Gantts bound to it. Two Gantts sharing one instance are x-synced by construction.
layout/viewport/scroll-axis.tsScrollAxis, ScrollAxisState, ScrollAxes, ScrollAxisBinding, ScrollAxisBindingHandle, bindScrollAxis()Shareable one-direction scroll position. Owns one shared position for one axis; each bound Gantt clamps it locally. A Gantt holds two, { x, y }.
layout/viewport/viewport.tsViewport, ViewportOptions, ViewportHandleThe fan-in: one bind, one handle, one reaction over both models plus this Gantt's own pane size, content size and overscan.
layout/bars/bar.tsBar, BarProducer, BarAnchor, FixedBarBox, DrawnVariant, VariantBars, entryBar(), wholeEntryBar(), fixedWidthBar()What one row draws, as plain data — one Bar is one bar. Holds the Bar vocabulary alone, so variants.ts may name BarProducer and produce-bars.ts may name both, with no import ring between the three.
layout/bars/variants.tsVariantRegistry, createVariantRegistry(), EntryRule, ResolvedVariant, EntryVariant, bar, summary, diamondThe Variant rule, and the file where a row meets one. One object answers five questions about a row's shape: which rows wear it (when), what shape it draws (bars), how it looks (paint), what you can do to it (can), and the rules its look needs (css).
layout/entry-double.tsentryDouble(), entryDoubles(), entryDoublesById(), entryValuesOf(), EntryDoubleValuesTest-only. The live Entry a layout/ test builds by hand. It also proves the seam by construction: layout/ satisfies the whole interface out of model/ and time/ alone, so the layout-boundary rule stays untouched.
layout/index.tsbarrelPublic layout entry points; the re-export that reaches model/ types.

render/ — DOM — GeometryFrame → pixels​

fileexportswhat it is for
render/backend.tsRenderBackend<THost>, RenderSurfaces, InteractionState, HitResultThe backend seam. Names no DOM type itself — THost carries that.
render/dom/index.tscreateDomBackend()The default backend: four absolutely-positioned layers plus a 1×1 content sizer, driven by syncKeyed.
render/dom/sync-keyed.tssyncKeyed(), SyncKeyedSpec, KeyedLayer, NestedKeyedLayersThe whole reconciler. Look-up-or-create per key → patch only if geometry changed → prune vanished keys. Scope is hard-bounded to attr/class/style/text + keyed children.
render/dom/pixel-property.tsreadPixelProperty(), PixelPropertyPolicyOne reader for every --fg-* pixel custom property, with an explicit validity policy so "zero is nonsense" and "zero is a choice" are stated, not implied.
render/dom/date-line.tsattachDateLines(), DateLineAttachmentRenders the today-line and authored date lines as positioned decoration elements.
render/dom/decorations.tsDecorationsAttachmentTurns RangeBand/RowStripe into keyed DOM nodes.
render/dom/dom-contract.tsBAR_CLASS, ROW_CLASS, …Class names and data attributes this backend writes. Nothing outside render/dom may retype them; view/gantt-dom.ts resolves a node from these constants alone.
render/dom/element-description.tsbuildElement()Turns one ElementDescription into a live DOM subtree. Stays inside the reconciler's hard-bounded scope.
render/dom/css-escape.tscssEscapeAttr()One place for the CSS.escape feature-detect every [data-field="…"] selector needs.
render/dom/row-twisty.tsrowIdFromTwistyClick()Row twisty hit target. Keeps .fg-row-twisty, .fg-row and data-row-id out of view/ so a second backend owns its own control geometry.
render/dom/tick-lines.tsattachTickLines(), TickLineAttachmentThe timeline grid lines, as one keyed layer. The geometry is GeometryFrame.tickLines from layout/frame.ts; this file only paints it. A consumer opts out through --fg-tick-line-color.
render/dom/text-ruler.tscreateTextRuler(), TextRulerMeasures a bar label's width in CSS px off a canvas 2D context that shares the bar layer's font. measureText reads glyph metrics with no layout pass, which is what keeps a placement check affordable for every bar on every frame.
render/null/index.tscreateNullBackend(), NullBackendHeadless backend recording the last frame. For tests, SSR-of-data, and the future export seam.

view/ — DOM — the shell, gestures, capabilities, navigation​

fileexportswhat it is for
view/gantt-shell.tsGanttShell, GanttShellOptionsThe composition root. Constructs the Viewport, FrameLayout, PaneLayout, RenderBackend, EventBus, FrameScheduler, GesturePipeline, PluginRuntime, TreeCollapse and every attachment; exposes live-reconfigurable properties.
view/frame-settings.tsFrameSettings, DEFAULT_ROW_HEIGHTEvery live setting that says what the next frame draws, plus the table of what each change invalidates.
view/plugin-ports.tsbuildPluginPorts(), PluginContextPartsEverything a PluginContext carries that GanttShell owns. Spread into the public context; api/gantt.ts adds only dataset and gantt.
view/plugin-registrations.tsPluginRegistrationsThe five seams a plugin registers into — renderer, decoration, bar producer, kind default, grid column — each with the refresh it owes.
view/renderer-registry.tsRendererRegistryResolves which renderer paints one bar/cell/header/tooltip. Consumer config always wins over a plugin.
view/gantt-dom.tsGanttDom, ContainerDom, DomTargetThis Gantt's rendered DOM as a read surface: is this node mine, what is it, where is the element for this entry.
view/mount-layer.tsMountLayerWhere a plugin mounts and how it stays put. Overlay escapes the pane box; row layer travels with the rows.
view/column-chrome.tsColumnChromeGrid-column resolve, live resize/reorder preview, and the one commit sequence pointer drag and gantt.gridColumns = … share.
view/column-toggle.tstoggleCellContent, switchToggleCellA toggle column's cell look, and the one gated switch a click and a key press share.
view/column-gesture-context.tsColumnGestureContextThe seam interaction/column-gestures.ts drives and GanttShell implements.
view/core-commands.tsregisterCoreCommands()The core command catalog, split out of the shell so it is reviewable as a table.
view/tree-collapse.tsTreeCollapseCollapsed RowIds as per-Gantt view state, plus the tree-arrow and ancestor-expand policy. Propose/commit two-step, so a beforeCollapseChange veto can cancel the commit.
view/today-landing.tspanToTodayLine()Today-landing policy. The shell asks this; Viewport only pans.
view/pane-layout.tsPaneLayoutBuilds the three-pane DOM skeleton — grid, splitter, timeline.
view/scroll-attachment.tsattachScroll(), ScrollAttachmentThe only file allowed to touch scrollTop/scrollLeft (invariant I12). ε-filtered so a model-driven write cannot bounce back as a user scroll.
view/pane-size-attachment.tsattachPaneSize(), PaneSizeAttachmentThe only file that observes element size. Reports a box and stops.
view/splitter.tsattachSplitter()Pointer-drag handler on the splitter chrome that resizes the grid pane.
view/styles.tsensureBaseStyles()Idempotently injects the library's base stylesheet once per document.
view/frame-scheduler.tsFrameSchedulerCoalesces render requests into at most one requestAnimationFrame per tick — the throttle between "a change happened" and "a frame drew".
view/row-edge-scroll.tscreateRowEdgeScroll(), edgeScrollSpeed(), RowEdgeScrollScrolls the rows while a row drag rests near the top or bottom edge of the rows viewport.
view/row-hover-expand.tscreateRowHoverExpand(), HOVER_EXPAND_DELAY_MS, RowHoverExpandExpands a collapsed parent while a row drag rests in its "into" zone, so a drop can land between its children.
view/dataset-change-subscription.tssubscribeToDatasetChanges()Bridges the dataset's change event into the shell's render pipeline.
view/grid-columns.tsresolveColumns(), resolveFieldCompares(), resolveGanttFields()Bridges the consumer's gridColumns input and field declarations to layout's ResolvedColumn[] model.
view/bar-labels.tsresolveBarLabelText(), resolveBarLabelPolicy()Bridges the Gantt's own barLabels and a row's own variant barLabels to a Field — the bar's own resolveColumns.
view/capability.tsresolveCapabilities()Merges the consumer's Interactions overrides with the per-kind default table; returns Capabilities with a can(capability, entry) method (invariant I14).
view/affordance-projection.tsprojectAffordances()Pure projection of hovered/movable/resizable paint tokens from hover, selection, and capability resolution.
view/entry-gesture-context.tsEntryGestureContext, EntryGestureSession, EntryHitThe type-seam between view/ (which implements it) and interaction/ (which drives it).
view/gesture-pipeline.tsGesturePipelineOwns the full gesture lifecycle for move/resize — entry resolution, draft math, preview rAF coalescing, snap resolution, and a commit pipeline with sync/async veto.
view/row-drop.tsresolveRowDrop(), RowDrop, RowDropInput, RowDropRefusal, PlacedEntryThe one resolver a vertical drag's preview and commit both call: wires dropPlaceFor and canPlace together, refuses a drop onto the moved Entry's own subtree, and turns a place answer into siblingBlockMove's writes.
view/entry-step.tsresolveEntryStep(), EntryStep, EntryStepInput, EntryStepVerdict, EntryStepRefusal, ENTRY_STEP_REFUSAL_TEXT, entryStepRefusalMessage()Turns one keyboard step (up, down, indent, outdent) into the Tree place it writes, or a refusal with its reason. It asks the same place rule as a row drop.
view/entry-step-tree.tsEntryStepTreeHolds the moves a run of keyboard steps made so far. Each later step reads the Tree as the earlier steps left it.
view/viewport-gestures.tsresolveViewportGestures()Resolves per-gesture on/off flags for wheel zoom/pan and keyboard pan.
view/convenience-chords.tsConvenienceChords, ResolvedConvenienceChords, resolveConvenienceChords()Resolves per-command on/off flags (#262) for the 11 convenience chords — the ones a chord shares with a button, a menu entry, or commands.run.
view/collapse-state.tsCollapseChangeThe payload both collapse events carry. The state itself lives in view/tree-collapse.ts.
view/attach-row-twisty.tsattachRowTwisty()Grid-pane click on a row twisty toggles collapse. Lives here, not in interaction/: collapse is viewport state, not a data gesture.
view/keyboard-navigation.tsattachKeyboardNavigation()Keydown handler for viewport navigation when nothing is selected.
view/wheel-navigation.tsattachWheelNavigation()ctrl/cmd+wheel zoom and shift+wheel pan.
view/event-bus.tsGanttEventMapDeclares the ten-plus event names and payload types, re-exporting the generic EventBus mechanism from data/event-bus.ts.
view/theme.tsresolveTheme(), ResolvedTheme, MatchMediaTheme resolution. The consumer states Theme ('auto', 'light', 'dark'); ResolvedTheme is what is actually painted once 'auto' settles. Reads the nearest ancestor's data-fg-theme, so a wrapping app's pin reaches every Gantt inside it.
view/variant-styles.tsattachVariantStyles(), VariantStylesThe second stylesheet a Gantt writes: the rules behind an installed Variant's own class. One node per Gantt, never one shared refcounted node per document — disposing one Gantt removes exactly its own rules.
view/grid-pane-width.tsGridPaneWidth, GridWidth, GridPaneWidthPortsThe grid pane's width rules: the minimum-width floor, the ceiling over the resolved columns' right edge, and the 'fitColumns' standing instruction that keeps the pane on that edge across every rebind. Split out of GanttShell so the rules are testable on their own.
view/entry-selection.tsEntrySelection, EntrySelectionRow, EntrySelectionPortsOne sentence: given what is selected, what would a click select? The pane rule ran as two switches — one here, one in interaction/entry-gestures.ts. Now it runs once, in view/, and interaction/ only ever asks for the answer. Selection holds EntryId alone — a former Segment is an ordinary child Entry now.
view/roving-focus.tsRovingFocus, RovingFocusRow, RovingFocusPortsWhich bar a keyboard action lands on. Restores by key, not by node: a virtualized row's node comes and goes as the window scrolls, but the row it stands for does not. The nudge and resize themselves stay interaction/keyboard-editing.ts's job.
view/live-region.tsLiveRegion, LiveRegionFeedOne polite live region per Gantt. A screen reader announces a change to its text, so this is how a keyboard action reaches a screen-reader user. It subscribes to the Gantt's own error event, and grows no second call path for a plugin to reach it through.
view/index.tspublic barrelRe-exports the shell and the view-surface types api/ needs.

interaction/ — DOM — drives the gesture seam, no layout math​

fileexportswhat it is for
interaction/entry-gestures.tsattachEntryGestures()Wires pointer events on entries into the EntryGestureContext's session lifecycle.
interaction/row-reorder-drag.tscreateRowReorderDrag()A grid row drag over its own pointer stream: grabs the row's subject and arms a { kind: 'reorder' } session, tree-only, no date write.
interaction/column-gestures.tsattachColumnGestures()Resize and reorder pointer sequences for grid columns, over the same createPointerGesture controller.
interaction/keyboard-editing.tsattachKeyboardEditing()Handles Delete/arrow-key edits on selected entries.
interaction/pointer-gesture.tscreatePointerGesture()Low-level pointer capture/release and move/up dispatch. Owns no DOM listeners of its own.
interaction/index.tsbarrelRe-exports the attach functions and createPointerGesture.

api/ — public — the only import a consumer makes​

fileexportswhat it is for
api/dataset.tsDataset, DatasetOptionsThe public data store. Entries CRUD, transactions, events, fields, undo/redo — all delegated into data/.
api/gantt.tsGantt, GanttOptionsThe public Gantt class. Constructs one GanttShell and forwards; exposes the live properties (preset, range, gridColumns, rowSource, collapsed, selection, plugins, commands, …).
api/plugin.tsChromePluginOf, DataPluginOf, PluginOfThe public Gantt-plugin contract, generic over TGantt so this file never imports Gantt (no cycle).
api/define-plugin.tsdefinePlugin()The one door a plugin author writes a plugin through. It returns the object it is given; what it adds is the type, so a mistake in the data half is a red squiggle in the editor, not a failure at mount.
api/grid-column-helper.tscreateGridColumnHelper()The typed way to write a Grid column. column(field, options) returns the plain column object; what it adds is the type, so a column renderer reads fieldValue as the key's own value.
api/plugin-context.tsPluginContextOfWhat a plugin's view half receives. Generic over the Gantt and Dataset types so api/ has no import ring; api/gantt.ts binds the arguments once as PluginContext, which is the name a plugin author writes.
api/dataset-plugin.tsDatasetPluginContextOf, mergeEntryEdits(), moveEntryTo()The public Dataset-plugin contract, plus the one legal merge of two extenders' writes.
api/command.tsCommandOf, CommandContextOf, BuiltInCommandIdThe public command and keybinding contract, generic over TGantt.
api/attempt-mutation.tsattemptMutation()Runs a mutating body and returns false when beforeChange refuses, instead of throwing.
api/watch-all-errors.tswatchAllErrors()One handler over the Dataset error feed and the Gantt's, de-duplicated.
api/time-facade.tsisCoarserThan, readPlainTime, startOfLastCoveredDay, startOfNextDayNarrow slice of time/ that extensions/features/time-shading.ts, extensions/features/time-shading-covers.ts, and extensions/features/inline-editing.ts need without importing the public barrel (that barrel re-exports tooltips/inlineEditing).
api/decoration-facade.tsDecorationLayer, DecorationContext, DecorationInputNarrow slice of layout/'s decoration vocabulary that extensions/features/time-shading.ts needs without importing the public barrel (that barrel re-exports timeShading).
api/index.tsthe public surfaceThe allow-list with a sealed exports map. Re-exports Gantt/Dataset/the viewport models, the plugin and command contracts, the shipped built-ins, and the model/ and time/ types a consumer needs.

extensions/ — DOM — plugin runtime + shipped built-ins​

fileexportswhat it is for
extensions/plugin-runtime.tsPluginRuntime, RegistrationGateInstalls, uninstalls, and sets up Gantt plugins. The shell constructs one; a built-in never imports the shell.
extensions/install-dataset-plugins.tsinstallDatasetPlugins(), resolveSetupOrder()Orders Dataset plugins by requires and installs them onto a Dataset.
extensions/disposables.tsDisposableStoreA plugin's cleanup list. Runs on uninstall or Gantt.destroy().
extensions/commands.tsCommandRegistryCommand registry, generic over its Gantt type. Core commands and plugin commands share it.
extensions/keymap.tsKeymap, normalizeChord()Newest-first key handler resolver. Innermost popup wins.
extensions/popup.tscreatePopup()Anchoring, flipping, clamping, and dismissal. Tooltips and the context menu each hold their own instance, so both may show.
extensions/focus-trap.tsactivateFocusTrap()Tab cycling and focus restore for Popup's trap policy.
extensions/features/tooltips.tstooltips()Shipped tooltip plugin. Ordinary ChromePlugin; dogfoods the public contract.
extensions/features/context-menu.tscontextMenu()Shipped context-menu plugin. Asks ctx.view.dom what a node is.
extensions/features/menu-view.tsMenuItem, MenuEntryMenu vocabulary and ElementDescription builder. Pure; no DOM mount.
extensions/features/inline-editing.tsinlineEditing()Shipped cell editor. Owns a live control rather than a static Popup content tree.
extensions/features/date-input.tsDateInput, DateInputFactoryDefault date seam: wraps <input type="date">. No extra runtime dep.
extensions/features/time-shading-covers.tsTimeCover, DayOfWeek, daysOfWeek(), hours(), dates(), spans(), notCovered(), mergeSpans(), complement(), coarsestFloor()The five TimeCover builders behind timeShading(), plus the span merge/complement they and the rule level share.
extensions/features/time-shading.tstimeShading(), ShadingRule, CoverPredicateShipped plugin that shades regions of the time axis. Ordinary ChromePlugin, layer underBars always. Every band carries the .fg-time-shading Part, themed by the --fg-time-shading-fill Token.
extensions/plugin-order.tsresolveSetupOrder(), assertNoDuplicateIds(), OrderedPluginThe one place that answers "in what order do plugins set up?". A plugin's two halves share one requires list between them, so the sort belongs to neither install site alone. Generic over the plugin shape — it reads id and requires and nothing else.
extensions/index.tsbarrelRe-exports the runtime, commands, keymap, and the shipped built-ins.

not yet written​

fileexportswhat it is for
scheduling/index.ts—The first-party default plugin's pure engine + the EditExtender occupancy.