Header menu logo issie

global Namespace

Modules Description

AppMessages

 The words Issie says to the user, where they are long enough to be worth reading as prose.

 WHAT IS HERE. The Info window's four written tabs, the waveform simulator's five help panels,
 the memory help, the twenty field explanations in the Properties pane, and the confirmation
 popups whose body is several sentences rather than one - thirty-five messages, about 25,000
 characters. They were spread through the view functions that show them, written as React
 element trees with the words threaded between `str` and `bSpan` and `li` - fine to render and
 very hard to read, which is a problem for text whose only job is to read well. Written as
 markdown here, the whole of Issie's long-form help can be read end to end and reviewed as
 writing.

 WHAT IS NOT HERE, and should not be moved in.

 - Short labels, button captions and headings. A caption is easier to judge beside the button it
   sits on than in a list of thirty other strings.
 - The Catalogue's component tooltips. They are short, they are one per component, and each sits
   beside the component type it describes, which is what makes them easy to keep true.
 - One-line confirmation bodies - "The current sheet has unsaved changes." A sentence that short
   belongs with the buttons it is explaining.
 - `failwithf` and `Log` messages. They are addressed to whoever is maintaining Issie, not to
   the user, and belong at the point that fails.
 - Anything assembled from a design: the components in a combinational loop, the ports of a
   mismatched instance. The sentence around such a list can live here; the list cannot.
 - The Keyboard Shortcuts tab, which is generated from the shortcut table so that it cannot
   drift from the keys that actually fire.

 THE RISK THIS RUNS. Text kept away from the code that shows it goes stale - Issie has had that
 exact bug, a help menu item whose name had drifted from the panel it opened, so that choosing
 it said "Feature not explained". What limits it is that only the words moved: which message to
 show, and when, stays at the point that shows it, so a message cannot be reached by a route
 this module knows nothing about. Every entry also carries a comment saying what it is and where
 it appears, which is what makes an entry that no longer matches its use site noticeable while
 reading.

 That is weaker than a check. `MarkdownTests` reads every message here and fails on one that
 will not render, but nothing yet fails on an entry that has quietly stopped being used, or on a
 comment that has stopped being true. Both would be worth adding - the first is a source scan
 for each entry's name, of the kind `SourceHygiene` already does for printf.

 Markdown is rendered by `Markdown.render` - see that module for the subset supported. Tooltips
 are the exception: they are drawn by CSS from a `data-tooltip` attribute, which can hold text
 and nothing else, so tooltip entries are plain sentences with no markup.

BlockHelpers

Bridge (Module)

The main-process half of Issie's renderer bridge. Every operation the renderer is allowed to ask the operating system for is registered here, as a named channel with a fixed shape. The renderer half is src/Preload/preload.js, and the two are meant to be read together: nothing should be exposed there that is not answered here, and nothing answered here should take a request general enough to stand in for `require`. Handlers use event.returnValue, the synchronous form, because the renderer's file wrappers return values from inside Elmish update and cannot await. get-user-data in Main.fs already worked this way before any of this existed.

Bridge (Module)

The renderer's half of the bridge to the main process. Partner to src/Preload/preload.js and src/Main/Bridge.fs. This module is the only place the renderer reads anything the operating system knows, and it exists so that the rest of the code asks for `Bridge.staticDir` rather than reaching for `__static`, `process.platform` or `@electron/remote` - none of which survive contextIsolation. Everything here is a value rather than a function. These are constants for the life of the process, so the preload fetches them once and this module just reads the result: no round trip, no ordering to get wrong, and five operations that never have to exist on the bridge at all.

BuildUartHelpers

The debug UART, as the draw block sees it. These were direct calls into IS-uart.js, imported into the renderer bundle. That file requires the native `usb` module, which a renderer without node integration cannot load at all, so it now lives in the main process and these are messages to it - see src/Main/Bridge.fs. The signatures are unchanged, which is the whole reason this port was cheap: the operations were already asynchronous, already took nothing but a viewer count, and already shared no objects with their caller. Nothing in SheetUpdate.fs had to change.

BuildView

BusWidthInferer

BusWire

BusWireRoute

BusWireRoutingHelpers

BusWireSeparate

BusWireUpdate

BusWireUpdateHelpers

CanvasExtractor

CanvasStateAnalyser

CatalogueView

CodeEditorHelpers

CommonTypes

ComponentLibraries

ComponentSlots

ConstraintReduceView

ContextMenuBuilder

Building and popping up the right-click menus, for the main process only. The menu DATA - which menus exist and what is on them - stays in ContextMenus.fs, which both processes compile: the renderer needs it to ask for a menu by name and to recognise the item that comes back. Only the part that builds an Electron menu is here, because it needs the electron module, and requiring that is what a renderer without node integration cannot do.

CustomCompPorts

DevHarness

 Driving Issie from outside it, for development and for automated checking.

 Published as `window.issieDev` in a debug build, and reached from a terminal through
 `scripts/drive.js`. It exists because the alternative - synthesising DOM events and reading
 rendered text back - is both slow and wrong often enough to mislead: a click has to find the
 right element, a wait has to guess how long a render takes, and text scraped from the DOM can
 be a frame out of date while looking authoritative.

 Four things, which is what that experience says are needed:

   onNextRender   when the update has been applied AND the view has run, so a caller can wait
                  for the app rather than for a stopwatch
   send           a named command, dispatched as the message the UI would have sent
   state          what the app currently is, as data rather than as rendered text
   simRefs        what is holding a simulation, which is the question the DOM cannot answer

 `send` takes a NAME from a fixed table rather than a serialised Msg. A Msg is an F# union
 carrying models, canvases and functions; nothing useful survives a round trip through JSON, and
 a general dispatch-anything surface in a debug build is a hazard for the sake of messages
 nobody wanted to send. Add a row to `commands` when a new one is needed - that is the point.

DiagramMainView

DiagramStyle

DrawHelpers

DrawModelType

Editor

ElectronModule

The `electron` module itself, for the main process only. These bindings used to live in ElectronAPI.fs, which both projects compile - the renderer needs its several hundred type definitions. But a binding is an import, and `import * from "electron"` runs when the file loads, so the renderer was requiring electron simply by using its types. A renderer without node integration cannot do that, and it is not a lazy failure: it throws as the bundle loads, before any of Issie runs. So the types stayed shared and the module came here, where requiring it is exactly right. If the renderer ever appears to need one of these values again, that is the signal that something wants a bridge operation instead.

ErrorCheck

ErrorCheckHelpers

ErrorCheckProcedural

EvalAlgebraic

EvalCompiled

 Per-component reducers, chosen and bound once when the simulation is built.

 fastReduce works out afresh, for every component of every clock step, things that were
 settled when the simulation was built: which component type this is, whether it is on the
 uint32 or the bigint path, which of its inputs are bigints, what its bus masks are, and
 which arrays its ports live in. A reducer built here has all of that already: it closes
 over the step arrays themselves and over the constants, and its body is only the work that
 actually differs from step to step.

 reducerFor returns None for a component type it does not handle, and the caller falls back
 to fastReduce. So this file can be filled in a type at a time, and the fallback is always
 the definition of what a reducer must do.

 Two rules the closures depend on, both guaranteed by the caller (see installReducers):

  - The step arrays captured here must be the ones the simulation actually uses. Reducers
    are therefore installed after every re-linking pass, including the one
    addWavesToFastSimulation does for custom components, and only for components that are
    reduced. Capturing an array that is later re-pointed would silently simulate the wrong
    signal.
  - Values in the step arrays are already within their bus width (the masking invariant), so
    a reducer masks its result exactly when its own operation can overflow, and reads
    without masking. On the uint32 path a width of exactly 32 needs care: 1u <<< 32 is 1u.

EvalKernel

EvalReference

EvilHoverCache

FastBuild

FastCreate

FastExtract

FastOrder

FastRun

FastValidate

FilesIO

FileUpdate

GraphBuilder

GraphMerger

HashMap

Helpers

JSHelpers

KeyBindings

Turns a key press into an action, using the table in KeyTypes and the context the user is in. This is the only place in Issie that should listen for keys. It replaces five mechanisms that grew independently and never agreed with each other: the Electron menu accelerators, the document.onkeydown reassigned on every render in SheetDisplay, the addEventListener subscription in Renderer, the one React handler in SelectedComponentView, and the arrow-key intercept in Update. KeyTypes says what the shortcuts are; this module says what they do and delivers them.

KeyTypes

The one place every keyboard shortcut in Issie is described. This module is deliberately pure: no Fable.React, no ElectronAPI, no ModelType, no Browser.Dom. It says *what* the shortcuts are, not what they do and not how they are delivered. The binding from an identity to an action lives in UI/KeyBindings.fs, which is the only module that needs the model. Keeping the two apart is what lets the help table, the renderer menus and (later) the context menus all be generated from one list instead of drifting apart, which is what happened to the hand-written table in UIPopups.fs. Platform is always a parameter, never read here, so this module has no dependency on how the host discovers it.

Main

Markdown

A small markdown renderer for Issie's in-app help. Issie's longer help text - the Info window, the waveform simulator's help panels, the bodies of confirmation popups - used to be written as React element trees: `str` and `bSpan` and `li` interleaved with the words, several hundred lines of it. Read as prose it was unreadable, which is a problem for text whose whole job is to read well. It is now written as markdown in AppMessages and rendered here. The subset is not a guess: it is what that text was already doing, counted. Headings, bold, italic, inline code, links, bullet and numbered lists, and tables - which is core GitHub markdown with nothing left over. There are no images and no nested lists, so there is no support for them; a line that tries will render as its own text rather than silently vanish. TWO THINGS THIS DELIBERATELY DOES NOT DO. It does not produce an HTML string. Rendering markdown by handing HTML to `dangerouslySetInnerHTML` is the usual shortcut, and in a renderer with node integration, for messages that interpolate sheet names the user chose, it is a bad trade. `render` builds React elements, so there is no path from message text to executed markup. It does not open links itself. A markdown link becomes an anchor with an `onClick` supplied by the caller, because in Electron an ordinary `href` navigates the application window away from Issie. Keeping the handler out here also keeps this module free of Electron and of anything above it in compile order. `parse` is pure and returns data, so the tests can read every message in AppMessages under plain .NET without a browser. `render` is the only part that needs one.

MemoryData

MemoryEditorView

MenuHelpers

MiscMenuView

ModelHelpers

ModelType

NearleyBindings

Notifications

NumberHelpers

Optics

ParameterAnalysis

ParameterTypes

ParameterView

PathHelpers

Pure path arithmetic, shared by the Fable and .NET builds. These were node's `path` under Fable and System.IO.Path under .NET - two implementations that already disagreed (node's join normalises, Path.Join does not), and one of which disappears with contextIsolation since `path` is a node module the renderer will not be able to require. Joining strings is not a privileged operation and has no business crossing a process boundary, so rather than becoming ten more bridge calls at 189 call sites this is one implementation for both targets. It follows node's semantics, which is what the app was built against, and Tests/Issie.Tests/PathHelperTests.fs holds it to them. Separator handling is platform-dependent in the way node's is: on Windows both `/` and `\` divide segments and output uses `\`; on POSIX only `/` divides and a backslash is an ordinary filename character. The leading root - a drive, a UNC share, or `/` - is found but never rewritten by dirname and basename, because node does not rewrite it either, and UNC paths matter here: Issie is run from networked locations on cluster machines.

Playground

PopupHelpers

PropertiesHelp

What the fields in the Properties pane mean, and the label element that says so. The Catalogue explains every component before you place it, in a tooltip written as a sentence. The pane where you then configure the component you placed explained nothing at all: it showed "Width (bits)", "Optional Ports", "LSB" and left the user to work out the rest. This closes that gap without adding prose to the pane itself, which is short on room. The text is keyed by the label the field displays, so a field acquires its explanation simply by being labelled - no call site has to pass one, and the same label used in two places is explained the same way in both. A label with no entry here renders exactly as it did before. Keep each entry to what the field does and, where there is one, the thing people get wrong.

RamStore

 How a read/write memory's contents are held while a simulation runs.

 See [docs/dev/ramRepresentation.md] for why this exists and what it is worth. In short: the
 contents used to be a `Map` and a fresh snapshot of it was stored for every
 clock step, so a write cost an AVL path copy plus two boxed `BigInt`s, a read cost a descent
 with boxed comparisons, and a long waveform simulation retained every version it had ever
 made. Here

 - a read during simulation is an array index,
 - a write appends two numbers to shared flat arrays,
 - the value an address held at any past step is a binary search of that address's own writes,
   whose cost does not depend on how far the cursor moved, and
 - nothing at all is allocated per step.

 **Storage is CSR.** Every address that is ever written is given a *slot* number, and a slot's
 writes are one contiguous run of the shared `IStep`/`IVal` arrays, delimited by `Start`. The
 alternative - a growable list per address - costs a record and two list objects per address,
 which is around 230 bytes for what may be a single 8-byte write. That is affordable for one
 big RAM and is not for a hundred small ones, which between them have just as many addresses.
 Recent writes go to a small tail and are folded in by `compact`.

 **Where this file sits.** F# compile order is the dependency layering, and
 `SimGraphTypes.SimulationComponentState` names this type, so it has to be compiled before
 `SimGraphTypes.fs` and cannot live in the `FastSim` block with the rest of the simulator. It
 depends on nothing but `CommonTypes`, which is what makes that possible.

 **Mutability.** This is the same layer as the step arrays, which are already mutable, and
 [docs/mutableState.md] allows it for a measured performance reason - the whole point here is
 to stop allocating. Nothing mutable escapes: `Memory1` remains the immutable type everywhere
 outside the running simulation, and `toMemory` builds one on demand.

Renderer

RotateScale

SelectedComponentView

Sheet

SheetCreator

SheetDescription

SheetDisplay

SheetLayout

SheetSnap

SheetUpdate

SheetUpdateHelpers

SimGraphTypes

SimpleJsonDotNet

Deserialises the JSON encoding used by Fable.SimpleJson, which is what the app writes into every .dgm file. Without this, nothing running under .NET can open a sheet, because Thoth.Json.Net - what `Helpers.jsonStringToState` otherwise uses there - cannot read what the app wrote. The two encodings disagree about unions (Thoth writes an array; SimpleJson writes a bare string for a nullary case and a single-property object otherwise), about options (Thoth wraps the value; SimpleJson writes the value itself, or null) and about maps with structural keys (SimpleJson writes an array of pairs). Rather than enumerate the `SavedInfo` schema - which would mean every `ComponentType` case, and a new one every time a component is added - this walks the target type by reflection, so it covers whatever the types say. Fable never sees any of this: under Fable the app calls SimpleJson itself, and Newtonsoft is not available. Under .NET it reaches this project transitively through Thoth.Json.Net.

SimTypes

SimulationGraphAnalyser

SimulationView

Simulator

StepSimulationTop

Symbol

SymbolHelpers

SymbolPortHelpers

SymbolReplaceHelpers

SymbolResizeHelpers

SymbolUpdate

SymbolView

SynchronousUtils

TestParser

TestWorker

TimeHelpers

TopMenuView

TruthTableCreate

TruthTableReduce

TruthTableTypes

TruthTableUpdate

TruthTableView

UIPopups

Update

UpdateHelpers

Verilog

VerilogAST

VerilogTypes

Version

WaveSimHelpers

Miscellaneous helpers used tby waveform simulator

WaveSimHierarchy

The collapsed design hierarchy the wave selector shows. A design whose sheets instantiate one another multiplies out: seven sheets of a few instances each is tens of thousands of instances, and a selector with one entry per instance grows with that rather than with the design somebody wrote. So the hierarchy shown has one node per SHEET at each point in the tree, and the user says which instance of it they mean. This module works out, once per render, everything both panes of the selector need: which nodes there are, which instances each one could show, and which one it is showing. Both panes read the same answer, so they cannot disagree about it.

WaveSimNavigation

All the code that determines which clock cycles are viewed in the simulator. basic functions are zooming and panning (moving + or - in time). Additional function is a sample-based zoom for viewing very long waveforms.. Also implement cursor control.

WaveSimRams

RAM display in waveform simulator

WaveSimSelect

WaveSimSelectHelpers

WaveSimStyle

Functions to style the DOM elements used in waveform simulator

WaveSimSVGs

Functions to make SVGs of waveforms from FastSimulation data

WaveSimTop

Top-level functions for Waveform Simulator

WaveSimTypes

WaveSimWaveforms

showWaveforms and subfunctions to display in DOM the waveforms Waveform SVGs themselves are generated from simulation in WaveSimWaves module

WorkerInterface

Type something to start searching.