Skip to content

Commit edc9017

Browse files
authored
Merge pull request GaijinEntertainment#2649 from GaijinEntertainment/bbatkin/docs-external-modules-dasimgui
docs: External modules section + dasImgui crosslink
2 parents a061bb6 + 52821ba commit edc9017

9 files changed

Lines changed: 264 additions & 1 deletion

.github/workflows/pages.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,14 @@ jobs:
7777
run: |
7878
set -eux
7979
cd web
80+
# step0 only clones the emsdk wrapper — the toolchain itself
81+
# (emsdk/upstream/emscripten/) lands via the install+activate pair
82+
# that web/step1_emsdk_activate_linux.sh does locally. Without these,
83+
# cmake's -DCMAKE_TOOLCHAIN_FILE points at a path that doesn't
84+
# exist yet and the WASM build silently fails under continue-on-error.
8085
bash step0_emsdk_install.sh
81-
# step1 sources emsdk_env.sh — re-export EMSDK for the configure step
86+
./emsdk/emsdk install latest
87+
./emsdk/emsdk activate latest
8288
bash -c "source emsdk/emsdk_env.sh && \
8389
mkdir -p build && cd build && \
8490
cmake -DCMAKE_BUILD_TYPE=Release -G Ninja \
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
dasImgui
2+
========
3+
4+
dasImgui is the daslang binding for `Dear ImGui <https://github.com/ocornut/imgui>`_.
5+
It ships with the boost macro layer that compresses immediate-mode boilerplate
6+
into block-style one-liners, full live-reload integration, and a snapshot-based
7+
test harness that drives the widget tree headlessly under ``dastest``.
8+
9+
Where to go
10+
-----------
11+
12+
* **Documentation**: https://borisbat.github.io/dasImgui/
13+
* **Repository**: https://github.com/borisbat/dasImgui
14+
15+
What's there
16+
------------
17+
18+
* Bindings covering the Dear ImGui surface, generated from the upstream
19+
``imgui.h`` via the in-repo binder.
20+
* The ``imgui_boost`` macro layer — ``window`` / ``tab_bar`` / ``tree_node``
21+
/ ``table`` and friends as block macros, so the typical ``Begin*`` / ``End*``
22+
pair becomes a single scoped block.
23+
* ``imgui_test``: a Playwright-style widget walker that asserts against the
24+
rendered tree without needing a GL context — usable from dastest, runnable
25+
in CI.
26+
* Examples and tutorials under ``examples/features/``, every one runnable both
27+
as a standalone binary and live-reloaded under ``daslang-live``.
28+
29+
Versioning
30+
----------
31+
32+
The documentation tracks the **v2.0** surface. The legacy ``daslib/imgui_boost``
33+
(v1) is not documented at the new site; v1 users pin to the older daspkg
34+
release.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
External modules
2+
================
3+
4+
Daslang modules maintained outside the main repository. Each one ships its
5+
own documentation; the entries below are short pointers describing what each
6+
module does and where its docs live.
7+
8+
.. toctree::
9+
:maxdepth: 1
10+
11+
dasimgui

doc/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Contents:
99

1010
reference/index.rst
1111
stdlib/index.rst
12+
external_modules/index.rst
1213

1314

1415
Indices and tables
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
slug: codemirror-5-multi-file-editor-swapdoc-autosave-discipline
3+
title: How do I implement a multi-file editor in CodeMirror 5 with per-tab undo history and localStorage autosave?
4+
created: 2026-05-13
5+
last_verified: 2026-05-13
6+
links: []
7+
---
8+
9+
**Core pattern: one `CodeMirror.Doc` per file, `editor.swapDoc(doc)` to switch the visible buffer. The editor instance is shared; each Doc carries its own text, undo stack, and scroll position.**
10+
11+
```js
12+
const editor = CodeMirror(el, opts); // one editor
13+
14+
const state = {
15+
files: { // one Doc per file
16+
'main.das': editor.getDoc(),
17+
'utils.das': new CodeMirror.Doc('', 'daslang'),
18+
},
19+
active: 'main.das',
20+
};
21+
22+
function switchFile(name) {
23+
state.active = name;
24+
editor.swapDoc(state.files[name]); // history preserved
25+
autosave(); // ← see below
26+
}
27+
```
28+
29+
## Autosave discipline — the trap
30+
31+
The instinct is to autosave on content change only:
32+
```js
33+
doc.on('change', autosave); // covers content edits
34+
```
35+
36+
But that misses **every other state mutation** — tab switch, add, delete, rename. If the user picks tab B, types nothing, and reloads, persistence restores tab A. Fix: call `autosave()` from *every* state-modifying entry point, not just `change`:
37+
38+
- `switchFile``autosave()`
39+
- `addFile``autosave()`
40+
- `deleteFile``autosave()`
41+
- `renameFile``autosave()`
42+
- Initial `change` listener stays (per-Doc, attached when Doc is created)
43+
44+
Debounce the writer (~250ms) so rapid edits coalesce. Redundant calls during rapid state changes are free — `clearTimeout(autosaveTimer)` then `setTimeout(..., 250)`.
45+
46+
## Serialization
47+
48+
Each Doc stringifies via `.getValue()`. Whole state:
49+
```js
50+
function getStateJson() {
51+
return {
52+
files: Object.fromEntries(
53+
Object.entries(state.files).map(([k, d]) => [k, d.getValue()])
54+
),
55+
active: state.active,
56+
};
57+
}
58+
```
59+
Persist `JSON.stringify(getStateJson())` into `localStorage`. Restore by parsing then reconstructing fresh Docs.
60+
61+
## URL share format
62+
63+
LZ-string the JSON, encode URL-safely:
64+
```js
65+
const url = location.origin + location.pathname
66+
+ '#z=' + LZString.compressToEncodedURIComponent(JSON.stringify(getStateJson()));
67+
```
68+
Decode with `LZString.decompressFromEncodedURIComponent`. ~50-line program → ~250B encoded → fits in any URL bar.
69+
70+
## Where this came from
71+
72+
PR #2648 (daslang.io playground): `web/ui` shipped a multi-file `files: [...]` JSON schema in samples since forever, but the loader only ever read `files[0]`. Adding tabs was a Doc map + `swapDoc` per click. The autosave-on-tab-switch bug landed in round 6 of review — `pgSwitchFile` updated state but didn't call `autosave()`. Three rounds of testing later, Copilot caught it.
73+
74+
## Bonus
75+
76+
`editor.refresh()` after layout changes (e.g. splitter resize) — CM caches gutter widths and won't redraw unless told.
77+
78+
## Questions
79+
- How do I implement a multi-file editor in CodeMirror 5 with per-tab undo history and localStorage autosave?
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
slug: css-import-must-precede-all-other-rules-or-silently-dropped
3+
title: My CSS @import isn't loading any fonts — what's wrong?
4+
created: 2026-05-13
5+
last_verified: 2026-05-13
6+
links: []
7+
---
8+
9+
**`@import` rules must precede every other style rule in the stylesheet (except `@charset` / `@layer`). A browser silently drops any `@import` placed after a normal rule.**
10+
11+
Per CSS 2.1 §6.3:
12+
> Any @import rules must precede all other rules (except the @charset rule if present).
13+
14+
Modern browsers honor this strictly — there is no console warning, the imported stylesheet just never loads. Symptoms: web fonts fall back to system serif, retoken colors revert to defaults, etc.
15+
16+
## The trap
17+
18+
```css
19+
:root {
20+
--bg: #0d0c0a;
21+
--fg: #e8e2d2;
22+
}
23+
24+
/* SILENTLY IGNORED — comes after :root */
25+
@import url('https://fonts.googleapis.com/css2?family=Inter+Tight&display=swap');
26+
27+
body {
28+
background: var(--bg); /* works */
29+
font-family: 'Inter Tight', sans-serif; /* falls back to system */
30+
}
31+
```
32+
33+
The whole file looks "right" — the rules work, just no Inter Tight. Easy to miss because the fallback chain renders something reasonable.
34+
35+
## Fix
36+
37+
Move the `@import` to the very top, before any rule (including `:root`):
38+
39+
```css
40+
@import url('https://fonts.googleapis.com/css2?family=Inter+Tight&display=swap');
41+
42+
:root { ... }
43+
body { ... }
44+
```
45+
46+
You can also avoid `@import` entirely by linking the stylesheet via `<link rel="stylesheet">` in HTML — same fonts, no ordering trap.
47+
48+
## Where this bit me
49+
50+
PR #2648 (daslang.io docs theme): `doc/source/_static/custom.css` had the Google Fonts `@import` after the `:root` block defining design tokens. Browsers dropped the import, Sphinx HTML rendered in system serif despite the file looking correct. Fix was hoisting the `@import` to line 11 of the file, before `:root` on line 13.
51+
52+
Spec reference: https://www.w3.org/TR/CSS21/cascade.html#at-import
53+
54+
## Questions
55+
- My CSS @import isn't loading any fonts — what's wrong?
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
slug: css-important-beats-js-inline-style-debug-cascade-first
3+
title: My JavaScript element.style.X assignment has no visible effect — why doesn't the element move/resize?
4+
created: 2026-05-13
5+
last_verified: 2026-05-13
6+
links: []
7+
---
8+
9+
**Suspect `!important` in the CSS cascade. Inline styles (set via `element.style.X = ...` or the `style="..."` attribute) lose to a stylesheet rule that has `!important`. Higher-specificity selectors without `!important` would normally win, but `!important` flips that order.**
10+
11+
CSS specificity order:
12+
1. Author `!important` declarations
13+
2. Inline style attribute (including JS-assigned `element.style.X`)
14+
3. Author normal declarations
15+
4. User agent styles
16+
17+
So `.main_col { flex: 1 1 0 !important; }` defeats `el.style.flex = '0 0 50%'`.
18+
19+
## How to spot it
20+
21+
1. Open DevTools → Elements → Styles panel. The "won" rule is the one not crossed out at the top, with the smaller specificity. Look for `!important` flags.
22+
2. Inline `style="..."` shows at the top of the Styles panel — if it's crossed out and a stylesheet rule isn't, `!important` is the culprit.
23+
24+
## Two fixes
25+
26+
1. **Remove `!important` from the stylesheet rule, increase its specificity instead.** Use `.main_workspace > .main_col` rather than `.main_col` — same effect, no `!important` needed, JS inline overrides work normally.
27+
2. **Set the JS-applied style as `!important` too.** `el.style.setProperty('flex', '0 0 50%', 'important')`. Use sparingly — `!important` arms races scale badly.
28+
29+
Option 1 is almost always right. `!important` in source CSS is usually a code smell; replacing it with selector specificity makes the cascade predictable.
30+
31+
## Where this bit me
32+
33+
PR #2648 (daslang playground splitter): drag handle wouldn't move the columns — `applyPct` was setting `left.style.flex` correctly, but the user-visible 50/50 layout never changed. `.main_col { flex: 1 1 0 !important; }` in `forge-skin.css` was beating the inline assignment. Fix: drop `!important`, use `.main_workspace > .main_col` selector specificity instead. The first-instinct debugging move (assume JS is broken) wastes time when the actual problem is CSS specificity.
34+
35+
## General lesson
36+
37+
When a JS-set inline style appears to have no effect, **inspect the Styles panel in DevTools first** before reaching for `console.log` or rewriting the JS — the cascade tells you exactly which rule won.
38+
39+
## Questions
40+
- My JavaScript element.style.X assignment has no visible effect — why doesn't the element move/resize?
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
slug: github-pages-deploy-pages-publishes-snapshot-not-overlay
3+
title: Does GitHub Pages preserve previously-deployed files when actions/deploy-pages publishes a new artifact?
4+
created: 2026-05-13
5+
last_verified: 2026-05-13
6+
links: []
7+
---
8+
9+
**No — `actions/deploy-pages` publishes the artifact as a complete site snapshot, not a layer over the prior deploy.**
10+
11+
If you upload `_site/` and it's missing a directory (e.g. `_site/playground/`) that was present in the previous deploy, that path will 404 on the live site after this deploy completes. Pages does not retain or merge files from earlier deploys.
12+
13+
## Implication for CI staging
14+
15+
You cannot use "skip this directory when the source artifacts are missing" as a graceful-degradation strategy — that will 404 the route. Two viable options when an artifact is unbuildable:
16+
17+
1. **Stage a placeholder `index.html`** so the URL keeps resolving to something useful (status page, "rebuilding" notice, link back to the rest of the site). Keeps the route alive while you re-roll.
18+
2. **Fail the deploy** when artifacts are missing. Couples the whole site publish to the failing component — only acceptable if the missing piece is critical.
19+
20+
The "graceful skip" intuition (let docs/blog ship even when WASM is broken) is correct in principle, but the implementation must explicitly stage *something* under the route — silent omission is the bug.
21+
22+
## Where this bit me
23+
24+
PR #2648 (daslang.io playground): WASM build had `continue-on-error: true` so emsdk hiccups wouldn't block docs/blog. Round-4 fix gated the playground staging on `daslang_static.{js,wasm}` existing — but with no `else` branch, `_site/playground/` was absent on WASM failure, which would 404 `/playground/` on the next deploy. Round-5 added the `site/playground/placeholder.html` fallback that explains the runtime is rebuilding.
25+
26+
## Quick check
27+
28+
`actions/deploy-pages@v4` reference: https://github.com/actions/deploy-pages — "deploys an artifact" (singular, replaces). The artifact is whatever `actions/upload-pages-artifact` packaged, which is the full `_site/` you staged.
29+
30+
## Questions
31+
- Does GitHub Pages preserve previously-deployed files when actions/deploy-pages publishes a new artifact?
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
date: 2026-05-13
3+
tag: 0.6.2
4+
title: Daslang 0.6.2 released.
5+
link: https://github.com/GaijinEntertainment/daScript/releases/tag/v0.6.2-RC3
6+
---

0 commit comments

Comments
 (0)