Wiki means quickly in Hawaiian... Probably.

jess 2026-04-15 09:38:07 -07:00
commit 4db96f5e3c
16 changed files with 1530 additions and 0 deletions

116
Architecture.md Normal file

@ -0,0 +1,116 @@
# Architecture
## Layers
```
┌────────────────────────────────────────────┐
│ Swift app (src/) │
│ NSWindow, NSMenu, CVDisplayLink │
│ File I/O, document browser, settings │
│ NSView with wgpu/Metal layer │
└────────────────────────────────────────────┘
│ FFI (viewport/include/acord.h)
┌────────────────────────────────────────────┐
│ Rust viewport (viewport/) │
│ iced 0.14 + iced_wgpu │
│ Editor state, blocks, table widgets │
│ text_widget (forked text_editor) │
│ Sidecar archive, crate export │
└────────────────────────────────────────────┘
│ acord-core dependency
┌────────────────────────────────────────────┐
│ Cordial interpreter (core/) │
│ Tokenizer, parser, AST, evaluator │
│ Type system, unit algebra │
│ Numerical solver │
│ Module system, sidecar persistence │
└────────────────────────────────────────────┘
```
## Block compositor
- `Vec<Box<dyn Block>>`
- Kinds: text, heading, HR, table, computed-table, computed-tree
- Iteration over recursion for selection paths
- Layered draw order: Below / Content / Above / Top
- Source: `viewport/src/block.rs`, `viewport/src/blocks.rs`
## TextWidget
Forked from iced's `text_editor`. Per-line `fill_paragraph()` interleaves anchored child Elements in a sequential stream.
- `Renderer` concretised to `iced_wgpu::Renderer`
- Source: `viewport/src/text_widget.rs`
## Sidecar archive
Base64-encoded ZIP in an HTML comment appended to the `.md`:
```
<!-- acord-archive
UEsDBBQAAAAIA…base64…AAAA
-->
```
| Entry | Contents |
| ----- | -------- |
| `config.toml` | Display metadata (col widths, row heights, cell formulas) |
| `src/<block>.cord` | Per-block TOML front-matter + source |
Save: regenerated fresh each write. Load: only `config.toml` consulted. Source: `viewport/src/sidecar.rs`.
## FFI
C ABI. Header: `viewport/include/acord.h` (cbindgen-generated).
| Function | Purpose |
| -------- | ------- |
| `viewport_create` / `viewport_destroy` | Lifecycle |
| `viewport_render` | Draw one frame |
| `viewport_resize` | Reconfigure surface |
| `viewport_mouse_event` / `viewport_key_event` / `viewport_scroll_event` | Input |
| `viewport_set_text` / `viewport_get_text` | Document replace / read (handles sidecar) |
| `viewport_set_lang` / `viewport_set_theme` | Highlighting, theme |
| `viewport_send_command` | Menu command by integer ID |
| `viewport_export_crate` | Write standalone Rust crate |
| `viewport_render_mode` | 0=Live, 1=Editor, 2=View |
| `viewport_free_string` | Free strings returned to Swift |
Mouse event sentinel: `button == 255` → pointer-move only.
## Command IDs
| ID | Command |
| -- | ------- |
| 1 | ToggleBold |
| 2 | ToggleItalic |
| 3 | InsertTable |
| 4 | SmartEval |
| 5 | Evaluate |
| 6 | TogglePreview |
| 7 | ZoomIn |
| 8 | ZoomOut |
| 9 | ZoomReset |
| 10 | FormatDocument |
| 11 | Live mode |
| 12 | Editor mode |
| 13 | View mode |
## Cordial interpreter
Hand-rolled tokenizer + recursive-descent parser + tree-walking evaluator.
- `use spice` pre-scanned from source text → tokenizer mode
- Implicit multiplication: number adjacent to identifier or `(` → Star token insertion
- `solve!` registry parallel to user fns; `eval_call` checks user fns → solved_fns → builtins
- Unit algebra: pure string manipulation
- Type system: round-trip coercion
Source: `core/src/interp.rs`, `core/src/eval.rs`.
## See also
- [Contributing](Contributing)
- [Cordial Modules](Cordial-Modules)

65
Contributing.md Normal file

@ -0,0 +1,65 @@
# Contributing
## Repository
`git.else-if.org/jess/Acord`
## Build and test
```sh
cargo test --workspace
./install.sh
```
~240 tests. Interpreter alone: ~220.
## Project layout
```
Acord/
├── core/ Cordial interpreter
│ └── src/
│ ├── interp.rs Tokenizer, parser, evaluator, type system
│ ├── eval.rs Module evaluation pipeline
│ ├── doc.rs Line classifier
│ ├── highlight.rs Tree-sitter integration
│ ├── persist.rs State index
│ ├── ffi.rs C ABI
│ └── document.rs
├── viewport/ Rust + iced editor
│ └── src/
│ ├── lib.rs FFI entry points
│ ├── bridge.rs Event shape conversions
│ ├── handle.rs Render loop, input dispatch
│ ├── editor.rs EditorState
│ ├── blocks.rs Markdown → block tree parser
│ ├── block.rs Block trait + LayeredView
│ ├── text_block.rs
│ ├── heading_block.rs
│ ├── hr_block.rs
│ ├── table_block.rs
│ ├── tree_block.rs
│ ├── text_widget.rs Forked text_editor + anchored compositor
│ ├── syntax.rs
│ ├── sidecar.rs
│ ├── module.rs
│ ├── selection.rs
│ ├── palette.rs
│ └── export.rs
└── src/ Swift shell
├── main.swift
├── AppDelegate.swift
├── AppState.swift
├── IcedViewportView.swift
├── RustBridge.swift
├── DocumentBrowserWindow.swift
├── SettingsView.swift
├── TitleBarView.swift
├── ConfigManager.swift
└── Theme.swift
```
## See also
- [Architecture](Architecture)
- [Cordial Reference](Cordial-Reference)

66
Cordial-Modules.md Normal file

@ -0,0 +1,66 @@
# Cordial Modules
## Boundaries
| Marker | Effect |
| ------ | ------ |
| `# Heading` (H1) | Root module — auto-imports into every other module |
| `## Heading` (H2) | Closes current, starts a new named module |
| `---` (HR) | Closes current, starts an unnamed module |
| `### Heading` (H3) | Does not split |
| `#### Heading` (H4) | Does not split |
## Naming
H2 heading text → module name, lowercased and snake-cased.
```markdown
## Trig Helpers
```
Module name: `trig_helpers`.
Unnamed modules auto-name from the first `fn` or `let`, or `_unnamed_N`.
## `use`
```cordial
use trig_helpers
use trig_helpers::deg_to_rad
use trig_helpers::*
```
Resolved via topological sort. Cycles fall back to evaluation without the cyclic dependency.
## Cross-block table references
| Heading above a table | Visibility |
| --------------------- | ---------- |
| `### Budget` (H3) | Global |
| `#### Budget` (H4) | Local to owning module |
```cordial
@trig_helpers::Lookup:A1
```
Positional fallbacks:
```cordial
@table_1
@table_3:B2
@my_module::table_2
```
## Naming conventions
| Element | Form |
| ------- | ---- |
| Note titles (filenames) | hyphen-form: `my-note.md` |
| Block / module titles | snake_form: `## Trig Helpers` |
| Unnamed blocks | `block_N` / `_unnamed_N` |
## See also
- [Cordial Tables](Cordial-Tables)
- [Cordial Reference](Cordial-Reference)
- [Architecture](Architecture)

111
Cordial-Quickstart.md Normal file

@ -0,0 +1,111 @@
# Cordial Quickstart
## Eval line
```cordial
/= 2 + 2
→ 4
```
## Variables and functions in scope
```cordial
let m = 100
let v = 9.8
/= 0.5 * m * v^2
→ 4802
```
## Types
```cordial
let count: int = 42
let bad: int = 3.7 // ERROR — lossy round-trip
```
Value types: `int`, `float`, `bool`, `str`. Anything else is a unit label — see [SPICE](Cordial-SPICE).
## Functions
Math-style, single expression:
```cordial
let area(w, h) = w * h
let hypot(a, b) = sqrt(a^2 + b^2)
/= area(4, 5) // → 20
/= hypot(3, 4) // → 5
```
Block-bodied:
```cordial
fn classify(x) {
if x > 0 {
return "positive"
} else if x < 0 {
return "negative"
}
return "zero"
}
/= classify(-7) // → "negative"
```
With types:
```cordial
fn area_typed(w: float, h: float) -> float {
return w * h
}
```
## Control flow
```cordial
let i = 0
let total = 0
while i < 10 {
total = total + i
i = i + 1
}
for x in [10, 20, 30] {
total = total + x
}
for n in 1..5 {
total = total + n
}
```
## Implicit multiplication
```cordial
2pi // 2 * pi
3x // 3 * x
4(a + b) // 4 * (a + b)
```
## Tables
```markdown
| Item | Cost | Tax (8%) |
| ------- | ----- | ------------ |
| Coffee | 4 | /= B2 * 0.08 |
| Bagel | 3 | /= B3 * 0.08 |
```
```cordial
/= sum(@Items:B2:B10)
```
## See also
- [Cordial Reference](Cordial-Reference)
- [Cordial Tables](Cordial-Tables)
- [Cordial SPICE](Cordial-SPICE)
- [Cordial solve!](Cordial-solve)
- [Cordial Modules](Cordial-Modules)
- [Keyboard Reference](Keyboard-Reference)

406
Cordial-Reference.md Normal file

@ -0,0 +1,406 @@
# Cordial Reference
## Eval lines
| Prefix | Result kind |
| ------ | ----------- |
| `/=` | Inline |
| `/=\|` | Table |
| `/=\` | Tree |
```cordial
/= 2 + 3 // → 5
/=| @Budget
/=\ [[1,2],[3,[4,5]]]
```
## Literals
### Numbers
```cordial
42
3.14
-7
1e-9
1E+3
.25
```
### Strings
```cordial
"hello"
"tab\there"
"quote: \"yes\""
```
Escapes: `\n`, `\t`, `\\`, `\"`.
### Booleans
```cordial
true
false
```
### Arrays
```cordial
[1, 2, 3]
[1, "two", true]
[]
```
### Ranges
```cordial
0..5 // [0, 1, 2, 3, 4]
```
Half-open. Capped at 10,000 elements.
### SPICE values
Requires `use spice`.
```cordial
100nF // [1e-7, "F"]
80Hz // [80, "HZ"]
10µF // [1e-5, "F"]
1n // [1e-9, ""]
```
### Implicit multiplication
```cordial
2pi // 2 * pi
3x // 3 * x
4(a + b) // 4 * (a + b)
2 pi // PARSE ERROR
```
## Operators
### Arithmetic
```cordial
a + b
a - b
a * b
a / b
a % b
a ^ b // right-associative
-a
```
### Comparison
```cordial
a == b a != b
a < b a > b
a <= b a >= b
```
### Logical
```cordial
a && b or a and b
a || b or a or b
!a or not a
```
Short-circuit on `&&`/`and` and `||`/`or`.
### Strip operator
```cordial
~true // 1
~false // 0
```
Spice `[n, unit]``n`. Bool → 0 / 1.
### `is` type-check
```cordial
1 is int // → true
1.5 is int // → false
1.5 is float // → true
true is bool // → true
[1,2] is array // → true
"x" is str // → true
```
### Cell assignment
Statement-level only.
```cordial
@Budget:A2 = "Housing"
@Sales:C3 = 1500
```
## Statements
### `let`
```cordial
let x = 5
let y = 2 + 3
```
### `let` with value-type annotation
```cordial
let x: int = 42
let y: float = 3.14
let z: int = 3.7 // ERROR
let b: bool = 0
let s: str = 42
```
Value types: `int`, `float`, `bool`, `str`.
### `let` with unit annotation
```cordial
use spice
let cap: F = 22n // [2.2e-8, "F"]
let freq: HZ = 60 // [60, "HZ"]
let h = 10
let hh: H = h // [10, "H"]
```
### Reassignment
```cordial
let x: int = 5
x = 10 // ok
x = 3.7 // ERROR
```
### `let f(x) = expr`
```cordial
let double(x) = x * 2
let hypot(a, b) = sqrt(a^2 + b^2)
```
### `fn`
```cordial
fn area(w, h) {
w * h
}
fn area_typed(w: float, h: float) -> float {
return w * h
}
fn charge(c: F, v: V) -> J {
0.5 * c * v^2
}
```
### Function inversion
Math form:
```cordial
let solve_l(f0, c) = l where lc_freq(l, c) = f0
```
Macro form:
```cordial
let solve_l = solve!(l, lc_freq)
let solve_l = solve!(l from lc_freq)
```
### `if` / `else if` / `else`
```cordial
if x > 0 {
y = 1
} else if x < 0 {
y = -1
} else {
y = 0
}
```
### `while`
Capped at 10,000 iterations.
```cordial
let i = 0
while i < 10 {
i = i + 1
}
```
### `for … in …`
```cordial
for i in 0..10 {
sum = sum + i
}
for x in [1, 2, 3] {
y = y + x
}
```
### `return`
```cordial
fn first_positive(a, b) {
if a > 0 { return a }
return b
}
```
### `use`
```cordial
use spice
use other_block::my_fn
use utils::*
```
## Expressions
### Function calls
```cordial
sin(x)
sum(@Budget:B2:B20)
my_fn(a, b, c)
```
### Indexing
```cordial
arr[0]
arr[-1]
"hello"[1] // "e"
```
### Cell references
```cordial
@Budget
@Budget:A1
@Budget:A2:A10
@Budget[A2:A10]
@Calculations::Revenue:B1
A1 // bare — in a cell formula
```
## Built-in functions
### Trigonometric and transcendental
| Function | |
| -------- | - |
| `sin`, `cos`, `tan` | radians |
| `asin`, `acos`, `atan` | inverse |
| `sqrt` | square root |
| `abs` | absolute value |
| `ln` | natural log |
| `log` | base-10 log |
Unit-transparent.
```cordial
sin(pi/2) // 1
sqrt(16) // 4
```
### Rounding
```cordial
floor(3.7) // 3
ceil(3.1) // 4
round(3.14159, 2) // 3.14
round(1234, -2) // 1200
```
### Aggregates
| Function | |
| -------- | - |
| `sum` | |
| `avg` | |
| `min` | |
| `max` | |
| `count` | |
| `std_devp` | population stddev |
| `std_devs` | sample stddev (≥ 2 values) |
```cordial
sum(@Budget:B2:B20)
avg([1, 2, 3, 4]) // 2.5
std_devs(@Sales)
```
### Array and string
| Function | |
| -------- | - |
| `len(x)` | length of array or string |
| `range(start, end)` | half-open integer range as array |
| `push(arr, val)` | append, returns new array |
```cordial
len([1, 2, 3]) // 3
len("hello") // 5
range(0, 5) // [0, 1, 2, 3, 4]
push([1, 2], 3) // [1, 2, 3]
```
## Built-in constants
| Name | Value |
| ---- | ----- |
| `pi` | `3.141592653589793` |
## Type system
### Value types
`int`, `float`, `bool`, `str`.
| Coercion | Result |
| -------- | ------ |
| `3.0 → int` | `3` |
| `3.7 → int` | error |
| `0 → bool` | `false` |
| `1 → bool` | `true` |
| `2 → bool` | error |
| `42 → str` | `"42"` |
| `"42" → int` | `42` |
| `"3.7" → int` | error |
### Unit types
Any identifier that isn't a value-type is a unit label. Spice-tagged values live as `[scalar, "UNIT"]` arrays.
## Resource limits
| Guard | Limit |
| ----- | ----- |
| Loop iterations | 10,000 |
| Call depth | 256 |
| `solve!` iterations | 100 |
| `solve!` epsilon | `1e-10` |
## See also
- [Cordial Quickstart](Cordial-Quickstart)
- [Cordial Modules](Cordial-Modules)
- [Cordial Tables](Cordial-Tables)
- [Cordial SPICE](Cordial-SPICE)
- [Cordial solve!](Cordial-solve)

109
Cordial-SPICE.md Normal file

@ -0,0 +1,109 @@
# Cordial: SPICE Notation
## Enable
```cordial
use spice
```
## SI prefixes
| Prefix | Multiplier | Name |
| ------ | ---------- | ---- |
| `m` / `M` | `×10⁻³` | milli |
| `u` / `U` / `µ` / `μ` | `×10⁻⁶` | micro |
| `n` / `N` | `×10⁻⁹` | nano |
| `p` / `P` | `×10⁻¹²` | pico |
## Units
| Unit | Quantity |
| ---- | -------- |
| `F` | Farads |
| `H` | Henries |
| `Hz` | Hertz |
| `V` | Volts |
| `A` | Amperes |
| `W` | Watts |
| `R` / `Ohm` | Ohms |
| `S` | Siemens |
| `J` | Joules |
Case-insensitive; uppercased on display.
## Literals
```cordial
use spice
100nF // 1e-7 F
80Hz // 80 Hz
10µF // 1e-5 F
22n // 2.2e-8 with no unit
```
Internally: `[scalar, "UNIT"]`.
## Unit annotations
```cordial
let cap: F = 22n // [2.2e-8, "F"]
let freq: Hz = 60 // [60, "HZ"]
let h = 10
let inductance: H = h // [10, "H"]
```
## Unit algebra
| Op | Rule |
| -- | ---- |
| `*` | same → squared; different → `A·B`; one-sided → carry |
| `/` | same → drop unit; one-sided → `A/X` or `1/X` |
| `+`, `-`, `%` | same or one-sided → carry; different → drop |
| `^` | `F²`, `F³`, `√F`, `F^N` |
```cordial
2F * 3H // [6, "F·H"]
6F / 3F // 2
6F / 2H // [3, "F/H"]
1F + 2F // [3, "F"]
1F + 2H // 3 — labels dropped
(2F)^2 // [4, "F²"]
```
## Function annotations
```cordial
use spice
fn charge(c: F, v: V) -> J {
return 0.5 * c * v^2
}
/= charge(100uF, 12V)
→ 7.2MJ
```
## Display formatting
3 significant figures. Closest SI prefix so mantissa ∈ `[1, 1000)`. Unit uppercased.
```
1.5e-7 F → 150NF
2.2e-5 F → 22UF
[707e-3, "F/H"] → 707M F/H
[5.0, "F²"] → 5 F²
```
## Strip operator
```cordial
let c = 22nF // [2.2e-8, "F"]
let raw = ~c // 2.2e-8
```
## See also
- [Cordial solve!](Cordial-solve)
- [Cordial Reference](Cordial-Reference)
- [Cordial Tables](Cordial-Tables)

89
Cordial-Tables.md Normal file

@ -0,0 +1,89 @@
# Cordial Tables
## Insert a table
**Cmd+T** at the cursor:
```markdown
| Header 1 | Header 2 | Header 3 |
| -------- | -------- | -------- |
| | | |
```
## Gestures
| Gesture | Action |
| ------- | ------ |
| Click | Select cell |
| Double-click | Enter edit mode |
| Tab / Enter | Navigate while editing |
| Esc | Leave edit mode |
| Cmd / Shift / Cmd+Shift + click | Toggle / Add / Remove in multi-cell selection |
| Drag | Rectangular selection |
| Corner cell click | Whole-table select |
| Right-click | Context menu |
| Column header edge | Column resize |
| Double-click a divider | Auto-fit |
## Cell formulas
```
| Item | Cost | Tax (8%) |
| ------- | ----- | ------------ |
| Coffee | 4 | /= B2 * 0.08 |
| Bagel | 3 | /= B3 * 0.08 |
```
Bare `A1`-style identifiers inside a formula resolve to the same table.
## Naming a table
```markdown
### Budget
| Category | Amount |
| -------- | ------ |
| Rent | 1500 |
| Food | 600 |
```
```cordial
/= sum(@Budget:B2:B3) // → 2100
```
H3 → global; H4 → module-local.
## Cell references from text blocks
```cordial
@Budget // whole table → 2D array
@Budget:A1 // single cell
@Budget:A2:B5 // rectangular range
@Budget[A2:B5] // alternative range syntax
@Calculations::Revenue:B1 // cross-block
```
## Mutation
```cordial
@Budget:C2 = "subtotal"
@Budget:C3 = sum(@Budget:B2:B5)
```
Statement-form only.
## Persistence
Markdown is the source of truth. Rich metadata — column widths, row heights, cell formulas — round-trips through a base64-encoded ZIP appended to the file inside an HTML comment:
```
<!-- acord-archive
UEsDBBQAAAAIAAAAIQDAm5...
-->
```
## See also
- [Cordial Reference](Cordial-Reference)
- [Cordial Modules](Cordial-Modules)
- [Keyboard Reference](Keyboard-Reference)

60
Cordial-solve.md Normal file

@ -0,0 +1,60 @@
# Cordial: solve!
## Math form
```cordial
let solve_for_l(f0, c) = l where lc_freq(l, c) = f0
```
## Macro form
```cordial
let solve_for_l = solve!(l, lc_freq)
let solve_for_l = solve!(l from lc_freq)
```
## Featured example — LC tank inversion
```cordial
use spice
// In Cordial, types can be arbitrary and are therefore assumed to be units
// when `use spice` is active. Hz, F, H are recognised ECE units; you could
// equally write `x: C` for Coulombs and the unit gets appended.
fn L(f: Hz, c: F) -> H {
return 1 / (((2pi * f)^2) * c)
}
// Math form: reads as an equation, mirrors how you'd write the algebra.
let fx(L, c) = f where L(f, c) = L
// Macro form: cleaner shorthand. Same result.
let f0 = solve!(f, L)
/= L(1000, 22n)
→ 1.15H
/= f0(1.15H, 22n)
→ ~1000
```
## Solver parameters
| Guard | Limit |
| ----- | ----- |
| Newton iterations | 100 |
| Convergence epsilon | `1e-10` |
| Initial guess | `1.0` |
| Damping | step-halving on NaN / Inf / error |
## Limits
- Single variable.
- Continuous, differentiable source function.
- Symbolic inversion not implemented — numerical backend only.
## See also
- [Cordial Reference](Cordial-Reference)
- [Cordial SPICE](Cordial-SPICE)
- [Cordial Modules](Cordial-Modules)

77
Crate-Export.md Normal file

@ -0,0 +1,77 @@
# Crate Export
## Trigger
**File ▸ Export as Rust Library…** or **Cmd+Shift+E**. Folder + crate name prompt via `NSSavePanel`.
## Output
```
my-note/
├── Cargo.toml
├── build.sh
├── install.sh
├── README.md
├── .gitignore
└── src/
├── main.rs
├── lib.rs
└── blocks/
├── block_N.cord
└── snake_name.cord
```
| File | Contents |
| ---- | -------- |
| `Cargo.toml` | lib + bin, git dep on `acord-core` |
| `build.sh` | `cargo build --release` |
| `install.sh` | Copy binary to `~/.acord/bin`, print PATH hints |
| `README.md` | Block inventory |
| `src/main.rs` | REPL (`:list` shows bindings, `:q` quits) |
| `src/lib.rs` | `pub fn load() -> Interpreter` |
| `src/blocks/*.cord` | One file per block |
Crate-name normalisation: lowercased, `_``-`, non-alphanumeric stripped.
## Run the exported crate
```sh
cd my-note
./build.sh
./install.sh
my-note
```
REPL:
```
> ke(100, 9.8)
4802
> :list
m: number = 100
v: number = 9.8
ke: fn(m, v)
> :q
```
## Embed in another crate
```toml
[dependencies]
my-note = { path = "../my-note" }
```
```rust
use my_note;
fn main() {
let mut interp = my_note::load();
let result = interp.eval_expr_str("ke(50, 7.0)").unwrap();
println!("{}", result.display());
}
```
## See also
- [Cordial Modules](Cordial-Modules)
- [Architecture](Architecture)

54
Document-Browser.md Normal file

@ -0,0 +1,54 @@
# Document Browser
## Open
**Ctrl+B** toggles the browser window.
## Storage
Default: `~/.acord/notes/`. Configurable in Settings (`autoSaveDirectory`). Subdirectories render as folders.
Titled notes: `<title>.md`. Untitled: `<uuid>.md`.
## Layout
| Element | |
| ------- | - |
| Breadcrumb bar | Clickable path segments |
| Cards | File preview (first 20 lines, sidecar stripped) or folder summary |
| Single click | Select |
| Double-click | Open file or enter folder |
| Drag | Move file onto folder |
| Right-click card | Open, Rename, Duplicate, Move to Trash, Reveal in Finder |
| Right-click folder | Open, Rename, Move to Trash, Reveal in Finder |
| Right-click empty area | New Folder, Reveal in Finder |
## Card scaling
Cmd+= / Cmd+- / Cmd+0 when the browser is key.
## Autosave
Writes every 100ms. Two paths:
- Viewport timer — reads straight from the editor, bypasses the data binding.
- `didSet` — on programmatic text change.
Skipped when the note is "effectively blank" (empty or default untouched table).
## Settings
| Setting | Options |
| ------- | ------- |
| Theme | Auto, Dark (mocha), Light (latte) |
| Line Numbers | On, Off, Vim |
| Auto-Save directory | Folder picker |
| Zoom level | Cmd+= / Cmd+- / Cmd+0 |
Stored in `~/.acord/config.json`.
## See also
- [Install](Install)
- [The Editor](The-Editor)
- [Keyboard Reference](Keyboard-Reference)

62
First-Note.md Normal file

@ -0,0 +1,62 @@
# First Note
## Open the app
```sh
open /Applications/Acord.app
```
## Write markdown
```markdown
# Today's notes
Some thoughts.
| Quantity | Value |
| -------- | ----- |
| Mass | 100 |
| Velocity | 9.8 |
```
**Cmd+T** inserts a table.
## Evaluate a line
```cordial
/= 2 + 2
→ 4
/= sin(pi / 2)
→ 1
```
```cordial
let m = 100
let v = 9.8
/= 0.5 * m * v^2
→ 4802
```
```cordial
fn ke(m, v) {
return 0.5 * m * v^2
}
/= ke(100, 9.8)
→ 4802
```
## Save
**Cmd+S**. Autosave also runs every 100ms to `~/.acord/notes/`.
## See also
- [Cordial Quickstart](Cordial-Quickstart)
- [Cordial Reference](Cordial-Reference)
- [Cordial Tables](Cordial-Tables)
- [Cordial SPICE](Cordial-SPICE)
- [Cordial solve!](Cordial-solve)
- [Keyboard Reference](Keyboard-Reference)

72
Home.md Normal file

@ -0,0 +1,72 @@
# Acord
Here's the 'sales' pitch:
"Hi there, do you enjoy casually solving project euler problems and are tired of using the spotlight bar as your primary calculator?" - Then this might be your kinda thing.
First, but least importantly, Acord is a native markdown editor... with a 'small' expression language baked in.
- Lines starting with `/=` evaluate — the result is printed inline below it.
- Tables are real, editable, and can carry formulas.
- Runs on Rust + iced + wgpu in a Swift shell on macOS.
- Sports my wicked cool Rust DSL, Cordial. Trust me, you'll love it. It's like Rust but you never have to remember any semi-colons. Don't panic - indentation, case, these things don't matter much either. It's a sensible, salad days sort of syntax.
The war - against the evil, as always.
Here's an analogy. Against the evil that is unsavory programming languages and — things... those things I don't like. Just generally, "things," sucky things. Evil.
Against this evil, let's say Rust is the best weapon made by humankind. It's what you need, it's the truth, it is the goodness within us all.
Python sure wanted to help, and golly, in its own ways it has. It was something like... the hand grenade. Effective at the start of the war, devastating it both its sweeping victories and its massive shortcomings. But I am grateful nonetheless, they did the trick when they were most needed. Not evil. Just bad. Normal bad. If the enemy is evil and the other side is good, that makes warfare `just` bad. This is just an analogy. I'm no expert on wars, or analogies for that matter. So bear with me.
Well, assuming everything I have said is true and just, then this,
- Cordial -
...would be like a garden hoe. Sure, you'll find plenty of them in a war. But that's just cuz lots of the country is farmland. Sure, you could do some damage with it. But mostly, its a garden hoe. It just does its job. Its no swiss army knife, nor scalpel, or even X-acto. It's the pocket knife you have, the one in your hand cuz there's an apple to cut and there's no need to get out the good knife, both the tool and the tool-be'd are already in hand, they'll handle it... man.
Happy? Me too.
I built Acord because I needed something between vim and the IDE — not a webapp (VS Code), not a stack of plugins held together with hope. A real, focused, native tool for thinking in. Notes I write turn into running calculators turn into working code, all in the same file.
## What you can do here
Math
```cordial
let m = 100
let v = 9.8
/= 0.5 * m * v^2
→ 4802
```
With `use spice` enabled, write with units and SPICE conventions.
```cordial
use spice
fn L(f: Hz, c: F) -> H {
return 1 / (((2pi * f)^2) * c)
}
/= L(1000, 22n)
→ 1.15H
```
Take any function and invert it on a chosen variable in one line:
```cordial
let f0 = solve!(f, L)
/= f0(1.15H, 22n)
→ 1000
```
Tables work the way you'd expect from Numbers or Excel: cells, formulas, ranges, references between tables, mutation from text blocks. Headings carry scope, so a table under `### Budget` becomes `@Budget` from anywhere in the document.
## Where to start
- **Install** [Install](Install) → [First Note](First-Note)
- **Cordial Language** [Cordial Quickstart](Cordial-Quickstart) → [Cordial Reference](Cordial-Reference)
- [Tables and formulas](Cordial-Tables)
- [SPICE notation for engineering values](Cordial-SPICE)
- [solve! for inverting functions](Cordial-solve)
- [Architecture](Architecture) → [Contributing](Contributing)
## Status
macOS only for now (Apple Silicon, macOS 14+). Windows is the next target. As in, I just installed a VM, will be done by the weekend.

40
Install.md Normal file

@ -0,0 +1,40 @@
# Install
## Requirements
- macOS 14 (Sonoma) or later, Apple Silicon
- Xcode command-line tools: `xcode-select --install`
- Rust toolchain via [rustup](https://rustup.rs)
- `rsvg-convert`: `brew install librsvg`
## Build and install
```sh
git clone https://git.else-if.org/jess/Acord.git
cd Acord
./install.sh
```
Build without installing:
```sh
./build.sh
```
## Where things live
| Path | |
| ---- | - |
| `/Applications/Acord.app` | App bundle |
| `~/.acord/notes/` | Auto-saved notes |
| `~/.acord/config.json` | Settings |
## Other platforms
Windows: in progress. Linux: not planned.
## See also
- [First Note](First-Note)
- [Cordial Quickstart](Cordial-Quickstart)
- [Keyboard Reference](Keyboard-Reference)

130
Keyboard-Reference.md Normal file

@ -0,0 +1,130 @@
# Keyboard Reference
## File
| Combo | Action |
| ----- | ------ |
| Cmd+N | New window |
| Cmd+Shift+N | New note |
| Cmd+O | Open… |
| Cmd+S | Save |
| Cmd+Shift+S | Save As… |
| Cmd+Shift+E | Export as Rust Library |
| Cmd+, | Settings… |
| Cmd+Q | Quit |
## Edit
| Combo | Action |
| ----- | ------ |
| Cmd+Z | Undo |
| Cmd+Shift+Z | Redo |
| Cmd+X / Cmd+C / Cmd+V | Cut / Copy / Paste |
| Cmd+A | Select all (escalating) |
| Cmd+B | Bold wrap |
| Cmd+I | Italic wrap |
| Cmd+T | Insert table |
| Cmd+E | Smart eval |
| Cmd+P | Toggle markdown preview |
| Cmd+F | Toggle find/replace |
| Cmd+G | Find next |
| Cmd+Shift+G | Find previous |
| Cmd+Shift+F | Format document |
Cmd+A: first press block-local; second press whole-document.
## Zoom
| Combo | Action |
| ----- | ------ |
| Cmd+= / Cmd++ | Zoom in |
| Cmd+- | Zoom out |
| Cmd+0 | Actual size |
## Modes
| Combo | Action |
| ----- | ------ |
| Ctrl+I | Editor mode |
| Ctrl+/ | Live mode |
| Ctrl+Esc | View mode |
| `i` (View) | Editor |
| `/` (View) | Live |
| Esc | Chain: dismiss menu → dismiss find → exit cell edit → cycle mode |
## Text navigation
| Combo | Action |
| ----- | ------ |
| Cmd+Up / Cmd+Down | Document start / end |
| Cmd+Shift+Up / Cmd+Shift+Down | Select to document start / end |
| Opt+Backspace / Opt+Delete | Delete prev / next word |
| Tab / Shift+Tab | Indent / outdent 4 spaces |
| Arrow / Home / End / PgUp / PgDn | Standard motion |
| Ctrl+B / Ctrl+F | Left / right (Emacs) |
| Ctrl+A / Ctrl+E | Line start / end (Emacs) |
| Ctrl+H / Ctrl+D | Backspace / Delete (Emacs) |
## Whole-document (after Cmd+A escalation)
| Combo | Action |
| ----- | ------ |
| Backspace / Delete | Clear every block's content |
| Cmd+Backspace | Wipe document to one empty text block |
## Table cell (selected, not editing)
| Gesture | Action |
| ------- | ------ |
| Click | Select cell |
| Double-click | Enter edit mode |
| Cmd+click | Toggle in selection |
| Shift+click | Add to selection |
| Cmd+Shift+click | Remove from selection |
| Drag | Rectangular selection |
| Right-click | Context menu |
| Corner cell click | Whole-table select |
| Column letter click | Column reorder drag |
| Row number click | Row reorder drag |
| Column header edge drag | Resize column |
| Double-click on divider | Auto-fit |
| Printable char | Overwrite + enter edit |
| Tab / Shift+Tab | Focus next / prev cell (Tab adds column at end of row) |
| Enter | Focus next row (adds row at last) |
| Arrow Up / Down | Move focus; edge-escapes to text block |
| Arrow Left / Right | Move focus within row |
| Cmd+Opt+Arrow Up / Down | Insert row above / below |
| Cmd+Opt+Arrow Left / Right | Insert column left / right |
| Cmd+Opt+Backspace | Delete current row |
| Cmd+Opt+Shift+Backspace | Delete current column |
| Backspace / Delete (single cell) | Empty the cell |
| Backspace / Delete (whole-table) | Clear every cell |
| Cmd+Backspace (whole-table) | Delete table |
| Esc | Exit edit mode |
## Find / replace bar
| Combo | Action |
| ----- | ------ |
| Cmd+F | Toggle bar |
| Enter (in find) | Find next |
| Enter (in replace) | Replace one |
| Cmd+G / Cmd+Shift+G | Next / previous |
| Esc | Hide bar |
## Menu bar
| Menu | Items |
| ---- | ----- |
| Acord | About Acord, Settings…, Quit Acord |
| File | New Window, New Note, Open…, Save, Save As…, Export as Rust Library…, Open Storage Directory |
| Edit | Undo, Redo, Cut, Copy, Paste, Select All, Bold, Italic, Insert Table, Smart Eval, Find…, Format Document |
| Render | Live, Editor, View |
| View | Document Browser (Ctrl+B), Zoom In, Zoom Out, Actual Size |
| Window | Minimize, Zoom |
## See also
- [The Editor](The-Editor)
- [Cordial Tables](Cordial-Tables)
- [Document Browser](Document-Browser)

44
The-Editor.md Normal file

@ -0,0 +1,44 @@
# The Editor
## Modes
| Mode | Behaviour |
| ---- | --------- |
| Live (default) | Blocks rendered, eval runs on a 300ms debounce, tables interactive |
| Editor | Raw markdown, single text editor, no eval |
| View | Read-only rendered. `i` → Editor, `/` → Live |
## Eval-line forms
| Prefix | Result |
| ------ | ------ |
| `/=` | Inline value (or `⚠` error) |
| `/=\|` | Read-only computed table |
| `/=\` | Tree widget |
## Title bar
Double-click the title to edit. Enter commits; Esc cancels. If the document's first line is `# heading`, it is rewritten; otherwise a fresh `# title` line is prepended.
## Find / replace
**Cmd+F** toggles. Live `current/total` match count. Case-insensitive. Cmd+G / Cmd+Shift+G for next / previous. Esc hides.
## Smart edits
- Smart-backspace: deletes back to the previous 4-space tab stop in leading whitespace.
- Auto-indent on Enter.
- Auto-indent after `{` / `[` / `(` on line end.
- Auto-dedent on `}` / `]` / `)` typed at a whitespace-only prefix.
- Tab / Shift+Tab indent / outdent to the nearest 4-space stop.
## Undo / redo
200-entry bounded stack. Edits within 500ms of the previous same-kind edit coalesce. Structural operations push an explicit snapshot.
## See also
- [Keyboard Reference](Keyboard-Reference)
- [Cordial Reference](Cordial-Reference)
- [Document Browser](Document-Browser)
- [Architecture](Architecture)

29
_Sidebar.md Normal file

@ -0,0 +1,29 @@
**Acord**
- [Home](Home)
- [Install](Install)
- [First Note](First-Note)
**The editor**
- [The Editor](The-Editor)
- [Document Browser](Document-Browser)
- [Keyboard Reference](Keyboard-Reference)
**Cordial**
- [Quickstart](Cordial-Quickstart)
- [Reference](Cordial-Reference)
- [Modules](Cordial-Modules)
- [Tables](Cordial-Tables)
- [SPICE notation](Cordial-SPICE)
- [solve!](Cordial-solve)
**Power tools**
- [Crate Export](Crate-Export)
**Contributors**
- [Architecture](Architecture)
- [Contributing](Contributing)