Go to file
Kinneyzhang 04a16eeb18 merge: reconcile retained-root metadata and unmount coverage
Integrate 127aa7a with the newer property-specific Host contract from 805323a. Preserve owned role and ARIA checks, callback and Behavior composition, and caller overrides for ordinary metadata. Carry the historical live-buffer unmount assertions into the current render-port revision test. Regression execution was waived; syntax and diff checks passed.
2026-09-08 21:37:53 +08:00
docs fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00
examples feat: close M0 core interaction contracts 2026-08-31 15:18:26 +08:00
postmortem merge: reconcile retained-root metadata and unmount coverage 2026-09-08 21:37:53 +08:00
scripts fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00
tests merge: reconcile retained-root metadata and unmount coverage 2026-09-08 21:37:53 +08:00
.gitignore feat: establish unified etaf view foundation 2026-08-05 00:36:52 +08:00
AGENTS.md fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00
DESIGN.md refactor: remove manual app precompilation 2026-08-25 20:03:06 +08:00
DESIGN.zh-CN.md refactor: remove manual app precompilation 2026-08-25 20:03:06 +08:00
etaf-actions.el feat(reactive): isolate dispatcher contexts 2026-09-01 02:17:13 +08:00
etaf-behavior.el feat: implement unified etaf architecture 2026-08-05 02:56:13 +08:00
etaf-compiler.el docs: clear final M0 checkdoc warnings 2026-08-31 16:27:31 +08:00
etaf-component.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf-context.el feat: establish unified Component authoring core 2026-08-28 23:21:27 +08:00
etaf-data.el chore: freeze verified etaf baseline before C1b 2026-09-05 05:07:38 +08:00
etaf-events.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf-generation.el feat: commit semantic generations with versioned CAS 2026-09-01 00:46:09 +08:00
etaf-host.el refactor: require SPI v2 for runtime mounts and Host attachment 2026-09-05 05:25:29 +08:00
etaf-observer.el perf: expose scoped ETAF operation observation 2026-08-28 00:50:58 +08:00
etaf-performance.el fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00
etaf-reactive.el chore: freeze verified etaf baseline before C1b 2026-09-05 05:07:38 +08:00
etaf-render-port.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf-renderer.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf-resource.el feat: establish unified Component authoring core 2026-08-28 23:21:27 +08:00
etaf-retirement.el chore: freeze verified etaf baseline before C1b 2026-09-05 05:07:38 +08:00
etaf-runtime.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf-scheduler.el fix(runtime): preserve scheduler work and selected publication 2026-09-01 08:11:51 +08:00
etaf-theme-tp.el perf: precompile apps and trace retained runtime updates 2026-08-25 17:16:56 +08:00
etaf-view.el feat: unify component views and public render boundaries 2026-09-06 13:53:07 +08:00
etaf.el build: require the v2-only renderer dependency closure 2026-09-05 05:25:30 +08:00
Makefile test: provide reusable existing-frame Ebox resize benchmark 2026-09-06 19:29:51 +08:00
README.md fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00
README.zh-CN.md fix: isolate performance records and qualify GUI callback measurements 2026-09-07 01:28:26 +08:00

ETAF

ETAF builds text applications from reusable Components above the independent Ebox layout and rendering engine.

Start with etaf-view and etaf-mount. Properties precede children in (name :property value ... child ...); property values are ordinary Elisp. Evaluate the complete example, switch to *etaf-hello*, and activate “Say hello”:

;;; -*- lexical-binding: t; -*-
(require 'etaf)

(etaf-mount
 "*etaf-hello*"
 (etaf-view
  (column
    (text :font-weight 'bold "Hello")
    (box :ref 'hello :role 'button :tab-index 0
         :on-press (lambda () (message "Hello ETAF"))
      "Say hello"))))

A Component receives declared props and optional content through slots. Use the exact name passed to etaf-define-component; the registry creates no aliases. (expr FORM) evaluates one child expression. In a structural child position it may return nil, text, a typed Host or Component View, or a proper sequence of those values. Inside text, an expression must return a string.

;;; -*- lexical-binding: t; -*-
(require 'etaf)

(etaf-define-component demo-card (&key title)
  :view
  (column
    (text :font-weight 'bold (expr title))
    (slot)
    (slot :name 'footer)))

(etaf-mount
 "*etaf-card*"
 (etaf-view
  (demo-card :title "Account"
    (text "Connected")
    (slot :name 'footer (text "Footer")))))

Add :setup when a Component owns state. It runs once per retained instance; :render uses ordinary Elisp to capture handles before returning etaf-view. The shorter :view form compiles the same View model.

;;; -*- lexical-binding: t; -*-
(require 'etaf)

(etaf-define-component demo-counter ()
  :setup (etaf-ref 0)
  :render
  (let ((count (etaf-state)))
    (etaf-view
     (column
       (text (expr (format "Count: %d" (etaf-value count))))
       (box :ref 'increment :role 'button :tab-index 0
            :on-press (lambda () (cl-incf (etaf-value count)))
         "Increment")))))

(etaf-mount "*etaf-counter*" (etaf-view (demo-counter)))

Keep etaf-value reads inside the property or expr that should update. Event callbacks capture ordinary lexical locals; call etaf-state during rendering. Use a lexical-binding .el file for reusable application code. etaf-node is available for programmatic View builders. Context, Data, Behavior, and named Actions are optional capabilities; simple callbacks need no Action registration.

Use exact catalog names such as etaf-button after (require 'etaf-ui). Core does not load .etaf files: Playground treats them as inert structure, with its explicit companion registration handling executable Elisp.

Performance records

ETAF provides an independent, opt-in, application-neutral timing recorder. It consumes public Runtime observer reports without advice or private cross-package probes. Runtime operations such as Event, Action, mount, flush, and unmount are recorded automatically; Ebox, TP, Data, Resource, and SQLite provider stages inside the same operation are correlated by sequence.

(require 'etaf-performance)
;; In a buffer with a mounted ETAF Runtime:
(etaf-performance-mode 1)
;; Use any mounted ETAF application normally.
(etaf-performance-show)

For an interactive capture, run M-x etaf-performance-clear first. After reproducing the operations, press c in the panel (or run M-x etaf-performance-copy-report) to copy a complete report. Press w (or run M-x etaf-performance-export) to save the same report as an .eld file. The portable report includes the Emacs/display environment, power source, low-power mode, native-JIT state, system load, grouped p50/p95/max, individual operations, GC deltas, and ordered provider stages. The panel header exposes the same environment context so a machine-wide slowdown is not mistaken for one package hotspot.

The *ETAF Performance* panel shows operation IDs, generation changes, total latency, GC deltas, and each flat provider stage in sequence. Provider stages may overlap, so they are not presented as exclusive/self time. Records are bounded by etaf-performance-max-records; disabling the mode only detaches the Runtime observer and never rewrites functions. etaf-performance-summary computes operation p50/p95/max statistics on demand, while etaf-performance-operation-stage-summary groups one operation's flat stages by provider category. etaf-performance-records returns defensive operation and stage snapshots; caller mutation cannot rewrite retained history. Pass a numeric observer runtime ID to etaf-performance-records to select one mount's history even when a buffer name is reused. Summary and report functions use all retained records when called without an argument; an explicit nil means an empty selection. The exported environment describes report generation, not each historical operation. These synchronous operation durations do not measure physical input-to-presentation latency; use the GUI measurement entry in scripts/README.md for per-action condition checks.

Use etaf-performance-call-operation or etaf-performance-with-operation to trace an arbitrary operation that has no built-in public boundary. Both delegate to the same Runtime operation boundary; they do not create a second timer.

Executable examples

The examples/ directory contains three core-only best-practice applications: retained state and Actions, Data Controller ownership, and Resource error/cleanup lifecycle. They are byte-compiled and driven through mounted public event paths by make check.

(add-to-list 'load-path "/path/to/github/etaf/examples")
(require 'etaf-counter-example)
(etaf-counter-example-open)

Documentation

Independent packages

Package Role
etaf-ui Official Component catalog: Button, Checkbox, Label, Panel, and DataGrid.
etaf-sqlite Concrete SQLite Data Source; the Data Controller remains in ETAF core.
etaf-playground ETAF examples, with the UI catalog loaded only when requested.
ebox-playground Ebox-only layout examples, independent from ETAF.

There is no separate etaf-data install: Data is a core ETAF capability. There is no generic etaf-adapters package: other databases, services, files, or ORMs should provide concrete Data Source packages with explicit names.

Load and verify

Install ECSS 0.1.0 and TP 2.0.0 before Ebox 3.0.0, then install ETAF 0.2.1. ETAF declares TP directly because Host final-accept authority uses the TP transaction contract. Rendering requires Ebox framework SPI v2; a missing, malformed, or incompatible provider fails during ETAF bootstrap.

ETAF snapshots one immutable v2 render port for the Emacs process. During an ordered upgrade it also accepts Ebox's transitional dual-capability TP manifest because that manifest contains the required v2 protocol. ETAF never dispatches through the retired v1 capability.

During development, load the sibling Ebox checkout before ETAF:

(add-to-list 'load-path "/path/to/github/ecss")
(add-to-list 'load-path "/path/to/github/tp")
(add-to-list 'load-path "/path/to/github/ebox")
(add-to-list 'load-path "/path/to/github/etaf")
(require 'etaf)

Run the complete local gate:

make check EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs

The core gate byte-compiles the implementation, runs the core/Data/Resource tests, and checks documentation/API boundaries. Run make check in the sibling etaf-ui, etaf-sqlite, etaf-playground, and ebox-playground repositories for their independent gates; none is loaded by the core facade.