mkui 0.3.2__tar.gz → 0.3.3__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.2 → mkui-0.3.3}/PKG-INFO +4 -2
- {mkui-0.3.2 → mkui-0.3.3}/README.md +3 -1
- {mkui-0.3.2 → mkui-0.3.3}/mkui/__init__.py +1 -1
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/history/config/client.toml +16 -4
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/core.js +1 -1
- {mkui-0.3.2 → mkui-0.3.3}/mkui.egg-info/PKG-INFO +4 -2
- {mkui-0.3.2 → mkui-0.3.3}/pyproject.toml +1 -1
- {mkui-0.3.2 → mkui-0.3.3}/tests/test_examples.py +177 -0
- {mkui-0.3.2 → mkui-0.3.3}/LICENSE +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/__main__.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/control.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/history/seed.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/history/server.toml +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/history/static/index.html +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/library-js/index.html +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/config/client.toml +22 -22
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/control.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/seed.csv +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/seed.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/server.toml +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/static/app.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/mkio-table/static/index.html +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/standalone-json/client.json +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/examples/standalone-json/index.html +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/auth.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/components/app.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/components/frame.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/components/menubar.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/components/statusbar.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/components/workspace.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/index.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/layout/drag.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/layout/tree.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/layouts.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/copy.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/expr.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/expressions.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/history.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/icons.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/layouts.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/links.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/rich.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/lib/timeparse.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/mkio-bridge.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/widgets/button.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/widgets/mkio-history.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/widgets/mkio-table.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/widgets/mkui-dialog.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/src/widgets/text.js +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui/static/styles/mkui.css +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui.egg-info/SOURCES.txt +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui.egg-info/dependency_links.txt +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui.egg-info/entry_points.txt +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/mkui.egg-info/top_level.txt +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/setup.cfg +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/tests/test_cli.py +0 -0
- {mkui-0.3.2 → mkui-0.3.3}/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.3
|
|
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
|
|
@@ -909,5 +909,7 @@ tests/
|
|
|
909
909
|
test_control.py ControlService: push actions to browsers (unittest)
|
|
910
910
|
test_examples.py Each example config against its server: the
|
|
911
911
|
[mkio.expect] pins, pane services, history
|
|
912
|
-
blocks, menu and frame pane ids
|
|
912
|
+
blocks, menu and frame pane ids, and every
|
|
913
|
+
menu and button action against the ones the
|
|
914
|
+
app registers (unittest)
|
|
913
915
|
```
|
|
@@ -889,5 +889,7 @@ tests/
|
|
|
889
889
|
test_control.py ControlService: push actions to browsers (unittest)
|
|
890
890
|
test_examples.py Each example config against its server: the
|
|
891
891
|
[mkio.expect] pins, pane services, history
|
|
892
|
-
blocks, menu and frame pane ids
|
|
892
|
+
blocks, menu and frame pane ids, and every
|
|
893
|
+
menu and button action against the ones the
|
|
894
|
+
app registers (unittest)
|
|
893
895
|
```
|
|
@@ -25,6 +25,14 @@ items = [
|
|
|
25
25
|
{ label = "Redo record", action = "edit.redo" },
|
|
26
26
|
]
|
|
27
27
|
|
|
28
|
+
[[menubar]]
|
|
29
|
+
label = "Edit"
|
|
30
|
+
items = [
|
|
31
|
+
{ label = "Copy", action = "edit.copy", shortcut = "mod+C" },
|
|
32
|
+
{ label = "Select All", action = "edit.selectAll", shortcut = "mod+A" },
|
|
33
|
+
{ label = "Find…", action = "edit.find", shortcut = "mod+F" },
|
|
34
|
+
]
|
|
35
|
+
|
|
28
36
|
[[menubar]]
|
|
29
37
|
label = "View"
|
|
30
38
|
items = [
|
|
@@ -37,11 +45,15 @@ items = [
|
|
|
37
45
|
]
|
|
38
46
|
|
|
39
47
|
[[menubar]]
|
|
40
|
-
label = "
|
|
48
|
+
label = "Window"
|
|
41
49
|
items = [
|
|
42
|
-
{ label = "
|
|
43
|
-
{ label = "
|
|
44
|
-
{ label = "
|
|
50
|
+
{ label = "Tile Horizontally", action = "window.tileH" },
|
|
51
|
+
{ label = "Tile Vertically", action = "window.tileV" },
|
|
52
|
+
{ label = "Grid", action = "window.grid" },
|
|
53
|
+
{ label = "Cascade", action = "window.cascade" },
|
|
54
|
+
{ sep = true },
|
|
55
|
+
# One item per open pane, rebuilt each time the menu opens.
|
|
56
|
+
{ windows = true },
|
|
45
57
|
]
|
|
46
58
|
|
|
47
59
|
# ─── The live table ───────────────────────────────────────────────────
|
|
@@ -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.3";
|
|
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.3
|
|
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
|
|
@@ -909,5 +909,7 @@ tests/
|
|
|
909
909
|
test_control.py ControlService: push actions to browsers (unittest)
|
|
910
910
|
test_examples.py Each example config against its server: the
|
|
911
911
|
[mkio.expect] pins, pane services, history
|
|
912
|
-
blocks, menu and frame pane ids
|
|
912
|
+
blocks, menu and frame pane ids, and every
|
|
913
|
+
menu and button action against the ones the
|
|
914
|
+
app registers (unittest)
|
|
913
915
|
```
|
|
@@ -23,6 +23,7 @@ inert — so they are caught by reading the two files against each other.
|
|
|
23
23
|
|
|
24
24
|
import asyncio
|
|
25
25
|
import json
|
|
26
|
+
import re
|
|
26
27
|
import time
|
|
27
28
|
import unittest
|
|
28
29
|
from pathlib import Path
|
|
@@ -237,6 +238,52 @@ class TestClientServerWiring(unittest.TestCase):
|
|
|
237
238
|
)
|
|
238
239
|
|
|
239
240
|
|
|
241
|
+
SRC = Path(__file__).resolve().parent.parent / "mkui" / "static" / "src"
|
|
242
|
+
|
|
243
|
+
# `app.quit` is the one action `fireAction` handles itself (core.js); every
|
|
244
|
+
# other built-in is registered by name, so the source is the list.
|
|
245
|
+
QUIT = "app.quit"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def registered_actions(root):
|
|
249
|
+
"""Every name passed to `registerAction("<name>", …)` under a JS tree."""
|
|
250
|
+
names = set()
|
|
251
|
+
for path in sorted(root.rglob("*.js")):
|
|
252
|
+
names |= set(re.findall(r'registerAction\(\s*"([^"]+)"', path.read_text()))
|
|
253
|
+
return names
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
BUILTIN_ACTIONS = registered_actions(SRC) | {QUIT}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def example_actions(name):
|
|
260
|
+
"""Built-ins plus whatever this example's own JS registers."""
|
|
261
|
+
return BUILTIN_ACTIONS | registered_actions(EXAMPLES / name / "static")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def menus(client):
|
|
265
|
+
return client.get("menubar", []) or []
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def button_actions(client):
|
|
269
|
+
"""(pane id, button, action name) for every `type = "action"` button."""
|
|
270
|
+
out = []
|
|
271
|
+
for pid, spec in panes(client).items():
|
|
272
|
+
for btn in (spec.get("buttons") if isinstance(spec, dict) else None) or []:
|
|
273
|
+
act = btn.get("action")
|
|
274
|
+
if isinstance(act, dict) and act.get("type") == "action":
|
|
275
|
+
out.append((pid, btn, act.get("name")))
|
|
276
|
+
return out
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def opened_panes(client):
|
|
280
|
+
"""The panes the config's frames open at startup."""
|
|
281
|
+
out = set()
|
|
282
|
+
for frame in client.get("frames", []) or []:
|
|
283
|
+
out |= set(layout_panes(frame.get("layout")))
|
|
284
|
+
return out
|
|
285
|
+
|
|
286
|
+
|
|
240
287
|
class TestPaneWiring(unittest.TestCase):
|
|
241
288
|
"""Every service, pane id, and table name a client config names.
|
|
242
289
|
|
|
@@ -456,5 +503,135 @@ class TestHistoryWiring(unittest.TestCase):
|
|
|
456
503
|
self.assertIn(hist.get("versions"), declared)
|
|
457
504
|
|
|
458
505
|
|
|
506
|
+
|
|
507
|
+
class TestMenubarWiring(unittest.TestCase):
|
|
508
|
+
"""What a menu item promises about the app behind it.
|
|
509
|
+
|
|
510
|
+
A menubar fails quietly in exactly the way the rest of this file is about:
|
|
511
|
+
`fireAction` warns to a console nobody is reading and the item does
|
|
512
|
+
nothing, so a renamed action, a `layout.*` item in an app with no
|
|
513
|
+
`[layouts]` block, or a pane that no menu can reach all look like a menu
|
|
514
|
+
that is simply broken. Reading the config against the source that
|
|
515
|
+
registers the actions catches them here instead.
|
|
516
|
+
"""
|
|
517
|
+
|
|
518
|
+
def test_at_least_one_example_has_a_menubar(self):
|
|
519
|
+
"""Guards the rest of this class against passing vacuously."""
|
|
520
|
+
self.assertTrue(
|
|
521
|
+
[e for e in examples() if menus(e[2])],
|
|
522
|
+
"no example declares a menubar any more",
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
def test_the_builtin_action_list_was_actually_found(self):
|
|
526
|
+
"""The negative control for the scrape: it is a regex over the source."""
|
|
527
|
+
for name in ("pane.show", "window.grid", "edit.copy", "table.history"):
|
|
528
|
+
self.assertIn(name, BUILTIN_ACTIONS, f"{name!r} is no longer registered")
|
|
529
|
+
self.assertNotIn("table.nope", BUILTIN_ACTIONS)
|
|
530
|
+
|
|
531
|
+
def test_every_menu_action_is_one_the_app_registers(self):
|
|
532
|
+
seen = 0
|
|
533
|
+
for name, _server, client in examples():
|
|
534
|
+
known = example_actions(name)
|
|
535
|
+
for item in menu_items(client):
|
|
536
|
+
action = item.get("action")
|
|
537
|
+
if action is None:
|
|
538
|
+
continue
|
|
539
|
+
seen += 1
|
|
540
|
+
with self.subTest(example=name, item=item.get("label")):
|
|
541
|
+
self.assertIn(
|
|
542
|
+
action, known,
|
|
543
|
+
f"{name}: menu item {item.get('label')!r} fires "
|
|
544
|
+
f"{action!r}, which nothing registers",
|
|
545
|
+
)
|
|
546
|
+
self.assertTrue(seen, "no menu item fires an action any more")
|
|
547
|
+
|
|
548
|
+
def test_every_button_action_is_one_the_app_registers(self):
|
|
549
|
+
for name, _server, client in examples():
|
|
550
|
+
known = example_actions(name)
|
|
551
|
+
for pid, btn, action in button_actions(client):
|
|
552
|
+
with self.subTest(example=name, pane=pid, button=btn.get("label")):
|
|
553
|
+
self.assertIn(
|
|
554
|
+
action, known,
|
|
555
|
+
f"{name}: {pid}'s {btn.get('label')!r} button fires "
|
|
556
|
+
f"{action!r}, which nothing registers",
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
def test_layout_items_require_a_layouts_block(self):
|
|
560
|
+
"""`layout.*` and `layouts = true` are inert without `[layouts]`."""
|
|
561
|
+
for name, _server, client in examples():
|
|
562
|
+
has_layouts = bool(client.get("layouts"))
|
|
563
|
+
for item in menu_items(client):
|
|
564
|
+
wants = (item.get("action") or "").startswith("layout.") or item.get("layouts")
|
|
565
|
+
if not wants:
|
|
566
|
+
continue
|
|
567
|
+
with self.subTest(example=name, item=item.get("label")):
|
|
568
|
+
self.assertTrue(
|
|
569
|
+
has_layouts,
|
|
570
|
+
f"{name}: menu item {item.get('label')!r} saves or "
|
|
571
|
+
f"restores layouts, but the config has no [layouts] block",
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
def test_an_expanding_item_carries_nothing_of_its_own(self):
|
|
575
|
+
"""`windows` / `layouts` items are replaced wholesale when the menu
|
|
576
|
+
opens, so an `action` or `items` on one is silently dropped."""
|
|
577
|
+
seen = 0
|
|
578
|
+
for name, _server, client in examples():
|
|
579
|
+
for item in menu_items(client):
|
|
580
|
+
kinds = [k for k in ("windows", "layouts") if item.get(k)]
|
|
581
|
+
if not kinds:
|
|
582
|
+
continue
|
|
583
|
+
seen += 1
|
|
584
|
+
with self.subTest(example=name, item=item.get("label"), kind=kinds[0]):
|
|
585
|
+
self.assertEqual(len(kinds), 1, "an item expands one way")
|
|
586
|
+
self.assertIsNone(
|
|
587
|
+
item.get("action"),
|
|
588
|
+
f"{name}: {kinds[0]} item also names an action",
|
|
589
|
+
)
|
|
590
|
+
self.assertIsNone(
|
|
591
|
+
item.get("items"),
|
|
592
|
+
f"{name}: {kinds[0]} item also names its own submenu",
|
|
593
|
+
)
|
|
594
|
+
self.assertTrue(seen, "no menu item expands any more")
|
|
595
|
+
|
|
596
|
+
def test_every_menubar_lists_the_open_panes(self):
|
|
597
|
+
"""A frame raised behind another, or a pane sharing a frame's tab bar,
|
|
598
|
+
is reachable only through the `windows = true` expansion."""
|
|
599
|
+
for name, _server, client in examples():
|
|
600
|
+
with self.subTest(example=name):
|
|
601
|
+
self.assertTrue(
|
|
602
|
+
any(it.get("windows") for it in menu_items(client)),
|
|
603
|
+
f"{name}: no menu expands into the open panes "
|
|
604
|
+
f"({{ windows = true }}), so a buried pane cannot be raised",
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
def test_a_parked_pane_has_a_menu_item_of_its_own(self):
|
|
608
|
+
"""`windows = true` lists *open* panes, so one no frame opens needs an
|
|
609
|
+
explicit `pane.show` or nothing can ever open it."""
|
|
610
|
+
for name, _server, client in examples():
|
|
611
|
+
shown = {
|
|
612
|
+
it.get("args") for it in menu_items(client)
|
|
613
|
+
if it.get("action") == "pane.show" and isinstance(it.get("args"), str)
|
|
614
|
+
}
|
|
615
|
+
# A history pane is opened by `table.history`, not by id.
|
|
616
|
+
for pid in sorted(set(panes(client)) - opened_panes(client)):
|
|
617
|
+
with self.subTest(example=name, pane=pid):
|
|
618
|
+
self.assertIn(
|
|
619
|
+
pid, shown,
|
|
620
|
+
f"{name}: pane {pid!r} is in no frame and in no menu, "
|
|
621
|
+
f"so nothing can open it",
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
def test_menu_labels_are_unique(self):
|
|
625
|
+
"""Two dropdowns with one name is a config mistake, not a feature."""
|
|
626
|
+
for name, _server, client in examples():
|
|
627
|
+
labels = [m.get("label") for m in menus(client)]
|
|
628
|
+
with self.subTest(example=name):
|
|
629
|
+
self.assertEqual(
|
|
630
|
+
len(labels), len(set(labels)),
|
|
631
|
+
f"{name}: duplicate menubar labels {labels}",
|
|
632
|
+
)
|
|
633
|
+
self.assertTrue(all(labels), f"{name}: a menu has no label")
|
|
634
|
+
|
|
635
|
+
|
|
459
636
|
if __name__ == "__main__":
|
|
460
637
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -9,15 +9,6 @@ background = "#b8b8b8"
|
|
|
9
9
|
|
|
10
10
|
# ─── Menubar ──────────────────────────────────────────────────────────
|
|
11
11
|
|
|
12
|
-
[[menubar]]
|
|
13
|
-
label = "Edit"
|
|
14
|
-
items = [
|
|
15
|
-
{ label = "Copy", action = "edit.copy", shortcut = "mod+C" },
|
|
16
|
-
{ sep = true },
|
|
17
|
-
{ label = "Select All", action = "edit.selectAll", shortcut = "mod+A" },
|
|
18
|
-
{ label = "Find…", action = "edit.find", shortcut = "mod+F" },
|
|
19
|
-
]
|
|
20
|
-
|
|
21
12
|
[[menubar]]
|
|
22
13
|
label = "Orders"
|
|
23
14
|
items = [
|
|
@@ -28,21 +19,12 @@ items = [
|
|
|
28
19
|
]
|
|
29
20
|
|
|
30
21
|
[[menubar]]
|
|
31
|
-
label = "
|
|
22
|
+
label = "Edit"
|
|
32
23
|
items = [
|
|
33
|
-
|
|
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" },
|
|
24
|
+
{ label = "Copy", action = "edit.copy", shortcut = "mod+C" },
|
|
44
25
|
{ sep = true },
|
|
45
|
-
{ label = "
|
|
26
|
+
{ label = "Select All", action = "edit.selectAll", shortcut = "mod+A" },
|
|
27
|
+
{ label = "Find…", action = "edit.find", shortcut = "mod+F" },
|
|
46
28
|
]
|
|
47
29
|
|
|
48
30
|
[[menubar]]
|
|
@@ -73,6 +55,24 @@ items = [
|
|
|
73
55
|
{ label = "Order Tree: collapse all", action = "table.expand", args = { pane = "order-tree" } },
|
|
74
56
|
]
|
|
75
57
|
|
|
58
|
+
[[menubar]]
|
|
59
|
+
label = "Record"
|
|
60
|
+
items = [
|
|
61
|
+
# The version timeline for whatever All Orders has selected: the chain
|
|
62
|
+
# as a table, with a diff and a blame view under it. One history pane
|
|
63
|
+
# per table, re-pointed as the selection moves rather than re-opened.
|
|
64
|
+
{ label = "History…", action = "table.history", args = { pane = "all-orders" } },
|
|
65
|
+
# `keys` names the record instead, so a menu item can be a deep link.
|
|
66
|
+
{ label = "History of order 1", action = "table.history", args = { pane = "all-orders", keys = ["1"] } },
|
|
67
|
+
{ sep = true },
|
|
68
|
+
# The steps the All Orders toolbar's Undo and Redo take, on the focused
|
|
69
|
+
# pane. Deliberately without a shortcut: this writes to shared state.
|
|
70
|
+
{ label = "Undo record", action = "edit.undo" },
|
|
71
|
+
{ label = "Redo record", action = "edit.redo" },
|
|
72
|
+
{ sep = true },
|
|
73
|
+
{ label = "Audit Tape", action = "pane.show", args = "audit-tape" },
|
|
74
|
+
]
|
|
75
|
+
|
|
76
76
|
[[menubar]]
|
|
77
77
|
label = "Trades"
|
|
78
78
|
items = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|