jsgui3-server 0.0.149 → 0.0.151

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 (106) hide show
  1. package/.github/agents/Mobile Developer.agent.md +89 -0
  2. package/.github/instructions/copilot.instructions.md +1 -0
  3. package/AGENTS.md +6 -0
  4. package/README.md +185 -0
  5. package/admin-ui/client.js +73 -43
  6. package/admin-ui/v1/admin_auth_service.js +197 -0
  7. package/admin-ui/v1/admin_user_store.js +71 -0
  8. package/admin-ui/v1/client.js +17 -0
  9. package/admin-ui/v1/controls/admin_shell.js +1399 -0
  10. package/admin-ui/v1/controls/group_box.js +84 -0
  11. package/admin-ui/v1/controls/stat_card.js +125 -0
  12. package/admin-ui/v1/server.js +658 -0
  13. package/admin-ui/v1/utils/formatters.js +68 -0
  14. package/docs/admin-extension-guide.md +345 -0
  15. package/docs/api-reference.md +383 -303
  16. package/docs/books/adaptive-control-improvements/01-control-candidate-matrix.md +122 -0
  17. package/docs/books/adaptive-control-improvements/02-tier-1-layout-playbooks.md +207 -0
  18. package/docs/books/adaptive-control-improvements/03-tier-2-navigation-form-overlay.md +140 -0
  19. package/docs/books/adaptive-control-improvements/04-cross-cutting-platform-functionality.md +141 -0
  20. package/docs/books/adaptive-control-improvements/05-styling-theming-density-upgrades.md +114 -0
  21. package/docs/books/adaptive-control-improvements/06-testing-quality-gates.md +97 -0
  22. package/docs/books/adaptive-control-improvements/07-delivery-roadmap-and-ownership.md +137 -0
  23. package/docs/books/adaptive-control-improvements/08-appendix-tier1-acceptance-and-pr-templates.md +261 -0
  24. package/docs/books/adaptive-control-improvements/README.md +66 -0
  25. package/docs/books/admin-ui-authentication/01-threat-model-and-goals.md +124 -0
  26. package/docs/books/admin-ui-authentication/02-session-model-and-token-model.md +75 -0
  27. package/docs/books/admin-ui-authentication/03-auth-middleware-patterns.md +77 -0
  28. package/docs/books/admin-ui-authentication/README.md +25 -0
  29. package/docs/books/creating-a-new-admin-ui/01-introduction-and-vision.md +130 -0
  30. package/docs/books/creating-a-new-admin-ui/02-architecture-and-data-flow.md +298 -0
  31. package/docs/books/creating-a-new-admin-ui/03-server-introspection.md +381 -0
  32. package/docs/books/creating-a-new-admin-ui/04-admin-module-adapter-layer.md +592 -0
  33. package/docs/books/creating-a-new-admin-ui/05-domain-controls-stat-cards-and-gauges.md +513 -0
  34. package/docs/books/creating-a-new-admin-ui/06-domain-controls-process-manager.md +544 -0
  35. package/docs/books/creating-a-new-admin-ui/07-domain-controls-resource-pool-inspector.md +493 -0
  36. package/docs/books/creating-a-new-admin-ui/08-domain-controls-route-table-and-api-explorer.md +586 -0
  37. package/docs/books/creating-a-new-admin-ui/09-domain-controls-log-viewer-and-activity-feed.md +490 -0
  38. package/docs/books/creating-a-new-admin-ui/10-domain-controls-build-status-and-bundle-inspector.md +526 -0
  39. package/docs/books/creating-a-new-admin-ui/11-domain-controls-configuration-panel.md +808 -0
  40. package/docs/books/creating-a-new-admin-ui/12-admin-shell-layout-sidebar-navigation.md +210 -0
  41. package/docs/books/creating-a-new-admin-ui/13-telemetry-integration.md +556 -0
  42. package/docs/books/creating-a-new-admin-ui/14-realtime-sse-observable-integration.md +485 -0
  43. package/docs/books/creating-a-new-admin-ui/15-styling-theming-aero-design-system.md +521 -0
  44. package/docs/books/creating-a-new-admin-ui/16-testing-and-quality-assurance.md +147 -0
  45. package/docs/books/creating-a-new-admin-ui/17-next-steps-process-resource-roadmap.md +356 -0
  46. package/docs/books/creating-a-new-admin-ui/README.md +68 -0
  47. package/docs/books/device-adaptive-composition/01-platform-feature-audit.md +177 -0
  48. package/docs/books/device-adaptive-composition/02-responsive-composition-model.md +187 -0
  49. package/docs/books/device-adaptive-composition/03-data-model-vs-view-model.md +231 -0
  50. package/docs/books/device-adaptive-composition/04-styling-theme-breakpoints.md +234 -0
  51. package/docs/books/device-adaptive-composition/05-showcase-app-multi-device-assessment.md +193 -0
  52. package/docs/books/device-adaptive-composition/06-implementation-patterns-and-apis.md +346 -0
  53. package/docs/books/device-adaptive-composition/07-testing-harness-and-quality-gates.md +265 -0
  54. package/docs/books/device-adaptive-composition/08-roadmap-and-adoption-plan.md +250 -0
  55. package/docs/books/device-adaptive-composition/README.md +47 -0
  56. package/docs/comparison-report-express-plex-cpanel.md +549 -0
  57. package/docs/comprehensive-documentation.md +220 -220
  58. package/docs/configuration-reference.md +227 -204
  59. package/docs/designs/server-admin-interface-aero.svg +611 -0
  60. package/docs/middleware-guide.md +236 -0
  61. package/docs/system-architecture.md +24 -18
  62. package/docs/troubleshooting.md +84 -53
  63. package/middleware/compression.js +217 -0
  64. package/middleware/index.js +15 -0
  65. package/module.js +19 -11
  66. package/package.json +1 -1
  67. package/serve-factory.js +29 -0
  68. package/server.js +280 -20
  69. package/tests/README.md +5 -0
  70. package/tests/admin-ui-jsgui-controls.test.js +581 -0
  71. package/tests/test-runner.js +1 -0
  72. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +0 -40
  73. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +0 -39
  74. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +0 -39
  75. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +0 -39
  76. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +0 -39
  77. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +0 -40
  78. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +0 -39
  79. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +0 -40
  80. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +0 -40
  81. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +0 -39
  82. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +0 -39
  83. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +0 -44
  84. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +0 -45
  85. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +0 -39
  86. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +0 -39
  87. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +0 -42
  88. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +0 -40
  89. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +0 -43
  90. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +0 -40
  91. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +0 -40
  92. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +0 -40
  93. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +0 -39
  94. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +0 -39
  95. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +0 -39
  96. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +0 -39
  97. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +0 -39
  98. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +0 -41
  99. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +0 -44
  100. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +0 -40
  101. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +0 -40
  102. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +0 -39
  103. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +0 -39
  104. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +0 -39
  105. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +0 -39
  106. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +0 -39
@@ -0,0 +1,193 @@
1
+ # Chapter 5 — Showcase App Multi-Device Assessment
2
+
3
+ ## About the Showcase App
4
+
5
+ The showcase app (`dev-examples/binding/showcase_app/`) is a comprehensive demo of jsgui3 controls featuring:
6
+
7
+ - A **navigation sidebar** with section links
8
+ - A **Theme Studio** panel with preset switching, CSS variable editing, and export/import
9
+ - A **main content area** with sections: status bars, button variants, filter chips, tabbed panels, accordions, drawers, code editors, and console panels
10
+ - **localStorage persistence** for theme preferences
11
+
12
+ The current layout uses a three-column CSS Grid shell: `240px` (nav) + `260px` (theme studio) + `1fr` (content). This chapter assesses how this app would behave across device categories and identifies concrete adaptation points.
13
+
14
+ ## Current Layout Analysis
15
+
16
+ ### What Works Well Already
17
+
18
+ The app benefits from several naturally responsive behaviors:
19
+
20
+ - **CSS Grid with `1fr`** — the content area is flexible and fills available space
21
+ - **Token-driven styling** — control sizes, padding, and typography are all token-based, not hardcoded
22
+ - **Section-based content architecture** — each demo section is independent and scrollable
23
+ - **Interactive controls work** — tab switching, accordion expansion, drawer toggle, console append all function correctly regardless of viewport
24
+
25
+ ### Where It Breaks
26
+
27
+ At narrower widths, the three-column shell creates problems:
28
+
29
+ - Below ~800px, the nav + theme studio consume most of the width, leaving the content area cramped
30
+ - Below ~600px, the three fixed columns overflow horizontally
31
+ - The theme studio's color pickers and text inputs become too narrow to use comfortably
32
+ - The navigation sidebar provides no value on phone screens where vertical scrolling is the natural navigation pattern
33
+
34
+ ## Phone Assessment (320–480px)
35
+
36
+ ### Target Devices
37
+
38
+ iPhone SE (375×667), iPhone 14 (390×844), Android small (360×800)
39
+
40
+ ### Structural Adaptation Needed
41
+
42
+ The three-column layout must collapse to a **single-column stack** with drawer-based access to navigation and theme tools:
43
+
44
+ | Desktop Component | Phone Adaptation |
45
+ |-------------------|-----------------|
46
+ | Navigation sidebar (240px) | Hamburger menu → Drawer overlay |
47
+ | Theme Studio panel (260px) | Settings icon → Drawer overlay or bottom sheet |
48
+ | Content area (1fr) | Full width, single column |
49
+ | Section headers | Become scroll-to anchors, possibly sticky |
50
+
51
+ ### Specific Concerns
52
+
53
+ 1. **Touch targets** — Button groups (icon buttons, split buttons) need minimum 44×44px hit areas. The current icon buttons may be too small for comfortable thumb interaction.
54
+
55
+ 2. **Filter chips** — The Cluster layout wraps naturally, which is good. But on narrow screens, a horizontally-scrollable strip might be better than wrapping to 3–4 rows.
56
+
57
+ 3. **Tabbed panels** — Horizontal tabs work on phone if there are ≤4 tabs. More tabs should overflow-scroll horizontally rather than wrapping to multiple lines.
58
+
59
+ 4. **Code editor** — Horizontal scrolling for code is acceptable on phone (developers expect it), but the container should be full-bleed to maximize line length.
60
+
61
+ 5. **Theme Studio** — The full theme editor is a power-user feature that may not need to be accessible on phone. A simplified "preset only" mode (just the preset buttons) is more appropriate.
62
+
63
+ ### Recommended Composition
64
+
65
+ ```
66
+ Phone layout:
67
+ ┌──────────────────┐
68
+ │ [≡] App Title [⚙]│ ← hamburger (nav) + gear (theme presets)
69
+ ├──────────────────┤
70
+ │ │
71
+ │ Content area │ ← full width, vertical scroll
72
+ │ (all sections │
73
+ │ stacked) │
74
+ │ │
75
+ └──────────────────┘
76
+ ```
77
+
78
+ ## Tablet Portrait Assessment (768×1024)
79
+
80
+ ### Target Devices
81
+
82
+ iPad (768×1024), iPad Air (820×1180), Surface Go (800×1280)
83
+
84
+ ### Structural Adaptation Needed
85
+
86
+ Two columns work well at this width. The question is which two columns:
87
+
88
+ **Option A: Content + Theme Studio**
89
+
90
+ Drop the nav sidebar. Use a collapsible top bar or hamburger for navigation. Keep the theme studio visible because it's the interactive focus of a showcase app.
91
+
92
+ **Option B: Nav + Content**
93
+
94
+ Drop the theme studio to a slide-over panel. Keep the nav visible for section discovery. This prioritizes browse-ability.
95
+
96
+ For a showcase app, **Option A** is stronger — the theme studio is the unique value proposition. Section navigation can use a compact horizontal pill bar or a dropdown.
97
+
98
+ ### Specific Concerns
99
+
100
+ 1. **Three columns at 768px** — the current 240 + 260 + remaining = 268px for content. That's too cramped for code editors and data tables. Two columns are necessary.
101
+
102
+ 2. **Spacing** — Desktop spacing tokens (`--j-space-5: 24px`) may feel too generous and waste tablet real estate. A `cozy` density mode with slightly tighter spacing would help.
103
+
104
+ 3. **Split pane** — If both content and theme studio are visible, a Split_Pane with a drag handle would let users choose the balance. The existing Split_Pane primitive supports this.
105
+
106
+ ### Recommended Composition
107
+
108
+ ```
109
+ Tablet portrait layout:
110
+ ┌─────────────────────────────┐
111
+ │ [≡] App Title [Nav pills]│ ← hamburger for full nav, pills for top sections
112
+ ├──────────────┬──────────────┤
113
+ │ │ │
114
+ │ Content │ Theme │
115
+ │ area │ Studio │
116
+ │ │ │
117
+ │ │ │
118
+ └──────────────┴──────────────┘
119
+ ```
120
+
121
+ ## Tablet Landscape Assessment (1024×768)
122
+
123
+ ### Target Devices
124
+
125
+ iPad landscape (1024×768), Surface Go landscape (1280×800)
126
+
127
+ ### Structural Adaptation
128
+
129
+ At 1024px, the full three-column layout becomes viable, though with tighter proportions than desktop:
130
+
131
+ - Nav: 180px (narrower than desktop's 240px)
132
+ - Theme Studio: 220px (narrower than desktop's 260px)
133
+ - Content: 624px (adequate for most controls)
134
+
135
+ Alternatively, keep the two-column layout from tablet portrait and add the nav as a collapsible sidebar, deferring to the Drawer pattern when space gets tight.
136
+
137
+ ### Specific Concerns
138
+
139
+ 1. **Vertical space** — At 768px height, sticky panels should be limited. The theme studio shouldn't use `position: sticky` with a long panel because it would consume most of the viewport height.
140
+
141
+ 2. **Keyboard navigation** — Tablet landscape is often used with a keyboard (Surface, iPad with Magic Keyboard). The full keyboard navigation paths should work, including tab-to-section and arrow-key-within-components.
142
+
143
+ ## Desktop Assessment (1280–1920px+)
144
+
145
+ ### What Works Well
146
+
147
+ The current layout is designed for desktop and works well:
148
+
149
+ - Three-column shell provides clear information hierarchy
150
+ - Generous spacing aids readability
151
+ - Theme studio is always accessible for experimentation
152
+ - Navigation sidebar gives quick section access
153
+
154
+ ### Remaining Concerns
155
+
156
+ 1. **Max content width** — On very wide screens (1920px+), text lines in the content area become too long for comfortable reading. Adding a `max-width: 900px` on text-heavy content or using the `Center` primitive would help.
157
+
158
+ 2. **Pinnable panels** — On wide screens, users might want to unpin the theme studio or nav to gain more content space. A pin/unpin toggle on panel headers would add flexibility.
159
+
160
+ 3. **Section subnavigation** — As the control catalog grows, flat scrolling through many sections becomes tedious. Section-level dropdowns or an accordion sidebar structure would improve discoverability.
161
+
162
+ ## Cross-Device Summary Table
163
+
164
+ | Feature | Phone | Tablet Portrait | Tablet Landscape | Desktop |
165
+ |---------|-------|----------------|------------------|---------|
166
+ | Columns | 1 | 2 | 2–3 | 3 |
167
+ | Navigation | Drawer | Pills / dropdown | Sidebar or collapsible | Sidebar |
168
+ | Theme Studio | Presets only | Side panel | Side panel | Side panel |
169
+ | Density | Comfortable | Cozy | Cozy | Default |
170
+ | Touch targets | 44px+ | 44px+ | 36px+ | 32px+ |
171
+ | Content max-width | Full bleed | ~500px | ~600px | ~900px |
172
+
173
+ ## Adaptation Points in the Current Code
174
+
175
+ Looking at the showcase app's `compose_ui()` method, the key adaptation opportunities are:
176
+
177
+ 1. **Shell grid template** — currently hardcoded as `240px 260px 1fr`. Should be derived from `layout_mode`.
178
+
179
+ 2. **Navigation panel** — currently composed inline. Should use `compose_adaptive` to switch between inline sidebar, pill bar, and drawer.
180
+
181
+ 3. **Theme studio** — currently composed inline. Should collapse to presets-only on phone, slide-over on tablet.
182
+
183
+ 4. **Section card widths** — currently use flexible styling. Could set `max-width` per layout mode to prevent overly wide content.
184
+
185
+ 5. **Token density** — currently static. Should respond to `density_mode` attribute on root.
186
+
187
+ ## Browser Window Variability
188
+
189
+ A final consideration: users frequently resize browser windows, especially when using split-screen on desktop. The layout should adapt fluidly between 600px and 1400px without sudden jumps or broken states.
190
+
191
+ The recommended approach uses CSS for fluid adaptation within a mode (e.g., flexible gaps and content wrapping) and JavaScript composition changes at mode boundaries (e.g., switching from two-column to drawer). This dual strategy avoids the flicker of constant recomposition while still handling structural changes cleanly.
192
+
193
+ **Next:** [Chapter 6](06-implementation-patterns-and-apis.md) provides the concrete patterns and API designs needed to implement these adaptations.
@@ -0,0 +1,346 @@
1
+ # Chapter 6 — Implementation Patterns and APIs
2
+
3
+ ## Design Goal
4
+
5
+ Make high-level app code easy to write. A developer building a dashboard shouldn't need to understand viewport detection, mode resolution, or responsive token cascades. They should express adaptive intent in a few lines and get correct behavior across devices.
6
+
7
+ Complexity belongs in the mid-level platform code — the services, helpers, and mixins that resolve intent into concrete outcomes. And the low-level foundations in lang-tools and html-core handle eventing, model synchronization, and rendering.
8
+
9
+ This chapter defines the concrete patterns and APIs that make this work.
10
+
11
+ ## Pattern 1: View Environment Service
12
+
13
+ ### What It Does
14
+
15
+ A lightweight service that observes the runtime environment and publishes normalized state. Every control and view model can read from it. It runs client-side only (on the server, it returns safe defaults).
16
+
17
+ ### API Design
18
+
19
+ ```js
20
+ const View_Environment = require('jsgui3-html/utils/view_environment');
21
+
22
+ // Created once per page context
23
+ const env = new View_Environment({
24
+ // Optional: override default breakpoints
25
+ breakpoints: {
26
+ phone_max: 599,
27
+ tablet_max: 1023
28
+ },
29
+ // Optional: default for SSR
30
+ defaults: {
31
+ layout_mode: 'desktop',
32
+ density_mode: 'cozy',
33
+ interaction_mode: 'pointer',
34
+ motion_mode: 'normal'
35
+ }
36
+ });
37
+ ```
38
+
39
+ ### Published State
40
+
41
+ ```js
42
+ env.state
43
+ // → {
44
+ // viewport: { width: 768, height: 1024, orientation: 'portrait' },
45
+ // layout_mode: 'tablet',
46
+ // density_mode: 'cozy',
47
+ // interaction_mode: 'touch',
48
+ // motion_mode: 'normal'
49
+ // }
50
+ ```
51
+
52
+ ### Change Events
53
+
54
+ ```js
55
+ env.on('change', (new_state, old_state) => {
56
+ // Fires when any property changes
57
+ });
58
+
59
+ env.on('change:layout_mode', (new_mode, old_mode) => {
60
+ // Fires only when layout_mode changes
61
+ });
62
+ ```
63
+
64
+ ### Integration with Page Context
65
+
66
+ ```js
67
+ // In the app's initialization:
68
+ const ctx = new jsgui.Page_Context();
69
+ ctx.view_environment = new View_Environment();
70
+
71
+ // In any control:
72
+ const mode = this.context.view_environment.state.layout_mode;
73
+ ```
74
+
75
+ ### Implementation Notes
76
+
77
+ The service should:
78
+ - Listen to `window.resize` (debounced) and `matchMedia` for reduced-motion and pointer queries
79
+ - Derive `layout_mode` from viewport width using configurable breakpoints
80
+ - Derive `interaction_mode` from `matchMedia('(pointer: coarse)')` — coarse = touch, fine = pointer, both = hybrid
81
+ - Derive `motion_mode` from `matchMedia('(prefers-reduced-motion: reduce)')`
82
+ - Set `data-layout-mode`, `data-density-mode`, and `data-interaction-mode` attributes on `document.documentElement`
83
+ - On the server (`typeof window === 'undefined'`), return static defaults
84
+
85
+ ### Server-Side Behavior
86
+
87
+ ```js
88
+ // On the server, the environment returns defaults:
89
+ const env = new View_Environment();
90
+ env.state.layout_mode // → 'desktop' (safe default)
91
+ env.state.viewport // → { width: 1280, height: 900, orientation: 'landscape' }
92
+
93
+ // Controls can use this for SSR composition:
94
+ if (env.state.layout_mode === 'phone') {
95
+ // Only reached if server received a mobile hint
96
+ this.compose_phone_shell();
97
+ } else {
98
+ this.compose_desktop_shell();
99
+ }
100
+ ```
101
+
102
+ ## Pattern 2: Adaptive Composition Helper
103
+
104
+ ### The Problem It Solves
105
+
106
+ Without a helper, every adaptive control writes the same boilerplate:
107
+
108
+ ```js
109
+ // Repeated in every control:
110
+ const mode = this.context.view_environment.state.layout_mode;
111
+ if (mode === 'phone') this.compose_phone();
112
+ else if (mode === 'tablet') this.compose_tablet();
113
+ else this.compose_desktop();
114
+
115
+ // And then separately, resize handling:
116
+ this.context.view_environment.on('change:layout_mode', (new_mode) => {
117
+ this.clear();
118
+ if (new_mode === 'phone') this.compose_phone();
119
+ else if (new_mode === 'tablet') this.compose_tablet();
120
+ else this.compose_desktop();
121
+ });
122
+ ```
123
+
124
+ ### The Helper API
125
+
126
+ ```js
127
+ const { compose_adaptive } = require('jsgui3-html/utils/adaptive');
128
+
129
+ class My_Dashboard extends Data_Model_View_Model_Control {
130
+ constructor(spec) {
131
+ super(spec);
132
+
133
+ compose_adaptive(this, {
134
+ phone: () => this.compose_phone_shell(),
135
+ tablet: () => this.compose_tablet_shell(),
136
+ desktop: () => this.compose_desktop_shell()
137
+ });
138
+ }
139
+
140
+ compose_phone_shell() {
141
+ const { context } = this;
142
+ this.header = new Stack({ context, direction: 'row', align: 'center' });
143
+ this.header.add(new Icon_Button({ context, icon: '≡', action: () => this.nav_drawer.open() }));
144
+ this.header.add(new Control({ context, text: 'Dashboard' }));
145
+ this.add(this.header);
146
+
147
+ this.nav_drawer = new Drawer({ context, position: 'left' });
148
+ this.add(this.nav_drawer);
149
+
150
+ this.content = new Stack({ context, direction: 'column' });
151
+ this.add(this.content);
152
+ }
153
+
154
+ compose_tablet_shell() {
155
+ const { context } = this;
156
+ this.shell = new Grid_Gap({ context, columns: '1fr 260px', gap: 12 });
157
+ this.content = new Stack({ context, direction: 'column' });
158
+ this.tools = new Stack({ context, direction: 'column' });
159
+ this.shell.add(this.content);
160
+ this.shell.add(this.tools);
161
+ this.add(this.shell);
162
+ }
163
+
164
+ compose_desktop_shell() {
165
+ const { context } = this;
166
+ this.shell = new Grid_Gap({ context, columns: '240px 1fr 260px', gap: 16 });
167
+ this.nav = new Stack({ context, direction: 'column' });
168
+ this.content = new Stack({ context, direction: 'column' });
169
+ this.tools = new Stack({ context, direction: 'column' });
170
+ this.shell.add(this.nav);
171
+ this.shell.add(this.content);
172
+ this.shell.add(this.tools);
173
+ this.add(this.shell);
174
+ }
175
+ }
176
+ ```
177
+
178
+ ### What `compose_adaptive` Does Internally
179
+
180
+ 1. Reads `this.context.view_environment.state.layout_mode`
181
+ 2. Calls the matching composition function
182
+ 3. Registers a listener for `change:layout_mode`
183
+ 4. On mode change: clears the control's children, calls the new composition function
184
+ 5. Returns a cleanup function for the listener (used in `destroy()`)
185
+
186
+ ### Fallback Behavior
187
+
188
+ If no exact match exists, the helper falls back intelligently:
189
+
190
+ ```js
191
+ compose_adaptive(this, {
192
+ phone: () => this.compose_narrow(),
193
+ desktop: () => this.compose_wide()
194
+ // No explicit tablet branch
195
+ });
196
+ // tablet falls back to → phone (nearest smaller mode)
197
+ ```
198
+
199
+ The fallback order is: exact match → next smaller mode → `desktop` default.
200
+
201
+ ## Pattern 3: Responsive Param Resolver
202
+
203
+ ### The Problem
204
+
205
+ Controls accept configuration parameters (`size`, `columns`, `density`). Currently, these are static — the same value is used regardless of screen size. Developers end up writing per-mode overrides manually.
206
+
207
+ ### The Solution
208
+
209
+ Extend `theme_params.js` to accept mode-branched parameter objects:
210
+
211
+ ```js
212
+ class My_Table extends Data_Model_View_Model_Control {
213
+ constructor(spec) {
214
+ super(spec);
215
+
216
+ // Responsive params — resolved automatically by layout_mode
217
+ this.resolve_responsive_params({
218
+ default: { columns: 8, row_height: 36, show_header: true },
219
+ phone: { columns: 2, row_height: 44, show_header: false },
220
+ tablet: { columns: 5, row_height: 40, show_header: true }
221
+ });
222
+
223
+ // this.params.columns → 8 (on desktop), 2 (on phone), etc.
224
+ }
225
+ }
226
+ ```
227
+
228
+ The resolver:
229
+ 1. Reads `context.view_environment.state.layout_mode`
230
+ 2. Merges the matching branch over the `default` branch
231
+ 3. Exposes the merged result as `this.params`
232
+ 4. Re-resolves and emits change events when mode changes
233
+
234
+ ## Pattern 4: Container-Aware Adaptation
235
+
236
+ ### Why Viewport Isn't Always Enough
237
+
238
+ A control embedded in a narrow sidebar behaves like a "phone" control even on a desktop viewport. Container queries (CSS `@container`) address this for styling, but composition decisions need JavaScript-level container awareness too.
239
+
240
+ ### Proposed Utility
241
+
242
+ ```js
243
+ const { container_mode } = require('jsgui3-html/utils/adaptive');
244
+
245
+ // In a control's activate():
246
+ activate() {
247
+ super.activate();
248
+
249
+ // Observe own container width, map to local mode
250
+ container_mode(this, {
251
+ narrow: { max_width: 400 },
252
+ medium: { max_width: 800 },
253
+ wide: { min_width: 801 }
254
+ }, (mode) => {
255
+ this.view.data.model.set('container_mode', mode);
256
+ });
257
+ }
258
+ ```
259
+
260
+ This uses `ResizeObserver` on the control's parent element to track available width, independent of viewport size.
261
+
262
+ ### When to Use Container vs Viewport Mode
263
+
264
+ | Concern | Use Viewport | Use Container |
265
+ |---------|-------------|---------------|
266
+ | App shell structure | ✅ | |
267
+ | Navigation morphing | ✅ | |
268
+ | Control internal layout | | ✅ |
269
+ | Embedded widget adaptation | | ✅ |
270
+ | Touch target sizing | ✅ | |
271
+ | Column count in a table | | ✅ |
272
+
273
+ ## Pattern 5: Declarative Region Morphing
274
+
275
+ ### The Concept
276
+
277
+ Some adaptive changes follow repeatable patterns: a sidebar becomes a drawer, a multi-panel becomes tabs, a toolbar becomes an overflow menu. Rather than writing this logic from scratch each time, the platform can provide declarative region morphing:
278
+
279
+ ```js
280
+ // Declarative: specify what morphs into what
281
+ this.nav_region = adaptive_region(this, 'nav', {
282
+ desktop: { as: Stack, options: { direction: 'column', gap: 8 } },
283
+ tablet: { as: Drawer, options: { position: 'left', breakpoint: 768 } },
284
+ phone: { as: Drawer, options: { position: 'left', overlay_mode: true } }
285
+ });
286
+
287
+ // The region control changes type based on layout_mode
288
+ // Content added to the region transfers across morphs
289
+ ```
290
+
291
+ This is a more advanced pattern that builds on `compose_adaptive`. It's best suited for standard layout regions (nav, tools, inspector) where the morph pattern is well-defined.
292
+
293
+ ## Pattern 6: Composition-Safe Persistence
294
+
295
+ ### What to Persist
296
+
297
+ Not all state should survive across sessions. A clear rule:
298
+
299
+ ```
300
+ Persist:
301
+ ✅ User's chosen theme name ('vs-dark')
302
+ ✅ User's density preference ('compact')
303
+ ✅ Panel pinning preference (pinned/unpinned)
304
+ ✅ Custom token overrides ({'--admin-font-size': '12px'})
305
+
306
+ Don't persist:
307
+ ❌ Current layout_mode (should always reflect current viewport)
308
+ ❌ Viewport dimensions
309
+ ❌ Container widths
310
+ ❌ Transient interaction state (hover, focus, scroll position)
311
+ ```
312
+
313
+ ### Persistence Integration
314
+
315
+ ```js
316
+ // Save: extract only stable preferences
317
+ const prefs = {
318
+ theme: this.data.model.get('theme_name'),
319
+ density: this.data.model.get('density_preference'),
320
+ overrides: this.data.model.get('token_overrides')
321
+ };
322
+ localStorage.setItem('app_prefs', JSON.stringify(prefs));
323
+
324
+ // Restore: apply preferences, let environment service handle the rest
325
+ const prefs = JSON.parse(localStorage.getItem('app_prefs'));
326
+ this.data.model.set('theme_name', prefs.theme);
327
+ this.data.model.set('density_preference', prefs.density);
328
+ // layout_mode is NOT restored — it's derived from the current viewport
329
+ ```
330
+
331
+ ## Complexity Budget
332
+
333
+ Each pattern has a different cost for app developers vs platform maintainers:
334
+
335
+ | Pattern | App Developer Effort | Platform Effort | Priority |
336
+ |---------|---------------------|-----------------|----------|
337
+ | View Environment Service | Near zero (read from context) | Medium | P0 |
338
+ | compose_adaptive() | Low (declare branches) | Medium | P0 |
339
+ | Responsive params | Low (declare param branches) | Medium | P1 |
340
+ | Container-aware mode | Medium (choose thresholds) | High | P2 |
341
+ | Region morphing | Low (declare morph map) | High | P2 |
342
+ | Persistence integration | Low (use conventions) | Low | P1 |
343
+
344
+ P0 patterns should ship first — they provide the most value with reasonable implementation cost.
345
+
346
+ **Next:** [Chapter 7](07-testing-harness-and-quality-gates.md) explains how to test responsive behavior systematically across viewport sizes.