@everystate/examples 1.0.0 → 1.0.1

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 (152) hide show
  1. package/README.md +15 -0
  2. package/everyState-core/001-counter/README.md +44 -0
  3. package/everyState-core/001-counter/index.html +79 -0
  4. package/everyState-core/002-counter-improved/README.md +44 -0
  5. package/everyState-core/002-counter-improved/index.html +83 -0
  6. package/everyState-core/003-input-reactive/README.md +44 -0
  7. package/everyState-core/003-input-reactive/index.html +68 -0
  8. package/everyState-core/004-computed-state/README.md +45 -0
  9. package/everyState-core/004-computed-state/index.html +83 -0
  10. package/everyState-core/005-conditional-rendering/README.md +42 -0
  11. package/everyState-core/005-conditional-rendering/index.html +68 -0
  12. package/everyState-core/006-list-rendering/README.md +49 -0
  13. package/everyState-core/006-list-rendering/index.html +92 -0
  14. package/everyState-core/007-form-validation/README.md +52 -0
  15. package/everyState-core/007-form-validation/index.html +108 -0
  16. package/everyState-core/008-undo-redo/README.md +70 -0
  17. package/everyState-core/008-undo-redo/index.html +133 -0
  18. package/everyState-core/009-localStorage-side-effects/README.md +72 -0
  19. package/everyState-core/009-localStorage-side-effects/index.html +80 -0
  20. package/everyState-core/010-decoupled-components/README.md +74 -0
  21. package/everyState-core/010-decoupled-components/index.html +117 -0
  22. package/everyState-core/011-async-patterns/README.md +98 -0
  23. package/everyState-core/011-async-patterns/index.html +132 -0
  24. package/everyState-css/001-stateDrivenCSS/index.html +377 -0
  25. package/everyState-css/002-cssV2FullDemo/index.html +630 -0
  26. package/everyState-view/001/counter/index.css +31 -0
  27. package/everyState-view/001/counter/index.html +50 -0
  28. package/everyState-view/002/datatable/index.css +70 -0
  29. package/everyState-view/002/datatable/index.html +118 -0
  30. package/everyState-view/003/todo/index.css +260 -0
  31. package/everyState-view/003/todo/index.html +218 -0
  32. package/everyState-view/003-input-reactive/README.md +44 -0
  33. package/everyState-view/003-input-reactive/index.html +68 -0
  34. package/everyState-view/004/quotesFetcher/index.css +124 -0
  35. package/everyState-view/004/quotesFetcher/index.html +108 -0
  36. package/everyState-view/004_01/quotesFetcher/app.js +32 -0
  37. package/everyState-view/004_01/quotesFetcher/components/appHeader/appSubtitle.js +2 -0
  38. package/everyState-view/004_01/quotesFetcher/components/appHeader/appTitle.js +2 -0
  39. package/everyState-view/004_01/quotesFetcher/components/appHeader.js +9 -0
  40. package/everyState-view/004_01/quotesFetcher/components/historyHeading.js +2 -0
  41. package/everyState-view/004_01/quotesFetcher/components/historyList/histAuthor.js +2 -0
  42. package/everyState-view/004_01/quotesFetcher/components/historyList/histQuote.js +2 -0
  43. package/everyState-view/004_01/quotesFetcher/components/historyList.js +14 -0
  44. package/everyState-view/004_01/quotesFetcher/components/quoteCard/fetchButton.js +2 -0
  45. package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteAuthor.js +2 -0
  46. package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteMessage.js +2 -0
  47. package/everyState-view/004_01/quotesFetcher/components/quoteCard/quoteText.js +2 -0
  48. package/everyState-view/004_01/quotesFetcher/components/quoteCard.js +11 -0
  49. package/everyState-view/004_01/quotesFetcher/index.css +124 -0
  50. package/everyState-view/004_01/quotesFetcher/index.html +23 -0
  51. package/everyState-view/004_01/quotesFetcher/store.js +35 -0
  52. package/everyState-view/004_02/quotesFetcher/app.js +20 -0
  53. package/everyState-view/004_02/quotesFetcher/components.js +46 -0
  54. package/everyState-view/004_02/quotesFetcher/index.css +124 -0
  55. package/everyState-view/004_02/quotesFetcher/index.html +23 -0
  56. package/everyState-view/004_02/quotesFetcher/store.js +35 -0
  57. package/everyState-view/004_03/quotesFetcher/actions.js +27 -0
  58. package/everyState-view/004_03/quotesFetcher/app.js +19 -0
  59. package/everyState-view/004_03/quotesFetcher/components.js +28 -0
  60. package/everyState-view/004_03/quotesFetcher/index.css +124 -0
  61. package/everyState-view/004_03/quotesFetcher/index.html +23 -0
  62. package/everyState-view/004_03/quotesFetcher/resolve.js +34 -0
  63. package/everyState-view/004_03/quotesFetcher/store.js +11 -0
  64. package/everyState-view/004_04/quotesFetcher/actions.js +66 -0
  65. package/everyState-view/004_04/quotesFetcher/app.js +24 -0
  66. package/everyState-view/004_04/quotesFetcher/components/archive.js +40 -0
  67. package/everyState-view/004_04/quotesFetcher/components/fetcher.js +29 -0
  68. package/everyState-view/004_04/quotesFetcher/components.js +20 -0
  69. package/everyState-view/004_04/quotesFetcher/index.css +283 -0
  70. package/everyState-view/004_04/quotesFetcher/index.html +24 -0
  71. package/everyState-view/004_04/quotesFetcher/resolve.js +34 -0
  72. package/everyState-view/004_04/quotesFetcher/store.js +21 -0
  73. package/everyState-view/004_04/statedump.json +826 -0
  74. package/everyState-view/004_05/quoteExplorer/actions.js +58 -0
  75. package/everyState-view/004_05/quoteExplorer/app.js +27 -0
  76. package/everyState-view/004_05/quoteExplorer/components.js +83 -0
  77. package/everyState-view/004_05/quoteExplorer/index.css +231 -0
  78. package/everyState-view/004_05/quoteExplorer/index.html +23 -0
  79. package/everyState-view/004_05/quoteExplorer/resolve.js +50 -0
  80. package/everyState-view/004_05/quoteExplorer/store.js +33 -0
  81. package/everyState-view/004_06/quoteExplorer/actions.js +21 -0
  82. package/everyState-view/004_06/quoteExplorer/app.js +44 -0
  83. package/everyState-view/004_06/quoteExplorer/components.js +80 -0
  84. package/everyState-view/004_06/quoteExplorer/derived.js +43 -0
  85. package/everyState-view/004_06/quoteExplorer/index.css +346 -0
  86. package/everyState-view/004_06/quoteExplorer/index.html +25 -0
  87. package/everyState-view/004_06/quoteExplorer/intents.js +44 -0
  88. package/everyState-view/004_06/quoteExplorer/policies.js +25 -0
  89. package/everyState-view/004_06/quoteExplorer/resolve.js +51 -0
  90. package/everyState-view/004_06/quoteExplorer/store.js +44 -0
  91. package/everyState-view/004_07/quoteExplorer/app.js +47 -0
  92. package/everyState-view/004_07/quoteExplorer/components.js +85 -0
  93. package/everyState-view/004_07/quoteExplorer/derived.js +43 -0
  94. package/everyState-view/004_07/quoteExplorer/index.css +346 -0
  95. package/everyState-view/004_07/quoteExplorer/index.html +25 -0
  96. package/everyState-view/004_07/quoteExplorer/intents.js +51 -0
  97. package/everyState-view/004_07/quoteExplorer/policies.js +21 -0
  98. package/everyState-view/004_07/quoteExplorer/resolve.js +39 -0
  99. package/everyState-view/004_07/quoteExplorer/store.js +44 -0
  100. package/everyState-view/004_08/quoteExplorer/app.js +78 -0
  101. package/everyState-view/004_08/quoteExplorer/components.js +85 -0
  102. package/everyState-view/004_08/quoteExplorer/derived.js +43 -0
  103. package/everyState-view/004_08/quoteExplorer/index.css +346 -0
  104. package/everyState-view/004_08/quoteExplorer/index.html +25 -0
  105. package/everyState-view/004_08/quoteExplorer/intents.js +51 -0
  106. package/everyState-view/004_08/quoteExplorer/policies.js +21 -0
  107. package/everyState-view/004_08/quoteExplorer/resolve.js +39 -0
  108. package/everyState-view/004_08/quoteExplorer/store.js +44 -0
  109. package/everyState-view/004_08_V2/app.js +78 -0
  110. package/everyState-view/004_08_V2/components/appDetail.js +8 -0
  111. package/everyState-view/004_08_V2/components/appDetailBar.js +7 -0
  112. package/everyState-view/004_08_V2/components/appDetailBarClose.js +8 -0
  113. package/everyState-view/004_08_V2/components/appDetailBarCount.js +7 -0
  114. package/everyState-view/004_08_V2/components/appDetailBarHeading.js +7 -0
  115. package/everyState-view/004_08_V2/components/appDetailQuotes.js +15 -0
  116. package/everyState-view/004_08_V2/components/appHeader.js +7 -0
  117. package/everyState-view/004_08_V2/components/appHeaderSubtitle.js +7 -0
  118. package/everyState-view/004_08_V2/components/appHeaderTitle.js +7 -0
  119. package/everyState-view/004_08_V2/components/appLog.js +7 -0
  120. package/everyState-view/004_08_V2/components/appLogHeading.js +7 -0
  121. package/everyState-view/004_08_V2/components/appLogList.js +16 -0
  122. package/everyState-view/004_08_V2/components/appSearch.js +7 -0
  123. package/everyState-view/004_08_V2/components/appSearchInput.js +9 -0
  124. package/everyState-view/004_08_V2/components/appStats.js +7 -0
  125. package/everyState-view/004_08_V2/components/appStatsContent.js +8 -0
  126. package/everyState-view/004_08_V2/components/appStatsContentFavcount.js +7 -0
  127. package/everyState-view/004_08_V2/components/appStatsContentText.js +7 -0
  128. package/everyState-view/004_08_V2/components/appStatsToggle.js +8 -0
  129. package/everyState-view/004_08_V2/components/appTags.js +7 -0
  130. package/everyState-view/004_08_V2/components/appTagsLabel.js +7 -0
  131. package/everyState-view/004_08_V2/components/appTagsRow.js +15 -0
  132. package/everyState-view/004_08_V2/components/index.js +59 -0
  133. package/everyState-view/004_08_V2/components/utils/css.js +88 -0
  134. package/everyState-view/004_08_V2/components/utils/elements.js +87 -0
  135. package/everyState-view/004_08_V2/components.js +79 -0
  136. package/everyState-view/004_08_V2/derived.js +43 -0
  137. package/everyState-view/004_08_V2/index.css +350 -0
  138. package/everyState-view/004_08_V2/index.html +25 -0
  139. package/everyState-view/004_08_V2/intents.js +51 -0
  140. package/everyState-view/004_08_V2/policies.js +21 -0
  141. package/everyState-view/004_08_V2/resolve.js +39 -0
  142. package/everyState-view/004_08_V2/store.js +44 -0
  143. package/everyState-view/006/api-datatable/index.css +388 -0
  144. package/everyState-view/006/api-datatable/index.html +355 -0
  145. package/everyState-view/007/apiUsers/index.html +307 -0
  146. package/everyState-view/007-form-validation/README.md +52 -0
  147. package/everyState-view/007-form-validation/index.html +108 -0
  148. package/everyState-view/010-decoupled-components/README.md +74 -0
  149. package/everyState-view/010-decoupled-components/index.html +117 -0
  150. package/everyState-view/index.html +36 -0
  151. package/index.js +0 -5
  152. package/package.json +2 -4
@@ -0,0 +1,43 @@
1
+ // ─── Derived state ──────────────────────────────────────────────
2
+ // Subscribe to state.* and auto-compute derived values.
3
+ // Pure recomputation: reads state.*, writes derived.*.
4
+ // No intent knowledge, no view knowledge.
5
+
6
+ export function mountDerived(store) {
7
+ const unsubs = [];
8
+
9
+ function recompute() {
10
+ const quotes = store.get('state.quotes') || [];
11
+ const author = store.get('state.selectedAuthor') || '';
12
+ const search = (store.get('state.searchTerm') || '').toLowerCase().trim();
13
+
14
+ // Filter: only when an author is selected
15
+ let filtered = [];
16
+ if (author) {
17
+ filtered = quotes.filter(q => q.author === author);
18
+ if (search) {
19
+ filtered = filtered.filter(q =>
20
+ q.text.toLowerCase().includes(search)
21
+ );
22
+ }
23
+ }
24
+
25
+ store.set('derived.filteredQuotes', filtered);
26
+ store.set('derived.quoteCount', filtered.length);
27
+ store.set('derived.favCount', quotes.filter(q => q.fav).length);
28
+
29
+ const totalAuthors = (store.get('state.authors') || []).length;
30
+ const favTotal = quotes.filter(q => q.fav).length;
31
+ store.set('derived.stats',
32
+ `${quotes.length} total quotes · ${totalAuthors} authors · ${favTotal} favorites`
33
+ );
34
+ }
35
+
36
+ // Recompute whenever any state.* path changes
37
+ unsubs.push(store.subscribe('state.*', recompute));
38
+
39
+ // Initial computation
40
+ recompute();
41
+
42
+ return () => unsubs.forEach(u => u());
43
+ }
@@ -0,0 +1,346 @@
1
+ /* ─── 004_06 Quote Explorer Pro ─────────────────────────────── */
2
+
3
+ :root {
4
+ --bg: #0f172a;
5
+ --surface: #1e293b;
6
+ --surface-alt: #334155;
7
+ --border: #475569;
8
+ --text: #e2e8f0;
9
+ --text-muted: #94a3b8;
10
+ --accent: #38bdf8;
11
+ --accent-hover: #7dd3fc;
12
+ --danger: #ef4444;
13
+ --fav: #f43f5e;
14
+ --fav-muted: #64748b;
15
+ --success: #22c55e;
16
+ --radius: 8px;
17
+ --shadow: 0 4px 16px rgba(0,0,0,.3);
18
+ }
19
+
20
+ * { margin: 0; padding: 0; box-sizing: border-box; }
21
+
22
+ body {
23
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
24
+ background: var(--bg);
25
+ color: var(--text);
26
+ line-height: 1.6;
27
+ min-height: 100vh;
28
+ }
29
+
30
+ /* ── App layout ── */
31
+
32
+ .app {
33
+ max-width: 720px;
34
+ margin: 0 auto;
35
+ padding: 2rem 1.5rem 4rem;
36
+ }
37
+
38
+ /* ── Header ── */
39
+
40
+ .app-header {
41
+ text-align: center;
42
+ margin-bottom: 2rem;
43
+ }
44
+
45
+ .app-header h1 {
46
+ font-size: 2rem;
47
+ font-weight: 700;
48
+ background: linear-gradient(135deg, var(--accent), #a78bfa);
49
+ -webkit-background-clip: text;
50
+ -webkit-text-fill-color: transparent;
51
+ background-clip: text;
52
+ }
53
+
54
+ .subtitle {
55
+ color: var(--text-muted);
56
+ font-size: 0.85rem;
57
+ margin-top: 0.25rem;
58
+ letter-spacing: 0.04em;
59
+ }
60
+
61
+ /* ── Search bar ── */
62
+
63
+ .search-bar {
64
+ margin-bottom: 1.5rem;
65
+ }
66
+
67
+ .search-input {
68
+ width: 100%;
69
+ padding: 0.7rem 1rem;
70
+ background: var(--surface);
71
+ color: var(--text);
72
+ border: 1px solid var(--border);
73
+ border-radius: var(--radius);
74
+ font-size: 0.95rem;
75
+ outline: none;
76
+ transition: border-color 0.2s;
77
+ }
78
+
79
+ .search-input::placeholder {
80
+ color: var(--text-muted);
81
+ }
82
+
83
+ .search-input:focus {
84
+ border-color: var(--accent);
85
+ }
86
+
87
+ /* ── Author tags ── */
88
+
89
+ .author-tags {
90
+ margin-bottom: 1.5rem;
91
+ }
92
+
93
+ .tags-label {
94
+ display: block;
95
+ font-size: 0.8rem;
96
+ color: var(--text-muted);
97
+ margin-bottom: 0.5rem;
98
+ text-transform: uppercase;
99
+ letter-spacing: 0.06em;
100
+ }
101
+
102
+ .tags-row {
103
+ display: flex;
104
+ flex-wrap: wrap;
105
+ gap: 0.5rem;
106
+ }
107
+
108
+ .tag {
109
+ padding: 0.4rem 0.9rem;
110
+ background: var(--surface);
111
+ color: var(--accent);
112
+ border: 1px solid var(--border);
113
+ border-radius: 999px;
114
+ font-size: 0.85rem;
115
+ cursor: pointer;
116
+ transition: all 0.15s;
117
+ }
118
+
119
+ .tag:hover {
120
+ background: var(--accent);
121
+ color: var(--bg);
122
+ border-color: var(--accent);
123
+ }
124
+
125
+ /* ── Detail panel ── */
126
+
127
+ .detail-panel {
128
+ background: var(--surface);
129
+ border: 1px solid var(--border);
130
+ border-radius: var(--radius);
131
+ padding: 1.25rem;
132
+ margin-bottom: 1.5rem;
133
+ animation: slideIn 0.25s ease-out;
134
+ }
135
+
136
+ @keyframes slideIn {
137
+ from { opacity: 0; transform: translateY(-8px); }
138
+ to { opacity: 1; transform: translateY(0); }
139
+ }
140
+
141
+ .detail-bar {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 0.75rem;
145
+ margin-bottom: 1rem;
146
+ padding-bottom: 0.75rem;
147
+ border-bottom: 1px solid var(--border);
148
+ }
149
+
150
+ .detail-heading {
151
+ font-size: 1.2rem;
152
+ font-weight: 600;
153
+ flex: 1;
154
+ }
155
+
156
+ .badge {
157
+ font-size: 0.75rem;
158
+ padding: 0.2rem 0.6rem;
159
+ background: var(--accent);
160
+ color: var(--bg);
161
+ border-radius: 999px;
162
+ font-weight: 600;
163
+ }
164
+
165
+ .detail-quotes {
166
+ list-style: none;
167
+ }
168
+
169
+ .quote-item {
170
+ display: flex;
171
+ align-items: flex-start;
172
+ gap: 0.75rem;
173
+ padding: 0.6rem 0;
174
+ border-bottom: 1px solid rgba(255,255,255,0.05);
175
+ }
176
+
177
+ .quote-item:last-child {
178
+ border-bottom: none;
179
+ }
180
+
181
+ .quote-text {
182
+ flex: 1;
183
+ font-size: 0.92rem;
184
+ color: var(--text);
185
+ line-height: 1.5;
186
+ }
187
+
188
+ .fav-btn {
189
+ background: none;
190
+ border: none;
191
+ font-size: 1.1rem;
192
+ cursor: pointer;
193
+ color: var(--fav-muted);
194
+ transition: color 0.15s, transform 0.15s;
195
+ padding: 0.2rem;
196
+ line-height: 1;
197
+ }
198
+
199
+ .fav-btn:hover {
200
+ color: var(--fav);
201
+ transform: scale(1.2);
202
+ }
203
+
204
+ .fav-btn.is-fav {
205
+ color: var(--fav);
206
+ }
207
+
208
+ /* ── Buttons ── */
209
+
210
+ .btn {
211
+ padding: 0.45rem 0.9rem;
212
+ border: 1px solid var(--border);
213
+ border-radius: var(--radius);
214
+ background: var(--surface);
215
+ color: var(--text);
216
+ font-size: 0.85rem;
217
+ cursor: pointer;
218
+ transition: all 0.15s;
219
+ }
220
+
221
+ .btn:hover {
222
+ background: var(--surface-alt);
223
+ }
224
+
225
+ .btn-close {
226
+ font-size: 1rem;
227
+ padding: 0.2rem 0.5rem;
228
+ color: var(--text-muted);
229
+ }
230
+
231
+ .btn-close:hover {
232
+ color: var(--danger);
233
+ border-color: var(--danger);
234
+ }
235
+
236
+ .btn-secondary {
237
+ background: var(--surface);
238
+ color: var(--accent);
239
+ border-color: var(--accent);
240
+ }
241
+
242
+ .btn-secondary:hover {
243
+ background: var(--accent);
244
+ color: var(--bg);
245
+ }
246
+
247
+ /* ── Stats section ── */
248
+
249
+ .stats-section {
250
+ margin-bottom: 1.5rem;
251
+ }
252
+
253
+ .stats-content {
254
+ margin-top: 0.75rem;
255
+ padding: 1rem;
256
+ background: var(--surface);
257
+ border: 1px solid var(--border);
258
+ border-radius: var(--radius);
259
+ }
260
+
261
+ .stats-text {
262
+ font-size: 0.9rem;
263
+ color: var(--text-muted);
264
+ }
265
+
266
+ .stats-fav {
267
+ font-size: 0.9rem;
268
+ color: var(--fav);
269
+ margin-top: 0.3rem;
270
+ }
271
+
272
+ /* ── Activity log ── */
273
+
274
+ .log-section {
275
+ margin-top: 1rem;
276
+ }
277
+
278
+ .log-heading {
279
+ font-size: 0.95rem;
280
+ color: var(--text-muted);
281
+ margin-bottom: 0.5rem;
282
+ font-weight: 600;
283
+ }
284
+
285
+ .log-list {
286
+ list-style: none;
287
+ max-height: 260px;
288
+ overflow-y: auto;
289
+ background: var(--surface);
290
+ border: 1px solid var(--border);
291
+ border-radius: var(--radius);
292
+ padding: 0.5rem;
293
+ }
294
+
295
+ .log-entry {
296
+ display: flex;
297
+ gap: 0.6rem;
298
+ padding: 0.35rem 0.5rem;
299
+ font-size: 0.78rem;
300
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
301
+ border-bottom: 1px solid rgba(255,255,255,0.04);
302
+ }
303
+
304
+ .log-entry:last-child {
305
+ border-bottom: none;
306
+ }
307
+
308
+ .log-time {
309
+ color: var(--text-muted);
310
+ min-width: 70px;
311
+ }
312
+
313
+ .log-intent {
314
+ color: var(--accent);
315
+ font-weight: 600;
316
+ min-width: 110px;
317
+ }
318
+
319
+ .log-value {
320
+ color: var(--text);
321
+ opacity: 0.7;
322
+ overflow: hidden;
323
+ text-overflow: ellipsis;
324
+ white-space: nowrap;
325
+ }
326
+
327
+ /* ── Utility ── */
328
+
329
+ .hidden {
330
+ display: none !important;
331
+ }
332
+
333
+ /* ── Scrollbar ── */
334
+
335
+ .log-list::-webkit-scrollbar {
336
+ width: 5px;
337
+ }
338
+
339
+ .log-list::-webkit-scrollbar-track {
340
+ background: var(--surface);
341
+ }
342
+
343
+ .log-list::-webkit-scrollbar-thumb {
344
+ background: var(--border);
345
+ border-radius: 3px;
346
+ }
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>004_06 - Quote Explorer Pro</title>
7
+ <link rel="stylesheet" href="index.css" />
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+
12
+ <script type="importmap">
13
+ {
14
+ "imports": {
15
+ "@everystate/core": "/everystate-core/index.js",
16
+ "@everystate/core/queryClient": "/everystate-core/queryClient.js",
17
+ "@everystate/view/resolve": "/everystate-view/resolve.js",
18
+ "@everystate/view/project": "/everystate-view/project.js",
19
+ "@everystate/perf": "/everystate-perf/index.js"
20
+ }
21
+ }
22
+ </script>
23
+ <script type="module" src="app.js"></script>
24
+ </body>
25
+ </html>
@@ -0,0 +1,44 @@
1
+ // ─── Intent handlers ────────────────────────────────────────────
2
+ // Subscribe to intent.* paths and handle business logic.
3
+ // Actions emit intents; this file processes them.
4
+ // Refs are injected so handlers can toggle view node classes.
5
+
6
+ export function mountIntents(store, refs) {
7
+ const unsubs = [];
8
+
9
+ // ── Select author → show detail panel ──
10
+ unsubs.push(store.subscribe('intent.selectAuthor', (author) => {
11
+ store.set('state.selectedAuthor', author);
12
+ store.set(`view.nodes.${refs.detail}.class`, 'detail-panel');
13
+ }));
14
+
15
+ // ── Close detail → hide panel, clear search ──
16
+ unsubs.push(store.subscribe('intent.closeDetail', () => {
17
+ store.set('state.selectedAuthor', '');
18
+ store.set('state.searchTerm', '');
19
+ store.set(`view.nodes.${refs.detail}.class`, 'detail-panel hidden');
20
+ }));
21
+
22
+ // ── Toggle favorite on a quote ──
23
+ unsubs.push(store.subscribe('intent.toggleFav', (id) => {
24
+ const quotes = store.get('state.quotes') || [];
25
+ store.set('state.quotes', quotes.map(q =>
26
+ q.id === id ? { ...q, fav: !q.fav } : q
27
+ ));
28
+ }));
29
+
30
+ // ── Toggle stats visibility ──
31
+ unsubs.push(store.subscribe('intent.toggleStats', () => {
32
+ const visible = store.get('ui.statsVisible');
33
+ store.set('ui.statsVisible', !visible);
34
+ if (!visible) {
35
+ store.set(`view.nodes.${refs.statsContent}.class`, 'stats-content');
36
+ store.set('ui.toggleLabel', '📊 Hide Stats');
37
+ } else {
38
+ store.set(`view.nodes.${refs.statsContent}.class`, 'stats-content hidden');
39
+ store.set('ui.toggleLabel', '📊 Show Stats');
40
+ }
41
+ }));
42
+
43
+ return () => unsubs.forEach(u => u());
44
+ }
@@ -0,0 +1,25 @@
1
+ // ─── Policy subscriptions ───────────────────────────────────────
2
+ // Cross-cutting concerns via wildcard subscribers.
3
+ // intent.* captures every intent and logs it to the activity feed.
4
+ // This is the "policy layer" pattern from the book (Ch 12, 17).
5
+
6
+ export function mountPolicies(store) {
7
+ const unsubs = [];
8
+
9
+ // Activity log: capture all intents via wildcard
10
+ unsubs.push(store.subscribe('intent.*', ({ path, value }) => {
11
+ const log = store.get('derived.activityLog') || [];
12
+ const intent = path.replace('intent.', '');
13
+ const display = typeof value === 'object'
14
+ ? JSON.stringify(value)
15
+ : String(value ?? '');
16
+ const entry = {
17
+ time: new Date().toLocaleTimeString(),
18
+ intent,
19
+ value: display.length > 30 ? display.slice(0, 30) + '…' : display,
20
+ };
21
+ store.set('derived.activityLog', [entry, ...log].slice(0, 15));
22
+ }));
23
+
24
+ return () => unsubs.forEach(u => u());
25
+ }
@@ -0,0 +1,51 @@
1
+ // ─── Resolve utilities ──────────────────────────────────────────
2
+ // Same pattern as 004_05: resolveTree, resolveActions, buildRefs.
3
+
4
+ /**
5
+ * resolveTree(registry, key)
6
+ * Recursively builds a nested spec from the flat dot-path registry.
7
+ * String references in children/template are replaced with resolved specs.
8
+ */
9
+ export function resolveTree(registry, key) {
10
+ const spec = registry[key];
11
+ if (!spec) return null;
12
+ const resolved = { ...spec };
13
+
14
+ if (Array.isArray(resolved.children)) {
15
+ resolved.children = resolved.children.map(childKey =>
16
+ typeof childKey === 'string' ? resolveTree(registry, childKey) : childKey
17
+ ).filter(Boolean);
18
+ }
19
+
20
+ if (typeof resolved.template === 'string') {
21
+ resolved.template = registry[resolved.template] || resolved.template;
22
+ }
23
+
24
+ return resolved;
25
+ }
26
+
27
+ /**
28
+ * resolveActions(actionRegistry, store, ...deps)
29
+ * Returns a flat map of 'actions.name' → handler(args...).
30
+ * Each handler pre-injects (store, ...deps) so callHandler only passes user args.
31
+ */
32
+ export function resolveActions(actionRegistry, store, ...deps) {
33
+ const handlers = {};
34
+ for (const [key, fn] of Object.entries(actionRegistry)) {
35
+ handlers[`actions.${key}`] = (...args) => fn(store, ...deps, ...args);
36
+ }
37
+ return handlers;
38
+ }
39
+
40
+ /**
41
+ * buildRefs(nodes)
42
+ * Scans normalized view nodes for ref properties.
43
+ * Returns { refName → nodeId } so intent handlers can target view nodes.
44
+ */
45
+ export function buildRefs(nodes) {
46
+ const refs = {};
47
+ for (const [id, node] of Object.entries(nodes)) {
48
+ if (node.ref) refs[node.ref] = id;
49
+ }
50
+ return refs;
51
+ }
@@ -0,0 +1,44 @@
1
+ // ─── Store: namespaced state tree ───────────────────────────────
2
+ // state.* = source of truth (user data)
3
+ // derived.* = auto-computed by subscribers
4
+ // ui.* = ephemeral UI flags
5
+ // intent.* = written by actions, consumed by intent handlers
6
+
7
+ import { createEveryState } from '@everystate/core';
8
+
9
+ export const store = createEveryState({
10
+ state: {
11
+ quotes: [
12
+ { id: 1, author: 'Rumi', text: 'Your heart is the size of an ocean. Go find yourself in its hidden depths.', fav: false },
13
+ { id: 2, author: 'Rumi', text: 'Respond to every call that excites your spirit.', fav: false },
14
+ { id: 3, author: 'Rumi', text: 'Everything in the universe is within you. Ask all from yourself.', fav: false },
15
+ { id: 4, author: 'Rumi', text: 'When I am silent, I have thunder hidden inside.', fav: false },
16
+ { id: 5, author: 'Einstein', text: 'Imagination is more important than knowledge.', fav: false },
17
+ { id: 6, author: 'Einstein', text: 'Life is like riding a bicycle. To keep your balance, you must keep moving.', fav: false },
18
+ { id: 7, author: 'Einstein', text: "If you can't explain it simply, you don't understand it well enough.", fav: false },
19
+ { id: 8, author: 'Mother Teresa', text: 'Spread love everywhere you go.', fav: false },
20
+ { id: 9, author: 'Mother Teresa', text: 'If you judge people, you have no time to love them.', fav: false },
21
+ { id: 10, author: 'Mother Teresa', text: 'Not all of us can do great things. But we can do small things with great love.', fav: false },
22
+ { id: 11, author: 'Muhammad Ali', text: 'Float like a butterfly, sting like a bee.', fav: false },
23
+ { id: 12, author: 'Muhammad Ali', text: "Don't count the days, make the days count.", fav: false },
24
+ { id: 13, author: 'Muhammad Ali', text: 'Service to others is the rent you pay for your room here on earth.', fav: false },
25
+ { id: 14, author: 'Socrates', text: 'The unexamined life is not worth living.', fav: false },
26
+ { id: 15, author: 'Socrates', text: 'I know that I know nothing.', fav: false },
27
+ { id: 16, author: 'Socrates', text: 'Wonder is the beginning of wisdom.', fav: false },
28
+ ],
29
+ authors: ['Rumi', 'Einstein', 'Mother Teresa', 'Muhammad Ali', 'Socrates'],
30
+ selectedAuthor: '',
31
+ searchTerm: '',
32
+ },
33
+ derived: {
34
+ filteredQuotes: [],
35
+ quoteCount: 0,
36
+ favCount: 0,
37
+ stats: '',
38
+ activityLog: [],
39
+ },
40
+ ui: {
41
+ statsVisible: false,
42
+ toggleLabel: '📊 Show Stats',
43
+ },
44
+ });
@@ -0,0 +1,47 @@
1
+ // ─── App: boot sequence ─────────────────────────────────────────
2
+ // Simplified from 004_06:
3
+ // - No actions.js (eliminated)
4
+ // - No resolveActions (eliminated)
5
+ // - Auto-intent Proxy: any 'intent.*' onClick in a component spec
6
+ // auto-writes to the store. Zero handler registration needed.
7
+ // - 6 steps instead of 8
8
+
9
+ import { flatten } from '@everystate/view/resolve';
10
+ import { mount } from '@everystate/view/project';
11
+ import { createPerfMonitor, mountOverlay } from '@everystate/perf';
12
+ import { store } from './store.js';
13
+ import { c } from './components.js';
14
+ import { resolveTree, buildRefs } from './resolve.js';
15
+ import { mountIntents } from './intents.js';
16
+ import { mountDerived } from './derived.js';
17
+ import { mountPolicies } from './policies.js';
18
+
19
+ // 1. Resolve component tree + flatten into store
20
+ const spec = resolveTree(c, 'app');
21
+ const { nodes } = flatten(spec, store, 'view');
22
+ const refs = buildRefs(nodes);
23
+ console.log('[004_07 refs]', refs);
24
+
25
+ // 2. Mount reactive subscribers (order: derived, policies, intents)
26
+ mountDerived(store);
27
+ mountPolicies(store);
28
+ mountIntents(store, refs);
29
+
30
+ // 3. Auto-intent handler: any 'intent.*' onClick writes directly to the store.
31
+ // Eliminates the entire actions.js file + resolveActions ceremony.
32
+ const handlers = new Proxy({}, {
33
+ get(_, name) {
34
+ if (typeof name === 'string' && name.startsWith('intent.')) {
35
+ return (arg, event) => {
36
+ store.set(name, arg instanceof Event ? Date.now() : (arg ?? Date.now()));
37
+ };
38
+ }
39
+ }
40
+ });
41
+
42
+ // 4. Mount DOM
43
+ mount(store, 'view', document.getElementById('app'), handlers);
44
+
45
+ // 5. Perf overlay
46
+ const perf = createPerfMonitor(store);
47
+ mountOverlay(perf, document.body);