mkui 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkui-0.3.0 → mkui-0.3.2}/PKG-INFO +35 -6
- {mkui-0.3.0 → mkui-0.3.2}/README.md +34 -5
- {mkui-0.3.0 → mkui-0.3.2}/mkui/__init__.py +1 -1
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/config/client.toml +92 -5
- mkui-0.3.2/mkui/static/examples/mkio-table/server.toml +137 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/core.js +1 -1
- {mkui-0.3.0 → mkui-0.3.2}/mkui.egg-info/PKG-INFO +35 -6
- {mkui-0.3.0 → mkui-0.3.2}/mkui.egg-info/SOURCES.txt +2 -1
- {mkui-0.3.0 → mkui-0.3.2}/pyproject.toml +1 -1
- mkui-0.3.2/tests/test_examples.py +460 -0
- mkui-0.3.0/mkui/static/examples/mkio-table/server.toml +0 -77
- {mkui-0.3.0 → mkui-0.3.2}/LICENSE +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/__main__.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/control.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/history/config/client.toml +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/history/seed.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/history/server.toml +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/history/static/index.html +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/library-js/index.html +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/control.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/seed.csv +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/seed.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/static/app.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/mkio-table/static/index.html +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/standalone-json/client.json +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/examples/standalone-json/index.html +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/auth.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/components/app.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/components/frame.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/components/menubar.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/components/statusbar.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/components/workspace.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/index.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/layout/drag.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/layout/tree.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/layouts.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/copy.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/expr.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/expressions.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/history.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/icons.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/layouts.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/links.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/rich.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/lib/timeparse.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/mkio-bridge.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/widgets/button.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/widgets/mkio-history.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/widgets/mkio-table.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/widgets/mkui-dialog.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/src/widgets/text.js +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui/static/styles/mkui.css +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui.egg-info/dependency_links.txt +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui.egg-info/entry_points.txt +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/mkui.egg-info/top_level.txt +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/setup.cfg +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/tests/test_cli.py +0 -0
- {mkui-0.3.0 → mkui-0.3.2}/tests/test_control.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkui
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Config-driven, zero-dependency web GUI framework with dockable panes
|
|
5
5
|
Author: Mark Kim
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -211,7 +211,7 @@ mkio server and verifies its identity. Connection is two-phase:
|
|
|
211
211
|
"name": "order-book",
|
|
212
212
|
"version": "1.0",
|
|
213
213
|
"protocol": "1.0",
|
|
214
|
-
"mkio": "0.
|
|
214
|
+
"mkio": "0.3",
|
|
215
215
|
"expr": "1"
|
|
216
216
|
},
|
|
217
217
|
"connected": { "status.message": "Connected", "status.background": null },
|
|
@@ -227,6 +227,15 @@ to the server's `_mkio` service). When `expect` is absent, the `_mkio`
|
|
|
227
227
|
query still runs to confirm it is an mkio server and to populate
|
|
228
228
|
`mkio.server.*` state paths (name, version, protocol, mkio).
|
|
229
229
|
|
|
230
|
+
Semver matching is stricter below 1.0, where the minor number carries the
|
|
231
|
+
breaking changes: `"0.3"` accepts 0.3.x and rejects 0.4.0, so pinning
|
|
232
|
+
`mkio` at a pre-1.0 version means every minor release of the library
|
|
233
|
+
invalidates the pin. A stale pin is quiet — the app connects, the
|
|
234
|
+
`incompatible` map is applied, and the statusbar reads "Wrong server"
|
|
235
|
+
while nothing is wrong with the server. Leave `mkio` out unless the app
|
|
236
|
+
truly depends on the library's version, and pin `name` and `version`,
|
|
237
|
+
which are your own, instead.
|
|
238
|
+
|
|
230
239
|
The same reply says what the server can do, which lands in
|
|
231
240
|
`mkio.server.services` (service name → protocol),
|
|
232
241
|
`mkio.server.versioned` (the tables whose every change it records — see
|
|
@@ -802,7 +811,8 @@ python3 -m http.server 8000
|
|
|
802
811
|
# http://localhost:8000/examples/library-js/
|
|
803
812
|
```
|
|
804
813
|
|
|
805
|
-
The mkio-table example requires [mkio](https://pypi.org/project/mkio/)
|
|
814
|
+
The mkio-table example requires [mkio](https://pypi.org/project/mkio/) 0.3.0 or
|
|
815
|
+
later (its `orders` table is versioned):
|
|
806
816
|
|
|
807
817
|
```
|
|
808
818
|
cd mkui/static/examples/mkio-table
|
|
@@ -824,6 +834,8 @@ Its `orders` table is `versioned = true`, so mkio records every version of every
|
|
|
824
834
|
|
|
825
835
|
**All Orders** broadcasts the selected orders' `id` and `symbol`; the **Child Orders** frame listens for the id on its `parent_id` column and **Pending** for the symbol, so a selection filters both (the chips on each toolbar show and pause the links, and a saved layout keeps them). `python control.py` serves the example in place of `mkio serve` and rewires those links from Python a few seconds after a browser connects. The seeder places child orders under pending parents (and some under other children), so the **Order Tree** tab shows the same orders nested by `parent_id`: expand a parent with its caret, place a child order under the selected row with *+ Child Order*, and try the View menu's expand/collapse actions and alt/option-clicking a filter button for the scope row. The toolbar buttons show both styling forms: the **Pending** tab's Fill and Cancel wear a plain `style` map (green, and black on red in capitals) at all times, while the All Orders Cancel turns red only once a pending order is selected, through a rule conditioned on `enabled`.
|
|
826
836
|
|
|
837
|
+
The `orders` table is `versioned = true`, so mkio records every version of every row in `orders__history`, and `server.toml` writes the four services that expose it — one record's chain, where a record sits in it, the live tape, and the table as at a moment. **All Orders** names them in a `history` block and shows `_mkio_version` beside the data, which puts *Undo*, *Redo* and *As of…* on its toolbar: filling or cancelling an order moves it to a second version, Undo steps it back with a confirmation that says what will change, and *As of…* shows the book as it stood before the seeder's last few minutes, read-only until *Live* re-subscribes. Undo, Redo and *As of…* arrive with the block; the version timeline does not, so All Orders adds a **History** button of its own beside them — a `table.history` action over one selected record — and Record → History… is the same step from the menubar. Either opens that order's version timeline with its diff and blame views. Record → Audit Tape opens a parked query pane over the history table — every recorded version, newest first, live as they land — whose *Go to order* selects the record in All Orders rather than filtering it (a key All Orders' `status` and `notional` filters hide is reported back rather than revealed). An existing `orders.db` picks all this up on the next start: `auto_migrate = "safe"` creates the history table and records every live row as its baseline version.
|
|
838
|
+
|
|
827
839
|
## Project layout
|
|
828
840
|
|
|
829
841
|
```
|
|
@@ -861,9 +873,10 @@ mkui/ Python package (pip install mkui)
|
|
|
861
873
|
standalone-json/ Loaded from a static config
|
|
862
874
|
library-js/ Built imperatively from JS
|
|
863
875
|
mkio-table/ Live table backed by mkio query/subpub services
|
|
864
|
-
(config-only derived columns, styling rules,
|
|
865
|
-
enable conditions
|
|
866
|
-
|
|
876
|
+
(config-only derived columns, styling rules,
|
|
877
|
+
enable conditions, and record history over a
|
|
878
|
+
versioned orders table; static/app.js registers
|
|
879
|
+
an expression function and the order-detail pane)
|
|
867
880
|
history/ Versioned table: the audit tape, the version
|
|
868
881
|
timeline with diff and blame, record undo/redo,
|
|
869
882
|
and the table as at a moment (config only)
|
|
@@ -872,13 +885,29 @@ tests/
|
|
|
872
885
|
layout.test.js Layout tree unit tests (node:test)
|
|
873
886
|
state.test.js State + connection lifecycle tests (node:test)
|
|
874
887
|
table.test.js mkio-table pane tests (node:test)
|
|
888
|
+
pane-filters.test.js Configured filters, sort, visible: specs and API (node:test)
|
|
889
|
+
links.test.js LinkHub and table-to-table linking (node:test)
|
|
890
|
+
history.test.js Versioned tables: capabilities, spec, chain logic (node:test)
|
|
891
|
+
history-pane.test.js mkio-history pane: versions, diff, blame (node:test)
|
|
892
|
+
copy.test.js Clipboard grids: TSV quoting and HTML (node:test)
|
|
893
|
+
timeparse.test.js Time detection, parsing, bounds, presets (node:test)
|
|
894
|
+
icons.test.js Icon library: every name resolves to an SVG (node:test)
|
|
875
895
|
dialog.test.js Dialog expression + submission tests (node:test)
|
|
876
896
|
auth.test.js Authentication module + state lifecycle tests (node:test)
|
|
877
897
|
expressions.test.js Expression conformance fixtures + mkui wrapper tests (node:test)
|
|
878
898
|
vendor-sync.test.js lib/expr.js and expr_cases.json match the installed mkio (node:test)
|
|
879
899
|
rich.test.js Rich text type, mkui function library, clipboard HTML (node:test)
|
|
880
900
|
styles.test.js mkui.css layout invariants (node:test)
|
|
901
|
+
tabbar.test.js Tab bar: drag, reorder, overflow, rename (node:test)
|
|
902
|
+
tab-focus.test.js Active tab group and focus routing (node:test)
|
|
903
|
+
edit-routing.test.js edit.* actions and shortcuts reach the pane (node:test)
|
|
904
|
+
pointer-guards.test.js Button/modifier guards on every drag start (node:test)
|
|
905
|
+
windows-menu.test.js Windows and layouts menu expansion (node:test)
|
|
881
906
|
layouts.test.js Saved layouts: format, stores, workspace restore, menu (node:test)
|
|
882
907
|
version.test.js The four version strings agree (node:test)
|
|
883
908
|
test_cli.py CLI init/serve tests (unittest)
|
|
909
|
+
test_control.py ControlService: push actions to browsers (unittest)
|
|
910
|
+
test_examples.py Each example config against its server: the
|
|
911
|
+
[mkio.expect] pins, pane services, history
|
|
912
|
+
blocks, menu and frame pane ids (unittest)
|
|
884
913
|
```
|
|
@@ -191,7 +191,7 @@ mkio server and verifies its identity. Connection is two-phase:
|
|
|
191
191
|
"name": "order-book",
|
|
192
192
|
"version": "1.0",
|
|
193
193
|
"protocol": "1.0",
|
|
194
|
-
"mkio": "0.
|
|
194
|
+
"mkio": "0.3",
|
|
195
195
|
"expr": "1"
|
|
196
196
|
},
|
|
197
197
|
"connected": { "status.message": "Connected", "status.background": null },
|
|
@@ -207,6 +207,15 @@ to the server's `_mkio` service). When `expect` is absent, the `_mkio`
|
|
|
207
207
|
query still runs to confirm it is an mkio server and to populate
|
|
208
208
|
`mkio.server.*` state paths (name, version, protocol, mkio).
|
|
209
209
|
|
|
210
|
+
Semver matching is stricter below 1.0, where the minor number carries the
|
|
211
|
+
breaking changes: `"0.3"` accepts 0.3.x and rejects 0.4.0, so pinning
|
|
212
|
+
`mkio` at a pre-1.0 version means every minor release of the library
|
|
213
|
+
invalidates the pin. A stale pin is quiet — the app connects, the
|
|
214
|
+
`incompatible` map is applied, and the statusbar reads "Wrong server"
|
|
215
|
+
while nothing is wrong with the server. Leave `mkio` out unless the app
|
|
216
|
+
truly depends on the library's version, and pin `name` and `version`,
|
|
217
|
+
which are your own, instead.
|
|
218
|
+
|
|
210
219
|
The same reply says what the server can do, which lands in
|
|
211
220
|
`mkio.server.services` (service name → protocol),
|
|
212
221
|
`mkio.server.versioned` (the tables whose every change it records — see
|
|
@@ -782,7 +791,8 @@ python3 -m http.server 8000
|
|
|
782
791
|
# http://localhost:8000/examples/library-js/
|
|
783
792
|
```
|
|
784
793
|
|
|
785
|
-
The mkio-table example requires [mkio](https://pypi.org/project/mkio/)
|
|
794
|
+
The mkio-table example requires [mkio](https://pypi.org/project/mkio/) 0.3.0 or
|
|
795
|
+
later (its `orders` table is versioned):
|
|
786
796
|
|
|
787
797
|
```
|
|
788
798
|
cd mkui/static/examples/mkio-table
|
|
@@ -804,6 +814,8 @@ Its `orders` table is `versioned = true`, so mkio records every version of every
|
|
|
804
814
|
|
|
805
815
|
**All Orders** broadcasts the selected orders' `id` and `symbol`; the **Child Orders** frame listens for the id on its `parent_id` column and **Pending** for the symbol, so a selection filters both (the chips on each toolbar show and pause the links, and a saved layout keeps them). `python control.py` serves the example in place of `mkio serve` and rewires those links from Python a few seconds after a browser connects. The seeder places child orders under pending parents (and some under other children), so the **Order Tree** tab shows the same orders nested by `parent_id`: expand a parent with its caret, place a child order under the selected row with *+ Child Order*, and try the View menu's expand/collapse actions and alt/option-clicking a filter button for the scope row. The toolbar buttons show both styling forms: the **Pending** tab's Fill and Cancel wear a plain `style` map (green, and black on red in capitals) at all times, while the All Orders Cancel turns red only once a pending order is selected, through a rule conditioned on `enabled`.
|
|
806
816
|
|
|
817
|
+
The `orders` table is `versioned = true`, so mkio records every version of every row in `orders__history`, and `server.toml` writes the four services that expose it — one record's chain, where a record sits in it, the live tape, and the table as at a moment. **All Orders** names them in a `history` block and shows `_mkio_version` beside the data, which puts *Undo*, *Redo* and *As of…* on its toolbar: filling or cancelling an order moves it to a second version, Undo steps it back with a confirmation that says what will change, and *As of…* shows the book as it stood before the seeder's last few minutes, read-only until *Live* re-subscribes. Undo, Redo and *As of…* arrive with the block; the version timeline does not, so All Orders adds a **History** button of its own beside them — a `table.history` action over one selected record — and Record → History… is the same step from the menubar. Either opens that order's version timeline with its diff and blame views. Record → Audit Tape opens a parked query pane over the history table — every recorded version, newest first, live as they land — whose *Go to order* selects the record in All Orders rather than filtering it (a key All Orders' `status` and `notional` filters hide is reported back rather than revealed). An existing `orders.db` picks all this up on the next start: `auto_migrate = "safe"` creates the history table and records every live row as its baseline version.
|
|
818
|
+
|
|
807
819
|
## Project layout
|
|
808
820
|
|
|
809
821
|
```
|
|
@@ -841,9 +853,10 @@ mkui/ Python package (pip install mkui)
|
|
|
841
853
|
standalone-json/ Loaded from a static config
|
|
842
854
|
library-js/ Built imperatively from JS
|
|
843
855
|
mkio-table/ Live table backed by mkio query/subpub services
|
|
844
|
-
(config-only derived columns, styling rules,
|
|
845
|
-
enable conditions
|
|
846
|
-
|
|
856
|
+
(config-only derived columns, styling rules,
|
|
857
|
+
enable conditions, and record history over a
|
|
858
|
+
versioned orders table; static/app.js registers
|
|
859
|
+
an expression function and the order-detail pane)
|
|
847
860
|
history/ Versioned table: the audit tape, the version
|
|
848
861
|
timeline with diff and blame, record undo/redo,
|
|
849
862
|
and the table as at a moment (config only)
|
|
@@ -852,13 +865,29 @@ tests/
|
|
|
852
865
|
layout.test.js Layout tree unit tests (node:test)
|
|
853
866
|
state.test.js State + connection lifecycle tests (node:test)
|
|
854
867
|
table.test.js mkio-table pane tests (node:test)
|
|
868
|
+
pane-filters.test.js Configured filters, sort, visible: specs and API (node:test)
|
|
869
|
+
links.test.js LinkHub and table-to-table linking (node:test)
|
|
870
|
+
history.test.js Versioned tables: capabilities, spec, chain logic (node:test)
|
|
871
|
+
history-pane.test.js mkio-history pane: versions, diff, blame (node:test)
|
|
872
|
+
copy.test.js Clipboard grids: TSV quoting and HTML (node:test)
|
|
873
|
+
timeparse.test.js Time detection, parsing, bounds, presets (node:test)
|
|
874
|
+
icons.test.js Icon library: every name resolves to an SVG (node:test)
|
|
855
875
|
dialog.test.js Dialog expression + submission tests (node:test)
|
|
856
876
|
auth.test.js Authentication module + state lifecycle tests (node:test)
|
|
857
877
|
expressions.test.js Expression conformance fixtures + mkui wrapper tests (node:test)
|
|
858
878
|
vendor-sync.test.js lib/expr.js and expr_cases.json match the installed mkio (node:test)
|
|
859
879
|
rich.test.js Rich text type, mkui function library, clipboard HTML (node:test)
|
|
860
880
|
styles.test.js mkui.css layout invariants (node:test)
|
|
881
|
+
tabbar.test.js Tab bar: drag, reorder, overflow, rename (node:test)
|
|
882
|
+
tab-focus.test.js Active tab group and focus routing (node:test)
|
|
883
|
+
edit-routing.test.js edit.* actions and shortcuts reach the pane (node:test)
|
|
884
|
+
pointer-guards.test.js Button/modifier guards on every drag start (node:test)
|
|
885
|
+
windows-menu.test.js Windows and layouts menu expansion (node:test)
|
|
861
886
|
layouts.test.js Saved layouts: format, stores, workspace restore, menu (node:test)
|
|
862
887
|
version.test.js The four version strings agree (node:test)
|
|
863
888
|
test_cli.py CLI init/serve tests (unittest)
|
|
889
|
+
test_control.py ControlService: push actions to browsers (unittest)
|
|
890
|
+
test_examples.py Each example config against its server: the
|
|
891
|
+
[mkio.expect] pins, pane services, history
|
|
892
|
+
blocks, menu and frame pane ids (unittest)
|
|
864
893
|
```
|
|
@@ -27,6 +27,24 @@ items = [
|
|
|
27
27
|
{ label = "Order Detail", action = "pane.show", args = "order-detail" },
|
|
28
28
|
]
|
|
29
29
|
|
|
30
|
+
[[menubar]]
|
|
31
|
+
label = "Record"
|
|
32
|
+
items = [
|
|
33
|
+
# The version timeline for whatever All Orders has selected: the chain
|
|
34
|
+
# as a table, with a diff and a blame view under it. One history pane
|
|
35
|
+
# per table, re-pointed as the selection moves rather than re-opened.
|
|
36
|
+
{ label = "History…", action = "table.history", args = { pane = "all-orders" } },
|
|
37
|
+
# `keys` names the record instead, so a menu item can be a deep link.
|
|
38
|
+
{ label = "History of order 1", action = "table.history", args = { pane = "all-orders", keys = ["1"] } },
|
|
39
|
+
{ sep = true },
|
|
40
|
+
# The steps the All Orders toolbar's Undo and Redo take, on the focused
|
|
41
|
+
# pane. Deliberately without a shortcut: this writes to shared state.
|
|
42
|
+
{ label = "Undo record", action = "edit.undo" },
|
|
43
|
+
{ label = "Redo record", action = "edit.redo" },
|
|
44
|
+
{ sep = true },
|
|
45
|
+
{ label = "Audit Tape", action = "pane.show", args = "audit-tape" },
|
|
46
|
+
]
|
|
47
|
+
|
|
30
48
|
[[menubar]]
|
|
31
49
|
label = "View"
|
|
32
50
|
items = [
|
|
@@ -106,8 +124,12 @@ protocol = "query"
|
|
|
106
124
|
# exists in no row, so it must be listed in `columns` explicitly. Derived
|
|
107
125
|
# values are what the table sorts, filters, and copies. SPREAD_BPS is an
|
|
108
126
|
# application function registered in static/app.js.
|
|
109
|
-
columns = ["id", "ts", "side", "symbol", "qty", "price", "notional", "status"]
|
|
110
|
-
|
|
127
|
+
columns = ["id", "_mkio_version", "ts", "side", "symbol", "qty", "price", "notional", "status"]
|
|
128
|
+
# `_mkio_version` is mkio's own column — the cursor into the record's
|
|
129
|
+
# chain, and the one thing the row itself says about its history. Naming
|
|
130
|
+
# it here is what shows it; mkio's columns are hidden otherwise, and
|
|
131
|
+
# `labels` overrides the default heading ("Version") as for any column.
|
|
132
|
+
labels = { ts = "Time", _mkio_version = "Ver" }
|
|
111
133
|
values = { notional = "ROUND(qty * price, 2)" }
|
|
112
134
|
# Which columns show, in this order. `id` and `price` start hidden — the
|
|
113
135
|
# Columns button at the header's right edge (badge: 2) opens the picker
|
|
@@ -115,14 +137,14 @@ values = { notional = "ROUND(qty * price, 2)" }
|
|
|
115
137
|
# columns still work: `notional` is derived from the hidden
|
|
116
138
|
# `price`, and sorts and filters on hidden columns still apply. Without
|
|
117
139
|
# `visible`, every column shows, including ones added later.
|
|
118
|
-
visible = ["ts", "side", "symbol", "qty", "notional", "status"]
|
|
140
|
+
visible = ["ts", "side", "symbol", "qty", "notional", "status", "_mkio_version"]
|
|
119
141
|
# Column groups categorise the picker (sections with show/hide-the-group
|
|
120
142
|
# toggles; ungrouped columns fall into "Other"). Structure only — `visible`
|
|
121
143
|
# says what shows. With hundreds of columns this is how a user finds the
|
|
122
144
|
# few they want; the config's `visible` is the curated default they can
|
|
123
145
|
# always reset to.
|
|
124
146
|
groups = [
|
|
125
|
-
{ label = "Order", columns = ["id", "ts", "side", "symbol"] },
|
|
147
|
+
{ label = "Order", columns = ["id", "_mkio_version", "ts", "side", "symbol"] },
|
|
126
148
|
{ label = "Pricing", columns = ["qty", "price", "notional"] },
|
|
127
149
|
]
|
|
128
150
|
# Mirror the current row into app state; the order-detail pane follows it.
|
|
@@ -169,6 +191,26 @@ side = "${BADGE(value, IF(value == 'Buy', 'green', 'red'))}"
|
|
|
169
191
|
notional = "${NUM(value, digits: 2, group: TRUE)}"
|
|
170
192
|
status = "${ICON(CASE(value == 'filled', 'check', value == 'cancelled', 'close', 'clock'))} ${TITLE(value)}"
|
|
171
193
|
|
|
194
|
+
# Where this table's record history lives. `versioned = true` on the
|
|
195
|
+
# server's orders table is what records it; mkio advertises no history
|
|
196
|
+
# table and writes no service for one, so this block is the only way the
|
|
197
|
+
# client can know — every name here is a service in server.toml.
|
|
198
|
+
#
|
|
199
|
+
# What it buys: Undo and Redo on the toolbar, stepping the selected orders
|
|
200
|
+
# along their own versions with a confirmation that says what will change;
|
|
201
|
+
# an *As of…* button that shows the table as it stood at a moment,
|
|
202
|
+
# read-only, until *Live* re-subscribes; and Record → History…, the
|
|
203
|
+
# version timeline for the selected order.
|
|
204
|
+
[panes.all-orders.history]
|
|
205
|
+
table = "orders" # checked against the server's versioned tables
|
|
206
|
+
key = ["id"] # asked of the server when omitted
|
|
207
|
+
versions = "order_versions" # one record's chain: the history pane
|
|
208
|
+
state = "order_state" # where the row sits in it: arms Redo
|
|
209
|
+
feed = "order_history" # the audit tape
|
|
210
|
+
undo = { service = "orders", op = "undo" }
|
|
211
|
+
redo = { service = "orders", op = "redo" }
|
|
212
|
+
asOf = "order_as_of" # the table as it stood at a moment
|
|
213
|
+
|
|
172
214
|
[[panes.all-orders.buttons]]
|
|
173
215
|
label = "+ New Order"
|
|
174
216
|
enable = { connected = true }
|
|
@@ -235,6 +277,17 @@ style = [
|
|
|
235
277
|
]
|
|
236
278
|
action = { type = "transaction", service = "orders", op = "cancel", data = { id = "${row.id}" } }
|
|
237
279
|
|
|
280
|
+
# Undo, Redo and "As of…" arrive on their own — the `history` block above
|
|
281
|
+
# is what puts them there. The version timeline does not: it opens through
|
|
282
|
+
# the `table.history` action, so a table that wants it next to them says
|
|
283
|
+
# so. `unit = "row"` means exactly one record, which is what a history is
|
|
284
|
+
# about; with no `keys` the action takes the pane's current selection.
|
|
285
|
+
[[panes.all-orders.buttons]]
|
|
286
|
+
label = "History"
|
|
287
|
+
unit = "row"
|
|
288
|
+
enable = { connected = true }
|
|
289
|
+
action = { type = "action", name = "table.history", args = { pane = "all-orders" } }
|
|
290
|
+
|
|
238
291
|
[panes.pending]
|
|
239
292
|
title = "Pending"
|
|
240
293
|
type = "mkio-table"
|
|
@@ -374,6 +427,35 @@ labels = { parent_id = "Parent" }
|
|
|
374
427
|
link.listen = { order_id = "parent_id" }
|
|
375
428
|
link.broadcast = { child_id = "id" }
|
|
376
429
|
|
|
430
|
+
# Every recorded version of every order, newest first, arriving live as
|
|
431
|
+
# changes land — an ordinary query pane over the history table, with no
|
|
432
|
+
# history block of its own. `_mkio_op`, `_mkio_user` and `_mkio_ref` are
|
|
433
|
+
# mkio's own columns, shown because they are named. The pane is parked (no
|
|
434
|
+
# frame lists it): Record → Audit Tape opens it in a frame of its own.
|
|
435
|
+
[panes.audit-tape]
|
|
436
|
+
title = "Audit Tape"
|
|
437
|
+
type = "mkio-table"
|
|
438
|
+
service = "order_history"
|
|
439
|
+
protocol = "query"
|
|
440
|
+
columns = ["_mkio_ref", "id", "_mkio_version", "_mkio_op", "_mkio_user", "side", "symbol", "qty", "price", "status"]
|
|
441
|
+
labels = { _mkio_ref = "When", _mkio_version = "Ver", id = "Order", _mkio_user = "Changed by" }
|
|
442
|
+
sort = "-_mkio_ref"
|
|
443
|
+
|
|
444
|
+
[panes.audit-tape.display]
|
|
445
|
+
# The ref is a UTC timestamp; REF_TIME reads it in the browser's zone.
|
|
446
|
+
_mkio_ref = "${REF_TIME(value, fmt: '%H:%M:%S', tz: 'local')}"
|
|
447
|
+
_mkio_op = "${BADGE(value, CASE(value == 'insert', 'green', value == 'baseline', 'gray', '#0e639c'))}"
|
|
448
|
+
_mkio_user = "${value ?? MUTED('(system)')}"
|
|
449
|
+
|
|
450
|
+
# Filtering would narrow what you are looking at; this jumps to the record
|
|
451
|
+
# instead, leaving All Orders showing everything around it. A key its
|
|
452
|
+
# filters hide is reported back rather than revealed, so an order below
|
|
453
|
+
# the pane's `notional` floor says so instead of silently doing nothing.
|
|
454
|
+
[[panes.audit-tape.buttons]]
|
|
455
|
+
label = "Go to order"
|
|
456
|
+
enable = { minSelected = 1, maxSelected = 1 }
|
|
457
|
+
action = { type = "action", name = "table.select", args = { pane = "all-orders", keys = ["${row.id}"] } }
|
|
458
|
+
|
|
377
459
|
[panes.last-trade]
|
|
378
460
|
title = "Last Trade"
|
|
379
461
|
type = "mkio-table"
|
|
@@ -417,11 +499,16 @@ url = "ws://localhost:8080/ws"
|
|
|
417
499
|
# it too, so the subscription always succeeds.
|
|
418
500
|
control = "_mkui"
|
|
419
501
|
|
|
502
|
+
# What this app requires of the server it connects to; a mismatch applies
|
|
503
|
+
# `incompatible` below. Below 1.0 semver matching is exact-minor, so the
|
|
504
|
+
# `mkio` pin has to be bumped with every minor release of the library —
|
|
505
|
+
# tests/test_examples.py fails when it goes stale, rather than letting the
|
|
506
|
+
# statusbar quietly read "Wrong server".
|
|
420
507
|
[mkio.expect]
|
|
421
508
|
name = "order-book"
|
|
422
509
|
version = "1.0"
|
|
423
510
|
protocol = "1.0"
|
|
424
|
-
mkio = "0.
|
|
511
|
+
mkio = "0.3"
|
|
425
512
|
expr = "1"
|
|
426
513
|
|
|
427
514
|
[mkio.connected]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
name = "order-book"
|
|
2
|
+
version = "1.0.0"
|
|
3
|
+
port = 8080
|
|
4
|
+
host = "0.0.0.0"
|
|
5
|
+
db_path = "orders.db"
|
|
6
|
+
# Apply safe schema changes (like adding a column) to an existing orders.db.
|
|
7
|
+
auto_migrate = "safe"
|
|
8
|
+
|
|
9
|
+
[tables.orders]
|
|
10
|
+
# `ts` is stamped by SQLite in UTC (`YYYY-MM-DD HH:MM:SS`), a format the
|
|
11
|
+
# table recognises as a time, so its filter dropdown offers a date range.
|
|
12
|
+
# `parent_id` nests child orders under a parent (the Order Tree pane): a
|
|
13
|
+
# row whose parent_id is empty is a top-level order.
|
|
14
|
+
columns = { id = "INTEGER PRIMARY KEY AUTOINCREMENT", ts = "TEXT DEFAULT CURRENT_TIMESTAMP", side = "TEXT NOT NULL", symbol = "TEXT NOT NULL", qty = "INTEGER", price = "REAL DEFAULT 0", status = "TEXT DEFAULT 'pending'", parent_id = "INTEGER" }
|
|
15
|
+
# `versioned = true` is the whole switch: mkio numbers every version of
|
|
16
|
+
# every row (`_mkio_version`, 1 on insert and +1 per edit) and records it
|
|
17
|
+
# in a companion `orders__history` table, stamped with the ref, the user,
|
|
18
|
+
# and the service the change came through. `auto_migrate = "safe"` above
|
|
19
|
+
# turns it on for an orders.db that predates this line — the history table
|
|
20
|
+
# is created and every live row recorded as its baseline version.
|
|
21
|
+
versioned = true
|
|
22
|
+
|
|
23
|
+
# Write endpoint: place and manage orders
|
|
24
|
+
[services.orders]
|
|
25
|
+
protocol = "transaction"
|
|
26
|
+
|
|
27
|
+
[services.orders.ops]
|
|
28
|
+
new = [{ table = "orders", op_type = "insert", fields = ["side", "symbol", "qty", "price"] }]
|
|
29
|
+
child = [{ table = "orders", op_type = "insert", fields = ["side", "symbol", "qty", "price", "parent_id"] }]
|
|
30
|
+
fill = [{ table = "orders", op_type = "update", key = ["id"], fields = ["status"], defaults = { status = "filled" } }]
|
|
31
|
+
cancel = [{ table = "orders", op_type = "update", key = ["id"], fields = ["status"], defaults = { status = "cancelled" } }]
|
|
32
|
+
# undo/redo take only the key — the values come from the history table —
|
|
33
|
+
# and record no new version, so a step is always reversible. Undoing
|
|
34
|
+
# version 1 removes the row and keeps its versions, so redo can rebuild it.
|
|
35
|
+
undo = [{ table = "orders", op_type = "undo", key = ["id"] }]
|
|
36
|
+
redo = [{ table = "orders", op_type = "redo", key = ["id"] }]
|
|
37
|
+
|
|
38
|
+
# Live table of all orders (mkui subscribes here)
|
|
39
|
+
[services.all_orders]
|
|
40
|
+
protocol = "query"
|
|
41
|
+
primary_table = "orders"
|
|
42
|
+
filterable = ["status", "symbol"]
|
|
43
|
+
|
|
44
|
+
# Last filled trade per symbol
|
|
45
|
+
[services.last_trade]
|
|
46
|
+
protocol = "subpub"
|
|
47
|
+
primary_table = "orders"
|
|
48
|
+
topic = "symbol"
|
|
49
|
+
where = "status == 'filled'"
|
|
50
|
+
|
|
51
|
+
[services.last_trade.publish]
|
|
52
|
+
symbol = "symbol"
|
|
53
|
+
side = "side"
|
|
54
|
+
price = "price"
|
|
55
|
+
qty = "qty"
|
|
56
|
+
|
|
57
|
+
[services.last_trade.defaults]
|
|
58
|
+
symbol = "_mkio_topic"
|
|
59
|
+
|
|
60
|
+
# ─── Record history ───────────────────────────────────────────────────
|
|
61
|
+
# mkio never advertises a history table and writes no service for one, so
|
|
62
|
+
# these four are the deliberate choice to expose it. Each would take its
|
|
63
|
+
# own `access` rule on a server with authentication enabled — audit
|
|
64
|
+
# visibility is a permission like any other.
|
|
65
|
+
|
|
66
|
+
# One record's chain, oldest first: what the history pane reads.
|
|
67
|
+
[services.order_versions]
|
|
68
|
+
protocol = "reqrep"
|
|
69
|
+
sql = "SELECT * FROM orders__history WHERE id = :id ORDER BY _mkio_version"
|
|
70
|
+
|
|
71
|
+
# Where a record sits in its chain: the version its live row is on, and the
|
|
72
|
+
# highest recorded. `current` is null once the row has been undone away.
|
|
73
|
+
# This is what tells the client whether redo has anywhere to go.
|
|
74
|
+
[services.order_state]
|
|
75
|
+
protocol = "reqrep"
|
|
76
|
+
sql = "SELECT (SELECT _mkio_version FROM orders WHERE id = :id) AS current, MAX(_mkio_version) AS top FROM orders__history WHERE id = :id"
|
|
77
|
+
|
|
78
|
+
# The audit tape: every recorded version, newest first, live as they land.
|
|
79
|
+
# The seeder fills and cancels orders as it runs, so the chains build up
|
|
80
|
+
# on their own.
|
|
81
|
+
[services.order_history]
|
|
82
|
+
protocol = "query"
|
|
83
|
+
primary_table = "orders__history"
|
|
84
|
+
sql = "SELECT * FROM orders__history ORDER BY _mkio_ref DESC"
|
|
85
|
+
filterable = ["id", "symbol", "status", "_mkio_op", "_mkio_user"]
|
|
86
|
+
change_log_size = 10000
|
|
87
|
+
|
|
88
|
+
# The table as it stood at a moment: for each id, the newest version
|
|
89
|
+
# recorded at or before `as_of`. Refs sort lexicographically, so the cutoff
|
|
90
|
+
# is an ordinary range scan.
|
|
91
|
+
#
|
|
92
|
+
# Reconstructed from recorded versions, which is not quite the same as
|
|
93
|
+
# "what the table showed then": undo and redo move a row's cursor without
|
|
94
|
+
# recording anything, so a row undone at 3pm still reads, as of 2pm, as
|
|
95
|
+
# whatever its newest version by 2pm was.
|
|
96
|
+
[services.order_as_of]
|
|
97
|
+
protocol = "reqrep"
|
|
98
|
+
sql = """
|
|
99
|
+
SELECT h.* FROM orders__history h
|
|
100
|
+
JOIN (SELECT id, MAX(_mkio_version) AS v
|
|
101
|
+
FROM orders__history
|
|
102
|
+
WHERE _mkio_ref <= :as_of
|
|
103
|
+
GROUP BY id) latest
|
|
104
|
+
ON h.id = latest.id AND h._mkio_version = latest.v
|
|
105
|
+
ORDER BY h.id
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# Saved window layouts for mkui's Layout menu: one row per save, the
|
|
109
|
+
# newest being the current layout (`delete` is how the client prunes the
|
|
110
|
+
# history). With no login on this server every save goes to the default
|
|
111
|
+
# history (owner "").
|
|
112
|
+
[tables.mkui_layouts]
|
|
113
|
+
columns = { id = "INTEGER PRIMARY KEY AUTOINCREMENT", app = "TEXT NOT NULL DEFAULT ''", owner = "TEXT NOT NULL DEFAULT ''", saved = "TEXT DEFAULT CURRENT_TIMESTAMP", layout = "TEXT NOT NULL" }
|
|
114
|
+
|
|
115
|
+
[services.mkui_layouts]
|
|
116
|
+
protocol = "transaction"
|
|
117
|
+
|
|
118
|
+
[services.mkui_layouts.ops]
|
|
119
|
+
save = [{ table = "mkui_layouts", op_type = "insert", fields = ["app", "owner", "layout"] }]
|
|
120
|
+
delete = [{ table = "mkui_layouts", op_type = "delete", key = ["id"] }]
|
|
121
|
+
|
|
122
|
+
[services.mkui_layouts_list]
|
|
123
|
+
protocol = "reqrep"
|
|
124
|
+
sql = "SELECT id, saved FROM mkui_layouts WHERE app = :app AND owner = :owner ORDER BY id DESC"
|
|
125
|
+
|
|
126
|
+
[services.mkui_layouts_get]
|
|
127
|
+
protocol = "reqrep"
|
|
128
|
+
sql = "SELECT id, saved, layout FROM mkui_layouts WHERE id = :id"
|
|
129
|
+
|
|
130
|
+
# Serve frontend: app at /, mkui framework assets at /mkui/
|
|
131
|
+
[static]
|
|
132
|
+
"/" = "./static"
|
|
133
|
+
"/mkui" = "../../"
|
|
134
|
+
|
|
135
|
+
# Serve config files
|
|
136
|
+
[config]
|
|
137
|
+
"/config" = "./config"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// The state store is intentionally tiny: it's a Proxy over a plain object,
|
|
5
5
|
// supports dot-path get/set, and notifies subscribers per path.
|
|
6
6
|
|
|
7
|
-
export const VERSION = "0.3.
|
|
7
|
+
export const VERSION = "0.3.2";
|
|
8
8
|
export function version() { return VERSION; }
|
|
9
9
|
|
|
10
10
|
import { registerExprFunction, registerExprLibrary, registerExprType, expr } from "./lib/expressions.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mkui
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Config-driven, zero-dependency web GUI framework with dockable panes
|
|
5
5
|
Author: Mark Kim
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -211,7 +211,7 @@ mkio server and verifies its identity. Connection is two-phase:
|
|
|
211
211
|
"name": "order-book",
|
|
212
212
|
"version": "1.0",
|
|
213
213
|
"protocol": "1.0",
|
|
214
|
-
"mkio": "0.
|
|
214
|
+
"mkio": "0.3",
|
|
215
215
|
"expr": "1"
|
|
216
216
|
},
|
|
217
217
|
"connected": { "status.message": "Connected", "status.background": null },
|
|
@@ -227,6 +227,15 @@ to the server's `_mkio` service). When `expect` is absent, the `_mkio`
|
|
|
227
227
|
query still runs to confirm it is an mkio server and to populate
|
|
228
228
|
`mkio.server.*` state paths (name, version, protocol, mkio).
|
|
229
229
|
|
|
230
|
+
Semver matching is stricter below 1.0, where the minor number carries the
|
|
231
|
+
breaking changes: `"0.3"` accepts 0.3.x and rejects 0.4.0, so pinning
|
|
232
|
+
`mkio` at a pre-1.0 version means every minor release of the library
|
|
233
|
+
invalidates the pin. A stale pin is quiet — the app connects, the
|
|
234
|
+
`incompatible` map is applied, and the statusbar reads "Wrong server"
|
|
235
|
+
while nothing is wrong with the server. Leave `mkio` out unless the app
|
|
236
|
+
truly depends on the library's version, and pin `name` and `version`,
|
|
237
|
+
which are your own, instead.
|
|
238
|
+
|
|
230
239
|
The same reply says what the server can do, which lands in
|
|
231
240
|
`mkio.server.services` (service name → protocol),
|
|
232
241
|
`mkio.server.versioned` (the tables whose every change it records — see
|
|
@@ -802,7 +811,8 @@ python3 -m http.server 8000
|
|
|
802
811
|
# http://localhost:8000/examples/library-js/
|
|
803
812
|
```
|
|
804
813
|
|
|
805
|
-
The mkio-table example requires [mkio](https://pypi.org/project/mkio/)
|
|
814
|
+
The mkio-table example requires [mkio](https://pypi.org/project/mkio/) 0.3.0 or
|
|
815
|
+
later (its `orders` table is versioned):
|
|
806
816
|
|
|
807
817
|
```
|
|
808
818
|
cd mkui/static/examples/mkio-table
|
|
@@ -824,6 +834,8 @@ Its `orders` table is `versioned = true`, so mkio records every version of every
|
|
|
824
834
|
|
|
825
835
|
**All Orders** broadcasts the selected orders' `id` and `symbol`; the **Child Orders** frame listens for the id on its `parent_id` column and **Pending** for the symbol, so a selection filters both (the chips on each toolbar show and pause the links, and a saved layout keeps them). `python control.py` serves the example in place of `mkio serve` and rewires those links from Python a few seconds after a browser connects. The seeder places child orders under pending parents (and some under other children), so the **Order Tree** tab shows the same orders nested by `parent_id`: expand a parent with its caret, place a child order under the selected row with *+ Child Order*, and try the View menu's expand/collapse actions and alt/option-clicking a filter button for the scope row. The toolbar buttons show both styling forms: the **Pending** tab's Fill and Cancel wear a plain `style` map (green, and black on red in capitals) at all times, while the All Orders Cancel turns red only once a pending order is selected, through a rule conditioned on `enabled`.
|
|
826
836
|
|
|
837
|
+
The `orders` table is `versioned = true`, so mkio records every version of every row in `orders__history`, and `server.toml` writes the four services that expose it — one record's chain, where a record sits in it, the live tape, and the table as at a moment. **All Orders** names them in a `history` block and shows `_mkio_version` beside the data, which puts *Undo*, *Redo* and *As of…* on its toolbar: filling or cancelling an order moves it to a second version, Undo steps it back with a confirmation that says what will change, and *As of…* shows the book as it stood before the seeder's last few minutes, read-only until *Live* re-subscribes. Undo, Redo and *As of…* arrive with the block; the version timeline does not, so All Orders adds a **History** button of its own beside them — a `table.history` action over one selected record — and Record → History… is the same step from the menubar. Either opens that order's version timeline with its diff and blame views. Record → Audit Tape opens a parked query pane over the history table — every recorded version, newest first, live as they land — whose *Go to order* selects the record in All Orders rather than filtering it (a key All Orders' `status` and `notional` filters hide is reported back rather than revealed). An existing `orders.db` picks all this up on the next start: `auto_migrate = "safe"` creates the history table and records every live row as its baseline version.
|
|
838
|
+
|
|
827
839
|
## Project layout
|
|
828
840
|
|
|
829
841
|
```
|
|
@@ -861,9 +873,10 @@ mkui/ Python package (pip install mkui)
|
|
|
861
873
|
standalone-json/ Loaded from a static config
|
|
862
874
|
library-js/ Built imperatively from JS
|
|
863
875
|
mkio-table/ Live table backed by mkio query/subpub services
|
|
864
|
-
(config-only derived columns, styling rules,
|
|
865
|
-
enable conditions
|
|
866
|
-
|
|
876
|
+
(config-only derived columns, styling rules,
|
|
877
|
+
enable conditions, and record history over a
|
|
878
|
+
versioned orders table; static/app.js registers
|
|
879
|
+
an expression function and the order-detail pane)
|
|
867
880
|
history/ Versioned table: the audit tape, the version
|
|
868
881
|
timeline with diff and blame, record undo/redo,
|
|
869
882
|
and the table as at a moment (config only)
|
|
@@ -872,13 +885,29 @@ tests/
|
|
|
872
885
|
layout.test.js Layout tree unit tests (node:test)
|
|
873
886
|
state.test.js State + connection lifecycle tests (node:test)
|
|
874
887
|
table.test.js mkio-table pane tests (node:test)
|
|
888
|
+
pane-filters.test.js Configured filters, sort, visible: specs and API (node:test)
|
|
889
|
+
links.test.js LinkHub and table-to-table linking (node:test)
|
|
890
|
+
history.test.js Versioned tables: capabilities, spec, chain logic (node:test)
|
|
891
|
+
history-pane.test.js mkio-history pane: versions, diff, blame (node:test)
|
|
892
|
+
copy.test.js Clipboard grids: TSV quoting and HTML (node:test)
|
|
893
|
+
timeparse.test.js Time detection, parsing, bounds, presets (node:test)
|
|
894
|
+
icons.test.js Icon library: every name resolves to an SVG (node:test)
|
|
875
895
|
dialog.test.js Dialog expression + submission tests (node:test)
|
|
876
896
|
auth.test.js Authentication module + state lifecycle tests (node:test)
|
|
877
897
|
expressions.test.js Expression conformance fixtures + mkui wrapper tests (node:test)
|
|
878
898
|
vendor-sync.test.js lib/expr.js and expr_cases.json match the installed mkio (node:test)
|
|
879
899
|
rich.test.js Rich text type, mkui function library, clipboard HTML (node:test)
|
|
880
900
|
styles.test.js mkui.css layout invariants (node:test)
|
|
901
|
+
tabbar.test.js Tab bar: drag, reorder, overflow, rename (node:test)
|
|
902
|
+
tab-focus.test.js Active tab group and focus routing (node:test)
|
|
903
|
+
edit-routing.test.js edit.* actions and shortcuts reach the pane (node:test)
|
|
904
|
+
pointer-guards.test.js Button/modifier guards on every drag start (node:test)
|
|
905
|
+
windows-menu.test.js Windows and layouts menu expansion (node:test)
|
|
881
906
|
layouts.test.js Saved layouts: format, stores, workspace restore, menu (node:test)
|
|
882
907
|
version.test.js The four version strings agree (node:test)
|
|
883
908
|
test_cli.py CLI init/serve tests (unittest)
|
|
909
|
+
test_control.py ControlService: push actions to browsers (unittest)
|
|
910
|
+
test_examples.py Each example config against its server: the
|
|
911
|
+
[mkio.expect] pins, pane services, history
|
|
912
|
+
blocks, menu and frame pane ids (unittest)
|
|
884
913
|
```
|