tinymoon 0.2.0__tar.gz → 0.3.1__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.
Files changed (102) hide show
  1. {tinymoon-0.2.0/.rlsbl/bases → tinymoon-0.3.1}/.github/workflows/publish.yml +16 -6
  2. tinymoon-0.3.1/.rlsbl/changes/0.3.0.jsonl +2 -0
  3. tinymoon-0.3.1/.rlsbl/changes/0.3.0.md +7 -0
  4. tinymoon-0.3.1/.rlsbl/changes/0.3.1.jsonl +1 -0
  5. tinymoon-0.3.1/.rlsbl/changes/0.3.1.md +7 -0
  6. tinymoon-0.3.1/.rlsbl/releases/v0.3.0.toml +12 -0
  7. tinymoon-0.3.1/.rlsbl/releases/v0.3.1.toml +12 -0
  8. {tinymoon-0.2.0 → tinymoon-0.3.1}/.strictcli/schema.json +1 -1
  9. {tinymoon-0.2.0 → tinymoon-0.3.1}/CHANGELOG.md +16 -0
  10. {tinymoon-0.2.0 → tinymoon-0.3.1}/PKG-INFO +4 -2
  11. {tinymoon-0.2.0 → tinymoon-0.3.1}/README.md +3 -1
  12. tinymoon-0.3.1/VERSION +1 -0
  13. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/index.js +1 -1
  14. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/shell.js +17 -2
  15. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/toast.js +15 -0
  16. {tinymoon-0.2.0 → tinymoon-0.3.1}/gallery/gallery.js +7 -1
  17. {tinymoon-0.2.0 → tinymoon-0.3.1}/package.json +1 -1
  18. {tinymoon-0.2.0 → tinymoon-0.3.1}/pyproject.toml +1 -1
  19. {tinymoon-0.2.0 → tinymoon-0.3.1}/tinymoon/__init__.py +1 -1
  20. {tinymoon-0.2.0 → tinymoon-0.3.1}/uv.lock +1 -1
  21. tinymoon-0.2.0/VERSION +0 -1
  22. {tinymoon-0.2.0 → tinymoon-0.3.1}/.github/workflows/ci-go.yml +0 -0
  23. {tinymoon-0.2.0 → tinymoon-0.3.1}/.github/workflows/ci-pypi.yml +0 -0
  24. {tinymoon-0.2.0 → tinymoon-0.3.1}/.gitignore +0 -0
  25. {tinymoon-0.2.0 → tinymoon-0.3.1}/.npmignore +0 -0
  26. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.github/workflows/ci-go.yml +0 -0
  27. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.github/workflows/ci-pypi.yml +0 -0
  28. {tinymoon-0.2.0 → tinymoon-0.3.1/.rlsbl/bases}/.github/workflows/publish.yml +0 -0
  29. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.gitignore +0 -0
  30. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.npmignore +0 -0
  31. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
  32. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.rlsbl/lint/go.toml +0 -0
  33. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
  34. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
  35. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/CHANGELOG.md +0 -0
  36. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/bases/VERSION +0 -0
  37. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.0.1.jsonl +0 -0
  38. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.0.1.md +0 -0
  39. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.1.0.jsonl +0 -0
  40. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.1.0.md +0 -0
  41. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.2.0.jsonl +0 -0
  42. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/0.2.0.md +0 -0
  43. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/changes/unreleased.jsonl +0 -0
  44. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/config.json +0 -0
  45. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/lint/go.toml +0 -0
  46. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/lint/npm.toml +0 -0
  47. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/lint/python.toml +0 -0
  48. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/managed-files.json +0 -0
  49. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/releases/v0.0.1.toml +0 -0
  50. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/releases/v0.2.0.toml +0 -0
  51. {tinymoon-0.2.0 → tinymoon-0.3.1}/.rlsbl/version +0 -0
  52. {tinymoon-0.2.0 → tinymoon-0.3.1}/CLAUDE.md +0 -0
  53. {tinymoon-0.2.0 → tinymoon-0.3.1}/LICENSE +0 -0
  54. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/css/base.css +0 -0
  55. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/css/primitives.css +0 -0
  56. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/css/shell.css +0 -0
  57. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/css/tokens.css +0 -0
  58. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/fonts/ibm-plex-mono-latin-400.woff2 +0 -0
  59. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/fonts/ibm-plex-mono-latin-500.woff2 +0 -0
  60. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/fonts/ibm-plex-sans-latin.woff2 +0 -0
  61. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/fonts/space-grotesk-latin.woff2 +0 -0
  62. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/controls.js +0 -0
  63. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/ctxmenu.js +0 -0
  64. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/dom.js +0 -0
  65. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/format.js +0 -0
  66. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/icons.js +0 -0
  67. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/markdown.js +0 -0
  68. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/modal.js +0 -0
  69. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/net.js +0 -0
  70. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/popover.js +0 -0
  71. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/select.js +0 -0
  72. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/settings.js +0 -0
  73. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/tooltip.js +0 -0
  74. {tinymoon-0.2.0 → tinymoon-0.3.1}/assets/js/wiki.js +0 -0
  75. {tinymoon-0.2.0 → tinymoon-0.3.1}/gallery/gallery.css +0 -0
  76. {tinymoon-0.2.0 → tinymoon-0.3.1}/gallery/index.html +0 -0
  77. {tinymoon-0.2.0 → tinymoon-0.3.1}/go.mod +0 -0
  78. {tinymoon-0.2.0 → tinymoon-0.3.1}/index.js +0 -0
  79. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/clean/clean.css +0 -0
  80. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/clean/clean.html +0 -0
  81. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/clean/clean.js +0 -0
  82. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/clean/tinymoon-allowlist.txt +0 -0
  83. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/border-radius.css +0 -0
  84. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/border-radius.html +0 -0
  85. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/border-radius.js +0 -0
  86. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/external-url.css +0 -0
  87. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/external-url.html +0 -0
  88. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/external-url.js +0 -0
  89. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/native-control.html +0 -0
  90. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/native-control.js +0 -0
  91. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/raw-color.css +0 -0
  92. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/raw-color.html +0 -0
  93. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/raw-color.js +0 -0
  94. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/title-attr.html +0 -0
  95. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/fixtures/violations/title-attr.js +0 -0
  96. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/test_assets.py +0 -0
  97. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/test_budgets.py +0 -0
  98. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/test_checker.py +0 -0
  99. {tinymoon-0.2.0 → tinymoon-0.3.1}/tests/test_version.py +0 -0
  100. {tinymoon-0.2.0 → tinymoon-0.3.1}/tinymoon/checker.py +0 -0
  101. {tinymoon-0.2.0 → tinymoon-0.3.1}/tinymoon/cli.py +0 -0
  102. {tinymoon-0.2.0 → tinymoon-0.3.1}/tinymoon.go +0 -0
@@ -184,9 +184,19 @@ jobs:
184
184
  - name: Scan source for secrets
185
185
  run: |
186
186
  gitleaks dir .
187
- - uses: goreleaser/goreleaser-action@v7
188
- with:
189
- version: ~> v2
190
- args: release --clean
191
- env:
192
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
187
+ # tinymoon's Go module is a library (embed.FS + http.Handler) -- there
188
+ # is no main package, so goreleaser has nothing to build. For a Go
189
+ # library the pushed tag IS the publish: the module proxy serves it
190
+ # straight from git. This step primes proxy.golang.org and hard-fails
191
+ # if the tagged version does not resolve. Like the gate job, it assumes
192
+ # the run is at the tag ref (release trigger or dispatch at the tag).
193
+ - name: Verify module on the Go proxy
194
+ run: |
195
+ set -euo pipefail
196
+ mod="$(go list -m)"
197
+ ver="$GITHUB_REF_NAME"
198
+ dir="$(mktemp -d)"
199
+ cd "$dir"
200
+ go mod init proxyprime
201
+ GOPROXY=https://proxy.golang.org go mod download "${mod}@${ver}"
202
+ echo "Go proxy serves ${mod}@${ver}"
@@ -0,0 +1,2 @@
1
+ {"id":"18c1d65de3b9d2de86a890bcf7074862b328946e3385930b","commits":["c6a33774c6adba04fc032378956edf906b18c20d"],"user_facing":false}
2
+ {"id":"18c1e67b4ce0af025424ac641be147a984cd844ae78a2fd3","commits":["acaf60b814a213bd132770c2143b33919740f9b0","e4dc2294b4194c08ee5dc8da7d1b2a7351dc1900","fecd63801e77e57eae3cb6b2aea82d797fd7f351"],"user_facing":true,"description":"mountShell gains an onRoute(routeKey, sub) callback and a refreshCurrent() helper; toast errors can be mirrored via setToastErrorHook().","type":"feature"}
@@ -0,0 +1,7 @@
1
+ ## 0.3.0
2
+
3
+ Shell routing hooks (onRoute, refreshCurrent) and a toast error hook for log mirroring.
4
+
5
+ ### Features
6
+
7
+ - mountShell gains an onRoute(routeKey, sub) callback and a refreshCurrent() helper; toast errors can be mirrored via setToastErrorHook().
@@ -0,0 +1 @@
1
+ {"id":"18c1e781f297e6d98212e9a92abb41bfb661bb0c8d34a9bc","commits":["f739dad4a49c1a871ff4c15ee0e9ea82bdd4e040"],"user_facing":true,"description":"The toast error hook now receives the toast's opts object, so per-call metadata reaches logging hooks.","type":"fix"}
@@ -0,0 +1,7 @@
1
+ ## 0.3.1
2
+
3
+ Toast error hook receives the toast opts for per-call metadata.
4
+
5
+ ### Fixes
6
+
7
+ - The toast error hook now receives the toast's opts object, so per-call metadata reaches logging hooks.
@@ -0,0 +1,12 @@
1
+ # Version bump type: patch, minor, major, hotfix, or prerelease
2
+ bump = "minor"
3
+ # Short description of this release (required)
4
+ description = "Shell routing hooks (onRoute, refreshCurrent) and a toast error hook for log mirroring."
5
+ # Optional context explaining why these changes were made
6
+ context = ""
7
+ # Pre-release identifier: alpha, beta, rc, or stable
8
+ # preid = ""
9
+ # Set to true to generate a blog post for this release
10
+ # blog = false
11
+ include = ["npm", "pypi", "go"]
12
+ exclude = []
@@ -0,0 +1,12 @@
1
+ # Version bump type: patch, minor, major, hotfix, or prerelease
2
+ bump = "patch"
3
+ # Short description of this release (required)
4
+ description = "Toast error hook receives the toast opts for per-call metadata."
5
+ # Optional context explaining why these changes were made
6
+ context = ""
7
+ # Pre-release identifier: alpha, beta, rc, or stable
8
+ # preid = ""
9
+ # Set to true to generate a blog post for this release
10
+ # blog = false
11
+ include = ["npm", "pypi", "go"]
12
+ exclude = []
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "project_id": "tinymoon",
50
50
  "name": "tinymoon",
51
- "version": "0.2.0",
51
+ "version": "0.3.1",
52
52
  "help": "Tooling for the tinymoon web framework. Ships the conformance checker that enforces the framework's non-negotiables (no external URLs, no native browser controls, no title= attributes, no rounded corners, no off-token colors) as hard errors.",
53
53
  "commands": {
54
54
  "check": {
@@ -2,6 +2,22 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## 0.3.1
6
+
7
+ Toast error hook receives the toast opts for per-call metadata.
8
+
9
+ ### Fixes
10
+
11
+ - The toast error hook now receives the toast's opts object, so per-call metadata reaches logging hooks.
12
+
13
+ ## 0.3.0
14
+
15
+ Shell routing hooks (onRoute, refreshCurrent) and a toast error hook for log mirroring.
16
+
17
+ ### Features
18
+
19
+ - mountShell gains an onRoute(routeKey, sub) callback and a refreshCurrent() helper; toast errors can be mirrored via setToastErrorHook().
20
+
5
21
  ## 0.2.0
6
22
 
7
23
  The framework lands: design tokens, base styles, app shell, and UI primitives as zero-build vanilla CSS and ES modules; embeddable Go module; conformance checker CLI.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinymoon
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Zero-dependency vanilla JS/CSS UI framework (placeholder release)
5
5
  Project-URL: Homepage, https://github.com/smm-h/tinymoon
6
6
  Project-URL: Repository, https://github.com/smm-h/tinymoon
@@ -52,7 +52,9 @@ const shell = mountShell({
52
52
  });
53
53
  ```
54
54
 
55
- Consumer apps can extend the built-in icon set with `registerIcons({name: svgString})` colliding with an existing icon name is a hard error, never a silent overwrite.
55
+ `mountShell` also accepts an optional `onRoute(routeKey, sub)` callback fired after every route is handled (including the initial one), and the returned shell exposes `refreshCurrent()` to re-run the current view's `refresh()` in place.
56
+
57
+ Consumer apps can extend the built-in icon set with `registerIcons({name: svgString})` — colliding with an existing icon name is a hard error, never a silent overwrite. Error toasts can be mirrored into a log with `setToastErrorHook(fn)` — the hook receives each error toast's message and its opts object (`{}` when the caller passed none), and registering a second hook is a hard error.
56
58
 
57
59
  Views are plain objects following the contract `{root, built, build(), refresh(), setSub?}` — the [gallery](gallery/) is a complete working app and documents every token, primitive, and extension point (serve the repo root with a static server and open `/gallery/`).
58
60
 
@@ -31,7 +31,9 @@ const shell = mountShell({
31
31
  });
32
32
  ```
33
33
 
34
- Consumer apps can extend the built-in icon set with `registerIcons({name: svgString})` colliding with an existing icon name is a hard error, never a silent overwrite.
34
+ `mountShell` also accepts an optional `onRoute(routeKey, sub)` callback fired after every route is handled (including the initial one), and the returned shell exposes `refreshCurrent()` to re-run the current view's `refresh()` in place.
35
+
36
+ Consumer apps can extend the built-in icon set with `registerIcons({name: svgString})` — colliding with an existing icon name is a hard error, never a silent overwrite. Error toasts can be mirrored into a log with `setToastErrorHook(fn)` — the hook receives each error toast's message and its opts object (`{}` when the caller passed none), and registering a second hook is a hard error.
35
37
 
36
38
  Views are plain objects following the contract `{root, built, build(), refresh(), setSub?}` — the [gallery](gallery/) is a complete working app and documents every token, primitive, and extension point (serve the repo root with a static server and open `/gallery/`).
37
39
 
tinymoon-0.3.1/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
@@ -7,7 +7,7 @@ export { api, post } from "./net.js";
7
7
  export { fmtTime } from "./format.js";
8
8
  export { renderMiniMd } from "./markdown.js";
9
9
  export { ensureTooltip, hideTip } from "./tooltip.js";
10
- export { toast } from "./toast.js";
10
+ export { toast, setToastErrorHook } from "./toast.js";
11
11
  export { openModal } from "./modal.js";
12
12
  export { Select } from "./select.js";
13
13
  export { registerCtx, registerCtxFooter, showCtxMenu, hideCtxMenu } from "./ctxmenu.js";
@@ -1,7 +1,8 @@
1
1
  // tinymoon — app shell: builds the sidebar/nav/topbar/content frame, mounts
2
2
  // the framework overlay roots, and runs the hash router.
3
3
  //
4
- // mountShell(config) → {navigate(route), setBusy(msg), setTitle(title, sub)}
4
+ // mountShell(config) → {navigate(route), setBusy(msg), setTitle(title, sub),
5
+ // refreshCurrent()}
5
6
  //
6
7
  // config (required unless marked optional):
7
8
  // root — container element the shell frame is appended to
@@ -18,6 +19,11 @@
18
19
  // into #tm-topbar-actions
19
20
  // footer? — {height, node}: sets --footer-h and appends node to
20
21
  // the body. Without it --footer-h stays 0.
22
+ // onRoute? — fn(routeKey, sub): called after the router finishes
23
+ // handling a route (view built, setSub applied, refresh
24
+ // run, title set), including the initial route during
25
+ // mount. routeKey is the resolved route key (post
26
+ // legacy-redirect); sub is the deep-link tail or null.
21
27
  //
22
28
  // View contract: {root, built, build(), refresh(), setSub?(sub)}. The router
23
29
  // creates each view's section element (section.view) inside #tm-content and
@@ -43,7 +49,7 @@ function ensureRoot(id) {
43
49
  }
44
50
 
45
51
  export function mountShell(config) {
46
- const { root, brand, routes, defaultRoute, legacyRoutes, topbarActions, footer } = config || {};
52
+ const { root, brand, routes, defaultRoute, legacyRoutes, topbarActions, footer, onRoute } = config || {};
47
53
  need(root && root.appendChild, "root element is required");
48
54
  need(brand && brand.name && brand.logoHTML, "brand {name, logoHTML} is required");
49
55
  need(routes && Object.keys(routes).length, "routes is required");
@@ -183,6 +189,8 @@ export function mountShell(config) {
183
189
  content.scrollTop = 0;
184
190
  }
185
191
  view.refresh();
192
+ // Last, so routing state is fully consistent when the hook observes it.
193
+ if (onRoute) onRoute(name, sub || null);
186
194
  }
187
195
 
188
196
  window.addEventListener("hashchange", route);
@@ -192,5 +200,12 @@ export function mountShell(config) {
192
200
  navigate(r) { location.hash = "#/" + r; },
193
201
  setBusy,
194
202
  setTitle,
203
+ // Re-run the current view's refresh() in place: no rebuild, no entry
204
+ // animation. No-op before the first route or if the view isn't built.
205
+ refreshCurrent() {
206
+ if (!currentRoute) return;
207
+ const view = routes[currentRoute].view();
208
+ if (view.built) view.refresh();
209
+ },
195
210
  };
196
211
  }
@@ -14,6 +14,18 @@ function toastRoot() {
14
14
  return root;
15
15
  }
16
16
 
17
+ let errorHook = null;
18
+
19
+ // setToastErrorHook(fn): register a module-level hook invoked as
20
+ // fn(message, opts) whenever an error toast is shown — e.g. to mirror errors
21
+ // into a log. opts is the same opts object toast() received ({} when the
22
+ // caller passed none, never undefined), so per-call metadata reaches the hook.
23
+ // Registering a second hook is a hard error, never a silent overwrite.
24
+ export function setToastErrorHook(fn) {
25
+ if (errorHook) throw new Error("setToastErrorHook: a hook is already registered");
26
+ errorHook = fn;
27
+ }
28
+
17
29
  // toast(msg, kind, opts): kind "err" turns the toast red and keeps it on
18
30
  // screen longer. opts.duration (ms) overrides the lifetime. Every toast
19
31
  // carries a copy icon for its message text. The mount point is created
@@ -31,4 +43,7 @@ export function toast(msg, kind, opts) {
31
43
  // must stay shorter than this removal delay.
32
44
  setTimeout(() => t.remove(), 200);
33
45
  }, life);
46
+ // After the toast is on screen (and its removal is scheduled, so a
47
+ // throwing hook can't strand it). Hook exceptions propagate.
48
+ if (kind === "err" && errorHook) errorHook(msg, opts || {});
34
49
  }
@@ -8,7 +8,7 @@
8
8
 
9
9
  import {
10
10
  $$, el, icon,
11
- toast, openModal, Select, openPopover,
11
+ toast, setToastErrorHook, openModal, Select, openPopover,
12
12
  registerCtx, registerCtxFooter,
13
13
  toggleWidget, segmented, copyButton, kebabButton,
14
14
  createSettings, cssVar,
@@ -448,6 +448,10 @@ registerCtxFooter(() => [
448
448
  { label: "Toggle theme", icon: "sun", action: () => settings.set("theme", settings.get("theme") === "dark" ? "light" : "dark") },
449
449
  ]);
450
450
 
451
+ // toast error hook (extension point): mirror error toasts into the console
452
+ // log — visible when pressing "Error toast" on the Widgets page.
453
+ setToastErrorHook((msg, opts) => console.error("[gallery] error toast: " + msg, opts));
454
+
451
455
  // verbose demo setting: narrate setting changes
452
456
  window.addEventListener("tm:setting", (e) => {
453
457
  if (settings.get("verbose") || e.detail.key === "verbose") {
@@ -487,11 +491,13 @@ Consumer apps extend tinymoon through explicit seams, never by patching internal
487
491
 
488
492
  - **Routes** — \`mountShell({routes})\`: each key maps to a title, an icon, and a view factory. \`hidden: true\` keeps a route out of the sidebar but routable.
489
493
  - **Legacy routes** — \`legacyRoutes: {old: "new"}\` redirects stale bookmarks, tails included.
494
+ - **Route hook** — \`onRoute: (routeKey, sub) => …\` fires after the router finishes handling a route (view built, \`setSub\` applied, \`refresh()\` run, title set), including the initial route; \`routeKey\` is the resolved key after legacy redirects and \`sub\` is the deep-link tail or \`null\`. The shell object returned by \`mountShell\` also exposes \`refreshCurrent()\`, which re-runs the current view's \`refresh()\` in place — no rebuild, no entry animation.
490
495
  - **Topbar actions** — \`topbarActions\`: prebuilt nodes or \`{icon, tip, onClick}\` specs rendered right of the busy indicator.
491
496
  - **Footer slot** — \`footer: {height, node}\` pins an app-owned bar to the bottom; the shell sets \`--footer-h\` so the frame, toasts, tooltips, selects, and popovers all clear it.
492
497
  - **Context menus** — \`registerCtx(key, provider)\` serves right-clicks on \`[data-ctx]\` regions; \`registerCtxFooter(fn)\` appends app-global items to every menu.
493
498
  - **Settings schema** — \`createSettings({storageKey, defaults})\`: the defaults object is the schema; unknown keys are hard errors.
494
499
  - **Icons** — \`registerIcons({name: svgString})\` merges consumer icons into the built-in set; a name collision with an existing icon is a hard error, never a silent overwrite.
500
+ - **Toast error hook** — \`setToastErrorHook(fn)\` mirrors every error toast's message into your hook (e.g. a log), invoked as \`fn(message, opts)\` where opts is the toast's own opts object (\`{}\` when the caller passed none) so per-call metadata reaches the hook; registering a second hook is a hard error, never a silent overwrite. This gallery mirrors error toasts (message and opts) to the browser console — press the **Error toast** button on the Widgets page with the console open.
495
501
 
496
502
  ### Custom components {#custom-components}
497
503
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinymoon",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Zero-dependency vanilla JS/CSS UI framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tinymoon"
7
- version = "0.2.0"
7
+ version = "0.3.1"
8
8
  description = "Zero-dependency vanilla JS/CSS UI framework (placeholder release)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -7,7 +7,7 @@ fonts) inside the wheel and exposes their location via assets_path().
7
7
  from importlib import resources
8
8
  from pathlib import Path
9
9
 
10
- __version__ = "0.2.0"
10
+ __version__ = "0.3.1"
11
11
 
12
12
 
13
13
  def assets_path() -> Path:
@@ -74,7 +74,7 @@ wheels = [
74
74
 
75
75
  [[package]]
76
76
  name = "tinymoon"
77
- version = "0.2.0"
77
+ version = "0.3.1"
78
78
  source = { editable = "." }
79
79
  dependencies = [
80
80
  { name = "strictcli" },
tinymoon-0.2.0/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.2.0
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