jsgui3-server 0.0.147 → 0.0.149

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 (145) hide show
  1. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  2. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
  3. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
  4. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
  5. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
  6. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
  7. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
  8. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
  9. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
  10. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
  11. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
  12. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
  13. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
  14. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
  15. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
  16. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
  17. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
  18. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
  19. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
  20. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
  21. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
  22. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
  23. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
  24. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
  25. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
  26. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
  27. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
  28. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
  29. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
  30. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
  31. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
  32. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
  33. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
  34. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
  35. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
  36. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
  37. package/README.md +85 -3
  38. package/admin-ui/client.js +213 -0
  39. package/admin-ui/server.js +104 -0
  40. package/client/controls/auto-observable.js +207 -0
  41. package/dev-status.svg +139 -0
  42. package/docs/api-reference.md +301 -43
  43. package/docs/books/admin-ui/01-introduction.md +32 -0
  44. package/docs/books/admin-ui/02-architecture.md +92 -0
  45. package/docs/books/admin-ui/03-controls.md +194 -0
  46. package/docs/books/admin-ui/04-implementation-plan.md +62 -0
  47. package/docs/books/admin-ui/README.md +26 -0
  48. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  49. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  50. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  51. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  52. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  53. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  54. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  55. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  56. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  57. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  58. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  59. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  60. package/docs/bundling-system-deep-dive.md +9 -4
  61. package/docs/comprehensive-documentation.md +49 -18
  62. package/docs/configuration-reference.md +152 -27
  63. package/docs/core/README.md +19 -0
  64. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  65. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  66. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  67. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  68. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  69. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  70. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  71. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  72. package/docs/publishers-guide.md +59 -4
  73. package/docs/resources-guide.md +184 -35
  74. package/docs/simple-server-api-design.md +72 -17
  75. package/docs/system-architecture.md +18 -14
  76. package/examples/controls/15) window, observable SSE/server.js +6 -1
  77. package/examples/controls/19) window, auto observable ui/client.js +125 -0
  78. package/examples/controls/19) window, auto observable ui/server.js +73 -0
  79. package/examples/controls/20) window, task manager app/README.md +133 -0
  80. package/examples/controls/20) window, task manager app/client.js +797 -0
  81. package/examples/controls/20) window, task manager app/server.js +178 -0
  82. package/examples/controls/6) window, color_palette/client.js +165 -68
  83. package/examples/controls/9) window, date picker/client.js +362 -76
  84. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  85. package/examples/jsgui3-html/06) theming/client.js +22 -1
  86. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  87. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  88. package/lab/experiments/capture-color-controls.js +196 -0
  89. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  90. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  91. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  92. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  93. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  94. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  95. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  96. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  97. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  98. package/lab/screenshot-utils.js +248 -0
  99. package/module.js +11 -4
  100. package/package.json +14 -4
  101. package/publishers/Publishers.js +4 -3
  102. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  103. package/publishers/http-observable-publisher.js +8 -0
  104. package/publishers/http-sse-publisher.js +341 -0
  105. package/publishers/http-webpage-publisher.js +13 -3
  106. package/publishers/http-webpageorsite-publisher.js +18 -0
  107. package/resources/process-resource.js +950 -0
  108. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
  109. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  110. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  111. package/resources/remote-process-resource.js +355 -0
  112. package/resources/server-resource-pool.js +354 -41
  113. package/serve-factory.js +441 -259
  114. package/server.js +161 -16
  115. package/tests/README.md +66 -4
  116. package/tests/admin-ui-render.test.js +24 -0
  117. package/tests/assigners.test.js +56 -40
  118. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  119. package/tests/configuration-validation.test.js +21 -18
  120. package/tests/content-analysis.test.js +7 -6
  121. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  122. package/tests/control-scan-manifest-regression.test.js +144 -0
  123. package/tests/end-to-end.test.js +15 -14
  124. package/tests/error-handling.test.js +222 -179
  125. package/tests/fixtures/bundling-default-button-client.js +37 -0
  126. package/tests/fixtures/bundling-default-window-client.js +34 -0
  127. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  128. package/tests/fixtures/resource-monitor-client.js +319 -0
  129. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  130. package/tests/http-sse-publisher.test.js +136 -0
  131. package/tests/performance.test.js +69 -65
  132. package/tests/process-resource.test.js +138 -0
  133. package/tests/publishers.test.js +7 -7
  134. package/tests/remote-process-resource.test.js +160 -0
  135. package/tests/sass-controls.e2e.test.js +7 -1
  136. package/tests/serve-resources.test.js +270 -0
  137. package/tests/serve.test.js +120 -50
  138. package/tests/server-resource-pool.test.js +106 -0
  139. package/tests/small-controls-bundle-size.test.js +252 -0
  140. package/tests/test-runner.js +13 -1
  141. package/tests/window-examples.puppeteer.test.js +204 -1
  142. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  143. package/tests/temp_invalid.js +0 -7
  144. package/tests/temp_invalid_utf8.js +0 -1
  145. package/tests/temp_malformed.js +0 -10
@@ -0,0 +1,797 @@
1
+ const jsgui = require('jsgui3-client');
2
+ const { controls, Control, mixins } = jsgui;
3
+ const { Select_Options, Button, Text_Field } = controls;
4
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
5
+
6
+ /**
7
+ * Task Manager Application
8
+ *
9
+ * A multi-window application demonstrating:
10
+ * - Multiple windows with different purposes
11
+ * - Tabbed panels for organization
12
+ * - Interactive controls (text inputs, buttons, selects)
13
+ * - API integration for data persistence
14
+ * - Real-time UI updates
15
+ */
16
+ class Task_Manager_App extends Active_HTML_Document {
17
+ constructor(spec = {}) {
18
+ spec.__type_name = spec.__type_name || 'task_manager_app';
19
+ super(spec);
20
+ const { context } = this;
21
+
22
+ if (typeof this.body.add_class === 'function') {
23
+ this.body.add_class('task-manager-app');
24
+ }
25
+
26
+ // Task data stored directly on the instance
27
+ this._tasks = [];
28
+ this._projects = ['Personal', 'Work', 'Shopping', 'Fitness'];
29
+
30
+ const compose = () => {
31
+ // Main Task Manager Window
32
+ const main_window = new controls.Window({
33
+ context,
34
+ title: 'Task Manager',
35
+ pos: [20, 20]
36
+ });
37
+ main_window.size = [500, 450];
38
+
39
+ // Create tabbed panel for main window - using simple Control for each tab
40
+ const tasks_panel = new Control({ context, size: [480, 350], background: { color: '#f8f9fa' } });
41
+ const projects_panel = new Control({ context, size: [480, 350], background: { color: '#e8f5e9' } });
42
+ const stats_panel = new Control({ context, size: [480, 350], background: { color: '#fff3e0' } });
43
+
44
+ // Build tasks panel content
45
+ this._build_tasks_panel(context, tasks_panel);
46
+
47
+ // Build projects panel content
48
+ this._build_projects_panel(context, projects_panel);
49
+
50
+ // Build stats panel content
51
+ this._build_stats_panel(context, stats_panel);
52
+
53
+ const main_tabs = new controls.Tabbed_Panel({
54
+ context,
55
+ tabs: [
56
+ ['Tasks', tasks_panel],
57
+ ['Projects', projects_panel],
58
+ ['Stats', stats_panel]
59
+ ]
60
+ });
61
+
62
+ main_window.inner.add(main_tabs);
63
+ this.body.add(main_window);
64
+
65
+ // Quick Add Window (smaller floating window)
66
+ const quick_add_window = new controls.Window({
67
+ context,
68
+ title: 'Quick Add Task',
69
+ pos: [540, 20]
70
+ });
71
+ quick_add_window.size = [280, 180];
72
+
73
+ this._build_quick_add_panel(context, quick_add_window.inner);
74
+ this.body.add(quick_add_window);
75
+
76
+ // Store references
77
+ this._ctrl_fields = {
78
+ main_window,
79
+ main_tabs,
80
+ quick_add_window
81
+ };
82
+ };
83
+
84
+ if (!spec.el) {
85
+ compose();
86
+ }
87
+ }
88
+
89
+ _build_tasks_panel(context, panel) {
90
+ // Filter bar
91
+ const filter_bar = new controls.div({ context });
92
+ filter_bar.dom.attributes['class'] = 'filter-bar';
93
+
94
+ const filter_label = new controls.span({ context });
95
+ filter_label.add('Filter by project:');
96
+ filter_bar.add(filter_label);
97
+
98
+ const project_filter = new Select_Options({
99
+ context,
100
+ options: ['All', ...this._projects]
101
+ });
102
+ filter_bar.add(project_filter);
103
+ this._project_filter = project_filter;
104
+
105
+ panel.add(filter_bar);
106
+
107
+ // Action buttons
108
+ const action_bar = new controls.div({ context });
109
+ action_bar.dom.attributes['class'] = 'action-bar';
110
+
111
+ const refresh_btn = new Button({ context, text: 'Refresh' });
112
+ refresh_btn.dom.attributes['id'] = 'refresh-btn';
113
+ action_bar.add(refresh_btn);
114
+ this._refresh_btn = refresh_btn;
115
+
116
+ const clear_completed_btn = new Button({ context, text: 'Clear Completed' });
117
+ clear_completed_btn.dom.attributes['id'] = 'clear-completed-btn';
118
+ action_bar.add(clear_completed_btn);
119
+ this._clear_completed_btn = clear_completed_btn;
120
+
121
+ panel.add(action_bar);
122
+
123
+ // Task list container - will be populated dynamically
124
+ const task_list = new controls.div({ context });
125
+ task_list.dom.attributes['class'] = 'task-list';
126
+ task_list.dom.attributes['id'] = 'task-list';
127
+ this._task_list = task_list;
128
+ panel.add(task_list);
129
+ }
130
+
131
+ _build_projects_panel(context, panel) {
132
+ const title = new controls.h3({ context });
133
+ title.add('Manage Projects');
134
+ panel.add(title);
135
+
136
+ // Project list
137
+ const project_list = new controls.div({ context });
138
+ project_list.dom.attributes['class'] = 'project-list';
139
+
140
+ for (const project of this._projects) {
141
+ const project_item = new controls.div({ context });
142
+ project_item.dom.attributes['class'] = 'project-item';
143
+
144
+ const project_name = new controls.span({ context });
145
+ project_name.add(project);
146
+ project_item.add(project_name);
147
+
148
+ const task_count = new controls.span({ context });
149
+ task_count.dom.attributes['class'] = 'task-count';
150
+ task_count.add('0 tasks');
151
+ project_item.add(task_count);
152
+
153
+ project_list.add(project_item);
154
+ }
155
+
156
+ panel.add(project_list);
157
+ }
158
+
159
+ _build_stats_panel(context, panel) {
160
+ const title = new controls.h3({ context });
161
+ title.add('Statistics');
162
+ panel.add(title);
163
+
164
+ // Stats grid
165
+ const stats_grid = new controls.div({ context });
166
+ stats_grid.dom.attributes['class'] = 'stats-grid';
167
+
168
+ // Total tasks card
169
+ const total_card = new controls.div({ context });
170
+ total_card.dom.attributes['class'] = 'stat-card';
171
+ const total_value = new controls.div({ context });
172
+ total_value.dom.attributes['id'] = 'total-value';
173
+ total_value.add('0');
174
+ total_card.add(total_value);
175
+ const total_label = new controls.div({ context });
176
+ total_label.add('Total Tasks');
177
+ total_card.add(total_label);
178
+ stats_grid.add(total_card);
179
+
180
+ // Completed tasks card
181
+ const completed_card = new controls.div({ context });
182
+ completed_card.dom.attributes['class'] = 'stat-card completed';
183
+ const completed_value = new controls.div({ context });
184
+ completed_value.dom.attributes['id'] = 'completed-value';
185
+ completed_value.add('0');
186
+ completed_card.add(completed_value);
187
+ const completed_label = new controls.div({ context });
188
+ completed_label.add('Completed');
189
+ completed_card.add(completed_label);
190
+ stats_grid.add(completed_card);
191
+
192
+ // Pending tasks card
193
+ const pending_card = new controls.div({ context });
194
+ pending_card.dom.attributes['class'] = 'stat-card pending';
195
+ const pending_value = new controls.div({ context });
196
+ pending_value.dom.attributes['id'] = 'pending-value';
197
+ pending_value.add('0');
198
+ pending_card.add(pending_value);
199
+ const pending_label = new controls.div({ context });
200
+ pending_label.add('Pending');
201
+ pending_card.add(pending_label);
202
+ stats_grid.add(pending_card);
203
+
204
+ // Rate card
205
+ const rate_card = new controls.div({ context });
206
+ rate_card.dom.attributes['class'] = 'stat-card rate';
207
+ const rate_value = new controls.div({ context });
208
+ rate_value.dom.attributes['id'] = 'rate-value';
209
+ rate_value.add('0%');
210
+ rate_card.add(rate_value);
211
+ const rate_label = new controls.div({ context });
212
+ rate_label.add('Completion Rate');
213
+ rate_card.add(rate_label);
214
+ stats_grid.add(rate_card);
215
+
216
+ panel.add(stats_grid);
217
+
218
+ // Progress bar
219
+ const progress_section = new controls.div({ context });
220
+ progress_section.dom.attributes['class'] = 'progress-section';
221
+ const progress_bar = new controls.div({ context });
222
+ progress_bar.dom.attributes['class'] = 'progress-bar';
223
+ const progress_fill = new controls.div({ context });
224
+ progress_fill.dom.attributes['class'] = 'progress-fill';
225
+ progress_fill.dom.attributes['id'] = 'progress-fill';
226
+ progress_bar.add(progress_fill);
227
+ progress_section.add(progress_bar);
228
+ panel.add(progress_section);
229
+ }
230
+
231
+ _build_quick_add_panel(context, panel) {
232
+ // Task text input
233
+ const task_input = new Text_Field({
234
+ context,
235
+ placeholder: 'Enter task description...'
236
+ });
237
+ panel.add(task_input);
238
+ this._task_input = task_input;
239
+
240
+ // Project selector label
241
+ const project_label = new controls.div({ context });
242
+ project_label.add('Project:');
243
+ panel.add(project_label);
244
+
245
+ const project_select = new Select_Options({
246
+ context,
247
+ options: this._projects
248
+ });
249
+ panel.add(project_select);
250
+ this._project_select = project_select;
251
+
252
+ // Add button
253
+ const add_btn = new Button({ context, text: 'Add Task' });
254
+ add_btn.dom.attributes['id'] = 'add-task-btn';
255
+ panel.add(add_btn);
256
+ this._add_task_btn = add_btn;
257
+ }
258
+
259
+ _render_task_list() {
260
+ // Use getElementById since we set the id in compose
261
+ const task_list_el = document.getElementById('task-list');
262
+ if (!task_list_el) return;
263
+
264
+ // Clear existing tasks
265
+ task_list_el.innerHTML = '';
266
+
267
+ const tasks = this._tasks || [];
268
+ const filter = this._current_filter || 'All';
269
+
270
+ const filtered_tasks = filter === 'All'
271
+ ? tasks
272
+ : tasks.filter(t => t.project === filter);
273
+
274
+ if (filtered_tasks.length === 0) {
275
+ const empty_msg = document.createElement('div');
276
+ empty_msg.className = 'empty-message';
277
+ empty_msg.textContent = 'No tasks found. Add one!';
278
+ task_list_el.appendChild(empty_msg);
279
+ return;
280
+ }
281
+
282
+ for (const task of filtered_tasks) {
283
+ const task_el = document.createElement('div');
284
+ task_el.className = 'task-item' + (task.completed ? ' completed' : '');
285
+ task_el.dataset.taskId = task.id;
286
+
287
+ const checkbox = document.createElement('input');
288
+ checkbox.type = 'checkbox';
289
+ checkbox.checked = task.completed;
290
+ checkbox.className = 'task-checkbox';
291
+ checkbox.addEventListener('change', () => this._toggle_task(task.id));
292
+ task_el.appendChild(checkbox);
293
+
294
+ const text = document.createElement('span');
295
+ text.className = 'task-text';
296
+ text.textContent = task.text;
297
+ task_el.appendChild(text);
298
+
299
+ const project_badge = document.createElement('span');
300
+ project_badge.className = 'project-badge';
301
+ project_badge.textContent = task.project;
302
+ task_el.appendChild(project_badge);
303
+
304
+ const delete_btn = document.createElement('button');
305
+ delete_btn.className = 'delete-btn';
306
+ delete_btn.textContent = '×';
307
+ delete_btn.addEventListener('click', () => this._delete_task(task.id));
308
+ task_el.appendChild(delete_btn);
309
+
310
+ task_list_el.appendChild(task_el);
311
+ }
312
+ }
313
+
314
+ _update_stats() {
315
+ const tasks = this._tasks || [];
316
+ const total = tasks.length;
317
+ const completed = tasks.filter(t => t.completed).length;
318
+ const pending = total - completed;
319
+ const rate = total > 0 ? Math.round((completed / total) * 100) : 0;
320
+
321
+ // Update UI using getElementById
322
+ const total_el = document.getElementById('total-value');
323
+ const completed_el = document.getElementById('completed-value');
324
+ const pending_el = document.getElementById('pending-value');
325
+ const rate_el = document.getElementById('rate-value');
326
+ const progress_el = document.getElementById('progress-fill');
327
+
328
+ if (total_el) total_el.textContent = total;
329
+ if (completed_el) completed_el.textContent = completed;
330
+ if (pending_el) pending_el.textContent = pending;
331
+ if (rate_el) rate_el.textContent = rate + '%';
332
+ if (progress_el) progress_el.style.width = rate + '%';
333
+ }
334
+
335
+ async _add_task() {
336
+ // Get text from DOM since control references don't persist through hydration
337
+ // The text input is in the "Quick Add Task" window (second window with class 'inner')
338
+ const input_el = document.querySelector('input[type="text"]');
339
+ // The project select is the second select on the page (first is the filter)
340
+ const selects = document.querySelectorAll('select');
341
+ const select_el = selects.length > 1 ? selects[1] : null;
342
+
343
+ const text = input_el ? input_el.value : '';
344
+ if (!text.trim()) {
345
+ return;
346
+ }
347
+
348
+ const project = select_el ? select_el.value : this._projects[0];
349
+
350
+ try {
351
+ const response = await fetch('/api/tasks/add', {
352
+ method: 'POST',
353
+ headers: { 'Content-Type': 'application/json' },
354
+ body: JSON.stringify({ text: text.trim(), project })
355
+ });
356
+ const result = await response.json();
357
+
358
+ if (result.success) {
359
+ this._tasks = result.tasks;
360
+ this._render_task_list();
361
+ this._update_stats();
362
+
363
+ // Clear input using DOM
364
+ if (input_el) {
365
+ input_el.value = '';
366
+ }
367
+ }
368
+ } catch (err) {
369
+ console.error('Failed to add task:', err);
370
+ }
371
+ }
372
+
373
+ async _toggle_task(id) {
374
+ try {
375
+ const response = await fetch('/api/tasks/toggle', {
376
+ method: 'POST',
377
+ headers: { 'Content-Type': 'application/json' },
378
+ body: JSON.stringify({ id })
379
+ });
380
+ const result = await response.json();
381
+
382
+ if (result.success) {
383
+ this._tasks = result.tasks;
384
+ this._render_task_list();
385
+ this._update_stats();
386
+ }
387
+ } catch (err) {
388
+ console.error('Failed to toggle task:', err);
389
+ }
390
+ }
391
+
392
+ async _delete_task(id) {
393
+ try {
394
+ const response = await fetch('/api/tasks/delete', {
395
+ method: 'POST',
396
+ headers: { 'Content-Type': 'application/json' },
397
+ body: JSON.stringify({ id })
398
+ });
399
+ const result = await response.json();
400
+
401
+ if (result.success) {
402
+ this._tasks = result.tasks;
403
+ this._render_task_list();
404
+ this._update_stats();
405
+ }
406
+ } catch (err) {
407
+ console.error('Failed to delete task:', err);
408
+ }
409
+ }
410
+
411
+ async _load_tasks() {
412
+ try {
413
+ const response = await fetch('/api/tasks/list');
414
+ const result = await response.json();
415
+ this._tasks = result.tasks || [];
416
+ this._render_task_list();
417
+ this._update_stats();
418
+ } catch (err) {
419
+ console.error('Failed to load tasks:', err);
420
+ }
421
+ }
422
+
423
+ async _clear_completed() {
424
+ try {
425
+ const response = await fetch('/api/tasks/clear-completed', { method: 'POST' });
426
+ const result = await response.json();
427
+
428
+ if (result.success) {
429
+ this._tasks = result.tasks;
430
+ this._render_task_list();
431
+ this._update_stats();
432
+ }
433
+ } catch (err) {
434
+ console.error('Failed to clear completed:', err);
435
+ }
436
+ }
437
+
438
+ activate() {
439
+ if (!this.__active) {
440
+ super.activate();
441
+ const { context } = this;
442
+
443
+ // Load initial tasks
444
+ this._load_tasks();
445
+
446
+ // Add task button handler - use getElementById for hydration support
447
+ const add_task_btn = document.getElementById('add-task-btn');
448
+ if (add_task_btn) {
449
+ add_task_btn.addEventListener('click', () => this._add_task());
450
+ }
451
+
452
+ // Refresh button handler
453
+ const refresh_btn = document.getElementById('refresh-btn');
454
+ if (refresh_btn) {
455
+ refresh_btn.addEventListener('click', () => this._load_tasks());
456
+ }
457
+
458
+ // Clear completed button handler
459
+ const clear_completed_btn = document.getElementById('clear-completed-btn');
460
+ if (clear_completed_btn) {
461
+ clear_completed_btn.addEventListener('click', () => this._clear_completed());
462
+ }
463
+
464
+ // Filter change handler
465
+ if (this._project_filter?.data?.model) {
466
+ this._project_filter.data.model.on('change', e => {
467
+ this._current_filter = this._project_filter.data.model.value || 'All';
468
+ this._render_task_list();
469
+ });
470
+ }
471
+
472
+ // Enter key to add task
473
+ if (this._task_input?.dom?.el) {
474
+ const input = this._task_input.dom.el.querySelector('input') || this._task_input.dom.el;
475
+ input.addEventListener('keypress', (e) => {
476
+ if (e.key === 'Enter') this._add_task();
477
+ });
478
+ }
479
+
480
+ // Handle window resize
481
+ context.on('window-resize', e_resize => {});
482
+ }
483
+ }
484
+ }
485
+
486
+ Task_Manager_App.css = `
487
+ * {
488
+ margin: 0;
489
+ padding: 0;
490
+ box-sizing: border-box;
491
+ }
492
+
493
+ body {
494
+ overflow-x: hidden;
495
+ overflow-y: hidden;
496
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
497
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
498
+ }
499
+
500
+ .task-manager-app {
501
+ min-height: 100vh;
502
+ }
503
+
504
+ /* Panel styles */
505
+ .tasks-panel,
506
+ .projects-panel,
507
+ .stats-panel,
508
+ .quick-add-panel {
509
+ padding: 15px;
510
+ height: 100%;
511
+ }
512
+
513
+ /* Filter bar */
514
+ .filter-bar {
515
+ display: flex;
516
+ align-items: center;
517
+ gap: 10px;
518
+ margin-bottom: 15px;
519
+ padding-bottom: 10px;
520
+ border-bottom: 1px solid #ddd;
521
+ }
522
+
523
+ .filter-label {
524
+ font-weight: 500;
525
+ color: #555;
526
+ }
527
+
528
+ /* Task list */
529
+ .task-list {
530
+ max-height: 250px;
531
+ overflow-y: auto;
532
+ margin-bottom: 15px;
533
+ }
534
+
535
+ .task-item {
536
+ display: flex;
537
+ align-items: center;
538
+ gap: 10px;
539
+ padding: 10px;
540
+ background: #f8f9fa;
541
+ border-radius: 6px;
542
+ margin-bottom: 8px;
543
+ transition: all 0.2s ease;
544
+ }
545
+
546
+ .task-item:hover {
547
+ background: #e9ecef;
548
+ }
549
+
550
+ .task-item.completed {
551
+ opacity: 0.6;
552
+ }
553
+
554
+ .task-item.completed .task-text {
555
+ text-decoration: line-through;
556
+ color: #888;
557
+ }
558
+
559
+ .task-checkbox {
560
+ width: 18px;
561
+ height: 18px;
562
+ cursor: pointer;
563
+ }
564
+
565
+ .task-text {
566
+ flex: 1;
567
+ font-size: 14px;
568
+ }
569
+
570
+ .project-badge {
571
+ font-size: 11px;
572
+ padding: 2px 8px;
573
+ background: #667eea;
574
+ color: white;
575
+ border-radius: 12px;
576
+ }
577
+
578
+ .delete-btn {
579
+ width: 24px;
580
+ height: 24px;
581
+ border: none;
582
+ background: #dc3545;
583
+ color: white;
584
+ border-radius: 50%;
585
+ cursor: pointer;
586
+ font-size: 16px;
587
+ line-height: 1;
588
+ opacity: 0;
589
+ transition: opacity 0.2s;
590
+ }
591
+
592
+ .task-item:hover .delete-btn {
593
+ opacity: 1;
594
+ }
595
+
596
+ .empty-message {
597
+ text-align: center;
598
+ color: #888;
599
+ padding: 30px;
600
+ font-style: italic;
601
+ }
602
+
603
+ /* Action bar */
604
+ .action-bar {
605
+ display: flex;
606
+ gap: 10px;
607
+ padding-top: 10px;
608
+ border-top: 1px solid #ddd;
609
+ }
610
+
611
+ /* Buttons */
612
+ .btn {
613
+ padding: 8px 16px;
614
+ border: none;
615
+ border-radius: 6px;
616
+ cursor: pointer;
617
+ font-size: 13px;
618
+ font-weight: 500;
619
+ transition: all 0.2s ease;
620
+ }
621
+
622
+ .btn-primary {
623
+ background: #667eea;
624
+ color: white;
625
+ }
626
+
627
+ .btn-primary:hover {
628
+ background: #5a6fd6;
629
+ }
630
+
631
+ .btn-secondary {
632
+ background: #6c757d;
633
+ color: white;
634
+ }
635
+
636
+ .btn-secondary:hover {
637
+ background: #5a6268;
638
+ }
639
+
640
+ .btn-warning {
641
+ background: #ffc107;
642
+ color: #212529;
643
+ }
644
+
645
+ .btn-warning:hover {
646
+ background: #e0a800;
647
+ }
648
+
649
+ /* Projects panel */
650
+ .projects-panel h3 {
651
+ margin-bottom: 15px;
652
+ color: #333;
653
+ }
654
+
655
+ .project-list {
656
+ margin-bottom: 20px;
657
+ }
658
+
659
+ .project-item {
660
+ display: flex;
661
+ justify-content: space-between;
662
+ align-items: center;
663
+ padding: 12px;
664
+ background: #f8f9fa;
665
+ border-radius: 6px;
666
+ margin-bottom: 8px;
667
+ }
668
+
669
+ .project-name {
670
+ font-weight: 500;
671
+ }
672
+
673
+ .task-count {
674
+ font-size: 12px;
675
+ color: #888;
676
+ }
677
+
678
+ .add-project-section {
679
+ display: flex;
680
+ gap: 10px;
681
+ }
682
+
683
+ .new-project-input {
684
+ flex: 1;
685
+ }
686
+
687
+ /* Stats panel */
688
+ .stats-panel h3 {
689
+ margin-bottom: 20px;
690
+ color: #333;
691
+ }
692
+
693
+ .stats-grid {
694
+ display: grid;
695
+ grid-template-columns: repeat(2, 1fr);
696
+ gap: 15px;
697
+ margin-bottom: 20px;
698
+ }
699
+
700
+ .stat-card {
701
+ background: #f8f9fa;
702
+ padding: 20px;
703
+ border-radius: 10px;
704
+ text-align: center;
705
+ }
706
+
707
+ .stat-card.completed {
708
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
709
+ color: white;
710
+ }
711
+
712
+ .stat-card.pending {
713
+ background: linear-gradient(135deg, #ffc107 0%, #fd7e14 100%);
714
+ color: white;
715
+ }
716
+
717
+ .stat-card.rate {
718
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
719
+ color: white;
720
+ }
721
+
722
+ .stat-value {
723
+ font-size: 32px;
724
+ font-weight: bold;
725
+ margin-bottom: 5px;
726
+ }
727
+
728
+ .stat-label {
729
+ font-size: 12px;
730
+ text-transform: uppercase;
731
+ opacity: 0.8;
732
+ }
733
+
734
+ .progress-section {
735
+ margin-top: 10px;
736
+ }
737
+
738
+ .progress-bar {
739
+ height: 20px;
740
+ background: #e9ecef;
741
+ border-radius: 10px;
742
+ overflow: hidden;
743
+ }
744
+
745
+ .progress-fill {
746
+ height: 100%;
747
+ background: linear-gradient(90deg, #28a745 0%, #20c997 100%);
748
+ width: 0%;
749
+ transition: width 0.5s ease;
750
+ border-radius: 10px;
751
+ }
752
+
753
+ /* Quick add panel */
754
+ .quick-add-panel {
755
+ display: flex;
756
+ flex-direction: column;
757
+ gap: 12px;
758
+ }
759
+
760
+ .task-input {
761
+ width: 100%;
762
+ }
763
+
764
+ .input-label {
765
+ font-size: 12px;
766
+ color: #666;
767
+ margin-bottom: -8px;
768
+ }
769
+
770
+ .add-task-btn {
771
+ width: 100%;
772
+ padding: 12px;
773
+ font-size: 14px;
774
+ }
775
+
776
+ /* Custom scrollbar */
777
+ .task-list::-webkit-scrollbar {
778
+ width: 6px;
779
+ }
780
+
781
+ .task-list::-webkit-scrollbar-track {
782
+ background: #f1f1f1;
783
+ border-radius: 3px;
784
+ }
785
+
786
+ .task-list::-webkit-scrollbar-thumb {
787
+ background: #c1c1c1;
788
+ border-radius: 3px;
789
+ }
790
+
791
+ .task-list::-webkit-scrollbar-thumb:hover {
792
+ background: #a8a8a8;
793
+ }
794
+ `;
795
+
796
+ controls.Task_Manager_App = Task_Manager_App;
797
+ module.exports = jsgui;