ai-design-context 0.4.0

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 (288) hide show
  1. package/AGENTS.md +156 -0
  2. package/CHANGELOG.md +71 -0
  3. package/CODE_OF_CONDUCT.md +29 -0
  4. package/CONTRIBUTING.md +128 -0
  5. package/LICENSE +21 -0
  6. package/README.md +283 -0
  7. package/RELEASE_NOTES_v0.1.0.md +45 -0
  8. package/RELEASE_NOTES_v0.3.0.md +47 -0
  9. package/RELEASE_NOTES_v0.4.0.md +29 -0
  10. package/RELEASE_READINESS.md +107 -0
  11. package/ROADMAP.md +60 -0
  12. package/SECURITY.md +40 -0
  13. package/assets/logo.svg +9 -0
  14. package/benchmarks/README.md +19 -0
  15. package/benchmarks/chat.md +37 -0
  16. package/benchmarks/mobile-home-screen.md +38 -0
  17. package/benchmarks/notes-app.md +37 -0
  18. package/benchmarks/settings.md +37 -0
  19. package/benchmarks/shopping-list.md +37 -0
  20. package/benchmarks/todo-app.md +144 -0
  21. package/checklists/DESIGN_QA.md +102 -0
  22. package/checklists/GENERATED_INDEX.md +7 -0
  23. package/docs/AGENT_CONTEXT.md +111 -0
  24. package/docs/BENCHMARK.md +170 -0
  25. package/docs/DESIGNLINT_READINESS.md +110 -0
  26. package/docs/DESIGN_SYSTEM.md +23 -0
  27. package/docs/EVALUATION_RUBRIC.md +154 -0
  28. package/docs/GITHUB_SETUP.md +28 -0
  29. package/docs/GLOSSARY.md +45 -0
  30. package/docs/INDEX.md +109 -0
  31. package/docs/KNOWLEDGE_ENGINE.md +594 -0
  32. package/docs/MOBILE_FIRST.md +18 -0
  33. package/docs/NPM_RELEASE.md +43 -0
  34. package/docs/PATTERN_SPEC.md +113 -0
  35. package/docs/PHILOSOPHY.md +25 -0
  36. package/docs/PRODUCT_THINKING.md +26 -0
  37. package/docs/STYLE_GUIDE.md +49 -0
  38. package/docs/TRACEABILITY.md +45 -0
  39. package/evidence/README.md +64 -0
  40. package/evidence/TEMPLATE.md +46 -0
  41. package/evidence/chat/.gitkeep +1 -0
  42. package/evidence/mobile-home/.gitkeep +1 -0
  43. package/evidence/notes/.gitkeep +1 -0
  44. package/evidence/settings/.gitkeep +1 -0
  45. package/evidence/shopping/.gitkeep +1 -0
  46. package/evidence/todo/.gitkeep +1 -0
  47. package/evidence/todo/2026-06-25-codex-gpt-5/EVALUATION.md +66 -0
  48. package/evidence/todo/2026-06-25-codex-gpt-5/README.md +40 -0
  49. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/generated-output.md +163 -0
  50. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/metadata.json +38 -0
  51. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/notes.md +20 -0
  52. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/prompt.md +16 -0
  53. package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/scores.md +18 -0
  54. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/generated-output.md +98 -0
  55. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/metadata.json +19 -0
  56. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/notes.md +19 -0
  57. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/prompt.md +9 -0
  58. package/evidence/todo/2026-06-25-codex-gpt-5/baseline/scores.md +18 -0
  59. package/evidence/todo/2026-09-28-codex-paired-todo/EVALUATION.md +70 -0
  60. package/evidence/todo/2026-09-28-codex-paired-todo/README.md +52 -0
  61. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/GENERATION_NOTES.md +133 -0
  62. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/app.js +289 -0
  63. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/context-preserving-preview.json +443 -0
  64. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/daily-home-surface.json +373 -0
  65. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/quick-capture.json +301 -0
  66. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/sources-read.txt +36 -0
  67. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/generated-output.md +11 -0
  68. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/index.html +64 -0
  69. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/launch-prompt.md +1 -0
  70. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/metadata.json +55 -0
  71. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/notes.md +15 -0
  72. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/parent-status-message.md +1 -0
  73. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/prompt.md +49 -0
  74. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/runtime-checks.json +776 -0
  75. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/scores.md +20 -0
  76. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-completed.png +0 -0
  77. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-default.png +0 -0
  78. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail-reduced-motion.png +0 -0
  79. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail.png +0 -0
  80. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-empty.png +0 -0
  81. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-keyboard-focus.png +0 -0
  82. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-save-error.png +0 -0
  83. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saved.png +0 -0
  84. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saving.png +0 -0
  85. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-validation.png +0 -0
  86. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-completed.png +0 -0
  87. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-default.png +0 -0
  88. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail-reduced-motion.png +0 -0
  89. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail.png +0 -0
  90. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-empty.png +0 -0
  91. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-keyboard-focus.png +0 -0
  92. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-save-error.png +0 -0
  93. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saved.png +0 -0
  94. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saving.png +0 -0
  95. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-validation.png +0 -0
  96. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/source-hashes.json +5 -0
  97. package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/styles.css +122 -0
  98. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/GENERATION_NOTES.md +86 -0
  99. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/app.js +303 -0
  100. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/generated-output.md +11 -0
  101. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/index.html +71 -0
  102. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/launch-prompt.md +1 -0
  103. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/metadata.json +55 -0
  104. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/notes.md +15 -0
  105. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/parent-status-message.md +1 -0
  106. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/prompt.md +49 -0
  107. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/runtime-checks.json +772 -0
  108. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/scores.md +20 -0
  109. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-completed.png +0 -0
  110. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-default.png +0 -0
  111. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail-reduced-motion.png +0 -0
  112. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail.png +0 -0
  113. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-empty.png +0 -0
  114. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-keyboard-focus.png +0 -0
  115. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-save-error.png +0 -0
  116. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saved.png +0 -0
  117. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saving.png +0 -0
  118. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-validation.png +0 -0
  119. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-completed.png +0 -0
  120. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-default.png +0 -0
  121. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail-reduced-motion.png +0 -0
  122. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail.png +0 -0
  123. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-empty.png +0 -0
  124. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-keyboard-focus.png +0 -0
  125. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-save-error.png +0 -0
  126. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saved.png +0 -0
  127. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saving.png +0 -0
  128. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-validation.png +0 -0
  129. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/source-hashes.json +5 -0
  130. package/evidence/todo/2026-09-28-codex-paired-todo/baseline/styles.css +9 -0
  131. package/evidence/todo/2026-09-28-codex-paired-todo/capture-ai-design-rules.js +60 -0
  132. package/evidence/todo/2026-09-28-codex-paired-todo/capture-baseline.js +60 -0
  133. package/evidence/todo/2026-09-28-codex-paired-todo/common/brief.md +35 -0
  134. package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge-files.json +127 -0
  135. package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge.patch +890 -0
  136. package/evidence/todo/2026-09-28-codex-paired-todo/common/review-context.json +550 -0
  137. package/evidence/todo/2026-09-28-codex-paired-todo/common/seed.json +86 -0
  138. package/evidence/todo/2026-09-28-codex-paired-todo/common/setup.json +47 -0
  139. package/evidence/todo/2026-09-28-codex-paired-todo/common/technical-envelope.md +11 -0
  140. package/evidence/todo/2026-09-28-codex-paired-todo/layout-metrics.json +133 -0
  141. package/evidence/todo/2026-09-28-codex-paired-todo/layout-probe.js +15 -0
  142. package/examples/GENERATED_INDEX.md +8 -0
  143. package/examples/INDEX.md +16 -0
  144. package/examples/README.md +15 -0
  145. package/examples/consequential-action-confirmation-reference.md +83 -0
  146. package/examples/todo-app-benchmark.md +117 -0
  147. package/examples/todo-reference/README.md +29 -0
  148. package/examples/todo-reference/app.js +154 -0
  149. package/examples/todo-reference/favicon.svg +4 -0
  150. package/examples/todo-reference/index.html +67 -0
  151. package/examples/todo-reference/review-evidence/2026-07-15/desktop-default.png +0 -0
  152. package/examples/todo-reference/review-evidence/2026-07-15/desktop-detail.png +0 -0
  153. package/examples/todo-reference/review-evidence/2026-07-15/mobile-detail.png +0 -0
  154. package/examples/todo-reference/review-evidence/2026-07-15/mobile-error.png +0 -0
  155. package/examples/todo-reference/styles.css +241 -0
  156. package/graph/GENERATED_GRAPH.md +71 -0
  157. package/observations/README.md +23 -0
  158. package/observations/accessibility/reduced-interaction-motion.md +49 -0
  159. package/observations/accessibility/textual-error-recovery.md +50 -0
  160. package/observations/accessibility/visible-keyboard-focus.md +49 -0
  161. package/observations/ai/agent-consequential-action-controls.md +56 -0
  162. package/observations/ai/agent-work-visibility-and-intervention.md +51 -0
  163. package/observations/ai/figma-agent-context-and-skills.md +52 -0
  164. package/observations/finance/high-risk-wallet-recovery-flows.md +55 -0
  165. package/observations/finance/market-interface-adaptive-trading-surfaces.md +55 -0
  166. package/observations/interaction/chrome-scoped-view-transitions.md +52 -0
  167. package/observations/interaction/figma-motion-as-system-capability.md +53 -0
  168. package/observations/mobile/mobile-bottom-sheet-choice-contracts.md +55 -0
  169. package/observations/performance/reserved-loading-space.md +51 -0
  170. package/observations/product-repos/pinned-ai-design-rules-integration.md +49 -0
  171. package/observations/visual/apple-liquid-glass-functional-layer.md +52 -0
  172. package/observations/visual/chrome-content-first-adaptive-ui.md +51 -0
  173. package/observations/visual/material-3-expressive-system.md +52 -0
  174. package/package.json +65 -0
  175. package/patterns/GENERATED_INDEX.md +13 -0
  176. package/patterns/INDEX.md +41 -0
  177. package/patterns/README.md +5 -0
  178. package/patterns/consequential-action-confirmation.md +127 -0
  179. package/patterns/context-preserving-preview.md +145 -0
  180. package/patterns/daily-home-surface.md +134 -0
  181. package/patterns/mobile-primary-action.md +110 -0
  182. package/patterns/object-status-list.md +114 -0
  183. package/patterns/progressive-detail.md +113 -0
  184. package/patterns/quick-capture.md +132 -0
  185. package/prompts/CONSEQUENTIAL_ACTION_REVIEW.md +100 -0
  186. package/prompts/DESIGN_EVIDENCE_REVIEW.md +49 -0
  187. package/prompts/GENERATED_INDEX.md +11 -0
  188. package/prompts/INDEX.md +24 -0
  189. package/prompts/PROTOTYPE_REVIEW.md +189 -0
  190. package/prompts/QUICK_CAPTURE_STATE_REVIEW.md +74 -0
  191. package/prompts/TODO_APP_BENCHMARK.md +88 -0
  192. package/registry/objects.json +568 -0
  193. package/registry/relationships.json +972 -0
  194. package/research/GENERATED_INDEX.md +18 -0
  195. package/research/INDEX.md +40 -0
  196. package/research/accessibility/keyboard-focus-and-interaction-motion.md +62 -0
  197. package/research/accessibility/textual-error-recovery.md +57 -0
  198. package/research/performance/reserved-loading-space.md +56 -0
  199. package/research/products/apple-reminders.md +59 -0
  200. package/research/products/arc.md +61 -0
  201. package/research/products/linear.md +62 -0
  202. package/research/products/telegram.md +61 -0
  203. package/research/products/things-3.md +60 -0
  204. package/research/ux/contextual-agentic-interface.md +68 -0
  205. package/research/ux/high-risk-operational-flows.md +89 -0
  206. package/research/ux/motion-as-state-continuity.md +71 -0
  207. package/research/visual/expressive-system-ui-2026.md +75 -0
  208. package/reviews/GENERATED_INDEX.md +9 -0
  209. package/reviews/README.md +25 -0
  210. package/reviews/consequential-action-confirmation-review.md +92 -0
  211. package/reviews/todo-app-benchmark-reference-review.md +86 -0
  212. package/reviews/todo-reference-fixture-review.md +77 -0
  213. package/rules/GENERATED_INDEX.md +23 -0
  214. package/rules/INDEX.md +37 -0
  215. package/rules/accessibility/A11Y-001.md +58 -0
  216. package/rules/accessibility/A11Y-002.md +55 -0
  217. package/rules/accessibility/A11Y-003.md +54 -0
  218. package/rules/accessibility/A11Y-004.md +54 -0
  219. package/rules/ia/IA-001.md +60 -0
  220. package/rules/ia/IA-002.md +61 -0
  221. package/rules/performance/PERF-001.md +55 -0
  222. package/rules/product/PRD-001.md +61 -0
  223. package/rules/product/PRD-002.md +61 -0
  224. package/rules/ux/UX-001.md +58 -0
  225. package/rules/ux/UX-002.md +61 -0
  226. package/rules/ux/UX-003.md +60 -0
  227. package/rules/ux/UX-004.md +61 -0
  228. package/rules/ux/UX-005.md +62 -0
  229. package/rules/ux/UX-006.md +69 -0
  230. package/rules/visual/VIS-001.md +59 -0
  231. package/rules/visual/VIS-002.md +59 -0
  232. package/schema/checklist.schema.json +21 -0
  233. package/schema/common.schema.json +178 -0
  234. package/schema/observation.schema.json +21 -0
  235. package/schema/pattern.schema.json +21 -0
  236. package/schema/prompt.schema.json +21 -0
  237. package/schema/reference-project.schema.json +21 -0
  238. package/schema/research.schema.json +21 -0
  239. package/schema/review.schema.json +21 -0
  240. package/schema/rule.schema.json +21 -0
  241. package/skills/README.md +48 -0
  242. package/skills/accessibility-reviewer/SKILL.md +101 -0
  243. package/skills/agent-context/SKILL.md +34 -0
  244. package/skills/agent-context/agents/openai.yaml +4 -0
  245. package/skills/design-evidence-researcher/SKILL.md +47 -0
  246. package/skills/design-evidence-researcher/agents/openai.yaml +4 -0
  247. package/skills/design-reviewer/SKILL.md +113 -0
  248. package/skills/design-system-architect/SKILL.md +101 -0
  249. package/skills/information-architect/SKILL.md +97 -0
  250. package/skills/interaction-designer/SKILL.md +104 -0
  251. package/skills/knowledge-graph-architect/SKILL.md +96 -0
  252. package/skills/mobile-ux-expert/SKILL.md +105 -0
  253. package/skills/motion-designer/SKILL.md +100 -0
  254. package/skills/performance-reviewer/SKILL.md +97 -0
  255. package/skills/product-designer/SKILL.md +104 -0
  256. package/skills/prompt-architect/SKILL.md +105 -0
  257. package/skills/reference-driven-design/SKILL.md +119 -0
  258. package/skills/ux-reviewer/SKILL.md +99 -0
  259. package/skills/visual-designer/SKILL.md +107 -0
  260. package/starter-kit/AGENTS.md +37 -0
  261. package/starter-kit/BOOTSTRAP.md +13 -0
  262. package/starter-kit/INSTALL_WITH_AGENT.md +133 -0
  263. package/starter-kit/PROJECT_INTEGRATION.md +60 -0
  264. package/starter-kit/README.md +41 -0
  265. package/starter-kit/benchmarks/BENCHMARK_CHECKLIST.md +10 -0
  266. package/starter-kit/docs/DESIGN_DECISIONS.md +22 -0
  267. package/starter-kit/docs/INFORMATION_ARCHITECTURE.md +25 -0
  268. package/starter-kit/docs/PERSONAS.md +14 -0
  269. package/starter-kit/docs/PRD.md +32 -0
  270. package/starter-kit/docs/USER_FLOWS.md +19 -0
  271. package/starter-kit/reviews/DESIGN_REVIEW.md +49 -0
  272. package/starter-kit/templates/FEATURE_TEMPLATE.md +36 -0
  273. package/starter-kit/templates/TASK_TEMPLATE.md +22 -0
  274. package/templates/CHECKLIST_TEMPLATE.md +43 -0
  275. package/templates/OBSERVATION_TEMPLATE.md +41 -0
  276. package/templates/PATTERN_TEMPLATE.md +100 -0
  277. package/templates/PROMPT_TEMPLATE.md +46 -0
  278. package/templates/PROTOTYPE_REVIEW.md +50 -0
  279. package/templates/REFERENCE_PROJECT_TEMPLATE.md +49 -0
  280. package/templates/REVIEW_TEMPLATE.md +55 -0
  281. package/templates/RULE_TEMPLATE.md +57 -0
  282. package/tools/cli.mjs +46 -0
  283. package/tools/context.mjs +342 -0
  284. package/tools/designlint.mjs +77 -0
  285. package/tools/generate-indexes.mjs +621 -0
  286. package/tools/init.mjs +99 -0
  287. package/tools/validate-benchmark-evidence.mjs +248 -0
  288. package/tools/validate-knowledge.mjs +481 -0
@@ -0,0 +1,20 @@
1
+ # ai-design-rules scores
2
+
3
+ Evaluator: Codex parent agent, internal and unblinded. See [the full evaluation](../EVALUATION.md) for category evidence and limitations.
4
+
5
+ | Category | Score |
6
+ | --- | ---: |
7
+ | Product Thinking | 8 |
8
+ | UX | 7 |
9
+ | Information Architecture | 8 |
10
+ | Navigation | 8 |
11
+ | Accessibility | 7 |
12
+ | Mobile-first | 8 |
13
+ | Visual Hierarchy | 8 |
14
+ | State Design | 8 |
15
+ | Consistency | 8 |
16
+ | Performance Awareness | 7 |
17
+ | Simplicity | 8 |
18
+ | Overall Product Quality | 7 |
19
+
20
+ Total: 92/120. Average: 7.67/10. These are subjective rubric ratings for one pair, not a statistical quality estimate.
@@ -0,0 +1,5 @@
1
+ {
2
+ "index.html": "91b9e63ca59048184ef449622e60cb8098a66952177a15aad8b1d939e7fde1a6",
3
+ "styles.css": "2f6f7bd55b33d3e440cdbea22697753d8bc064584dbcdff85c4e5c263be03481",
4
+ "app.js": "7aabff88e038b7bb0bb7da83a0413cfbfbea6047a4a60a5629a0032e1adb917a"
5
+ }
@@ -0,0 +1,122 @@
1
+ :root {
2
+ color-scheme: light;
3
+ --surface-page: #f7f8f4; --surface: #ffffff; --surface-soft: #eef1e9;
4
+ --text: #25372f; --text-muted: #657068; --line: #dce2d9;
5
+ --accent: #276149; --accent-hover: #194c36; --accent-soft: #e3eddf;
6
+ --success: #276149; --danger: #a52e26; --danger-soft: #fff0ed;
7
+ --focus: #926315; --overlay: #172d304a;
8
+ --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 24px; --space-xl: 32px; --space-2xl: 48px;
9
+ --radius: 14px; --radius-small: 8px; --font-body: 16px; --font-small: 13px;
10
+ }
11
+ * { box-sizing: border-box; }
12
+ html { scrollbar-gutter: stable; }
13
+ body { margin: 0; background: var(--surface-page); color: var(--text); font: var(--font-body)/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
14
+ button, input, textarea { font: inherit; }
15
+ button { cursor: pointer; color: inherit; border: 0; background: none; min-height: 44px; }
16
+ button:disabled { cursor: wait; opacity: .6; }
17
+ :focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
18
+ [hidden] { display: none !important; }
19
+ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
20
+ .skip { position: fixed; z-index: 10; top: -70px; left: 16px; background: var(--surface); color: var(--accent); padding: var(--space-md); }
21
+ .skip:focus { top: 8px; }
22
+ .layout { max-width: 1120px; margin: auto; display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: 64px; padding: 0 var(--space-xl); }
23
+ .sidebar { padding-top: 53px; position: sticky; top: 0; height: 100vh; }
24
+ .brand { display: flex; align-items: center; font-size: 24px; font-weight: 750; letter-spacing: -1.2px; }
25
+ .brand-mark { display: inline-grid; place-items: center; background: var(--accent-soft); border-radius: 50%; width: 36px; height: 36px; margin-right: 10px; font-size: 28px; color: var(--accent); }
26
+ .brand-dot { color: var(--accent); }
27
+ nav { margin-top: 52px; display: grid; gap: var(--space-sm); }
28
+ .nav-button { display: flex; align-items: center; gap: 12px; width: 100%; border-radius: var(--radius-small); padding: 10px 14px; text-align: left; color: var(--text-muted); }
29
+ .nav-button > span:first-child { font-size: 23px; width: 24px; }
30
+ .nav-button.active { background: var(--accent-soft); color: var(--accent); font-weight: 650; }
31
+ .nav-count { margin-left: auto; font-size: var(--font-small); font-variant-numeric: tabular-nums; }
32
+ .sidebar-note { position: absolute; bottom: 40px; color: var(--text-muted); font-size: var(--font-small); line-height: 1.7; }
33
+ main { min-width: 0; padding-top: 64px; }
34
+ .eyebrow { font-size: 11px; font-weight: 700; letter-spacing: 1.6px; margin: 0 0 var(--space-sm); color: var(--text-muted); }
35
+ h1 { font-size: 48px; letter-spacing: -2px; line-height: 1.15; margin: 0; font-weight: 650; display: flex; align-items: center; gap: 18px; }
36
+ .sun { color: var(--accent); font-size: 32px; font-weight: 400; }
37
+ .summary { margin: 12px 0 36px; color: var(--text-muted); font-size: 15px; min-height: 23px; }
38
+ .section-label { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--line); padding-bottom: 12px; color: var(--text-muted); font-size: var(--font-small); }
39
+ .section-label h2 { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; margin: 0; }
40
+ .task-list { margin: 0; padding: 0; list-style: none; }
41
+ .task-row { display: flex; align-items: center; min-height: 60px; border-bottom: 1px solid var(--line); }
42
+ .task-row:hover { background: var(--surface-soft); }
43
+ .check { display: grid; place-items: center; flex-shrink: 0; width: 48px; height: 52px; padding: 0; }
44
+ .check-circle { border: 1.5px solid #88958a; border-radius: 50%; width: 21px; height: 21px; display: grid; place-items: center; font-size: 13px; }
45
+ .check[aria-checked="true"] .check-circle { border-color: var(--accent); color: var(--surface); background: var(--accent); }
46
+ .task-open { flex: 1; min-width: 0; display: flex; align-items: center; text-align: left; gap: var(--space-sm); padding: 12px 6px 12px 2px; min-height: 59px; line-height: 1.4; }
47
+ .task-text { min-width: 0; flex: 1; overflow-wrap: anywhere; }
48
+ .task-title { display: block; }
49
+ .task-meta { display: block; margin-top: 2px; font-size: 12px; color: var(--text-muted); }
50
+ .row-arrow { color: var(--text-muted); width: 24px; text-align: center; font-size: 20px; }
51
+ .is-completed .task-title { text-decoration: line-through; color: var(--text-muted); }
52
+ .completed-section { margin-top: var(--space-lg); }
53
+ .completed-toggle { display: flex; align-items: center; gap: var(--space-sm); color: var(--text-muted); padding: 0 10px; font-size: var(--font-small); }
54
+ .completed-toggle span:last-child { margin-left: var(--space-xs); }
55
+ .completed-list .task-row { border-bottom: 0; }
56
+ .capture-space { height: 180px; }
57
+ .capture-dock { position: fixed; bottom: 0; left: max(320px, calc(50% - 240px)); width: min(768px, calc(100% - 352px)); padding: 0 0 max(16px, env(safe-area-inset-bottom)); background: var(--surface-page); z-index: 2; }
58
+ .feedback { display: flex; align-items: center; min-height: 46px; gap: var(--space-sm); font-size: var(--font-small); color: var(--text-muted); padding: 0 var(--space-sm); }
59
+ .feedback.error { color: var(--danger); }
60
+ .feedback.success { color: var(--success); }
61
+ .retry { flex-shrink: 0; color: var(--danger); font-weight: 650; text-decoration: underline; min-width: 52px; }
62
+ .capture { display: flex; align-items: center; gap: var(--space-sm); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-sm); background: var(--surface); box-shadow: 0 4px 20px #25372f09; }
63
+ .capture:focus-within { border-color: var(--accent); }
64
+ .capture-plus { color: var(--accent); font-size: 26px; margin-left: 8px; }
65
+ .capture input { flex: 1; min-width: 0; padding: 10px 0; border: 0; background: transparent; color: var(--text); height: 44px; }
66
+ input::placeholder, textarea::placeholder { color: var(--text-muted); opacity: 1; }
67
+ .primary { background: var(--accent); color: var(--surface); border-radius: 10px; padding: 10px 19px; font-weight: 600; white-space: nowrap; }
68
+ .primary:hover:not(:disabled) { background: var(--accent-hover); }
69
+ .primary span { margin-left: 5px; opacity: .75; }
70
+ .capture-hint { font-size: 11px; color: var(--text-muted); text-align: center; margin: 8px 0 0; }
71
+ .empty { text-align: center; padding: 52px var(--space-md); }
72
+ .empty-mark { display: inline-grid; place-items: center; width: 52px; height: 52px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); font-size: 26px; }
73
+ .empty h2 { font-size: 21px; letter-spacing: -.5px; margin: 18px 0 8px; }
74
+ .empty p { color: var(--text-muted); font-size: 15px; margin: 0; }
75
+ .text-button { font-weight: 650; color: var(--accent); margin-top: var(--space-md); padding: 0 14px; }
76
+ dialog { color: var(--text); border: 1px solid var(--line); background: var(--surface); padding: var(--space-xl); width: 420px; max-width: calc(100% - 32px); border-radius: 20px; max-height: calc(100dvh - 48px); overflow: auto; margin: auto 24px auto auto; box-shadow: 0 20px 90px #172d3030; }
77
+ dialog::backdrop { background: var(--overlay); }
78
+ .detail-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-lg); }
79
+ .detail-head .eyebrow { margin: 0; }
80
+ .icon-button { min-width: 44px; font-size: 30px; line-height: 1; border-radius: var(--radius-small); }
81
+ .icon-button:hover { background: var(--surface-soft); }
82
+ #detail-form { display: flex; flex-direction: column; }
83
+ #detail-form label { font-size: var(--font-small); font-weight: 650; margin-bottom: var(--space-sm); }
84
+ .optional { color: var(--text-muted); font-weight: 400; margin-left: var(--space-sm); }
85
+ #detail-form input, #detail-form textarea { width: 100%; background: var(--surface-page); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius-small); padding: 12px; margin-bottom: var(--space-lg); min-height: 48px; }
86
+ #detail-form textarea { resize: vertical; }
87
+ #detail-title { font-size: 21px; font-weight: 550; line-height: 1.4; }
88
+ .detail-feedback { min-height: 48px; margin: 0 0 var(--space-sm); color: var(--text-muted); font-size: var(--font-small); }
89
+ .detail-feedback.error { color: var(--danger); }
90
+ .detail-feedback.success { color: var(--success); }
91
+ .detail-actions { display: flex; justify-content: space-between; gap: var(--space-md); }
92
+ .secondary { border: 1px solid var(--line); border-radius: 10px; padding: 10px 20px; }
93
+ .skeleton { height: 14px; border-radius: var(--space-xs); background: var(--line); width: 60%; margin-left: var(--space-md); }
94
+ .task-row[aria-busy="true"] { opacity: .65; }
95
+ @media (max-width: 760px) {
96
+ .layout { display: block; padding: 0 20px; }
97
+ .sidebar { position: static; height: auto; padding-top: 20px; }
98
+ .brand { font-size: 21px; }
99
+ .brand-mark { width: 30px; height: 30px; font-size: 24px; }
100
+ nav { margin-top: 22px; display: flex; gap: 8px; }
101
+ .nav-button { width: auto; padding: 7px 12px; font-size: 14px; gap: 8px; }
102
+ .nav-button > span:first-child { font-size: 20px; width: 20px; }
103
+ .nav-count { margin-left: 5px; }
104
+ .sidebar-note { display: none; }
105
+ main { padding-top: 28px; }
106
+ h1 { font-size: 40px; }
107
+ .summary { margin: 10px 0 25px; font-size: 14px; }
108
+ .task-row { min-height: 60px; }
109
+ .task-open { font-size: 15px; }
110
+ .check { width: 44px; }
111
+ .capture-dock { left: 0; width: 100%; padding: 0 16px max(12px, env(safe-area-inset-bottom)); }
112
+ .capture-plus { margin-left: 3px; }
113
+ .primary { padding: 10px 16px; }
114
+ .capture .primary span { display: none; }
115
+ .capture input { font-size: 16px; }
116
+ .feedback { font-size: 12px; min-height: 46px; }
117
+ dialog { margin: auto 0 0; border-radius: 22px 22px 0 0; width: 100%; max-width: 100%; padding: 24px; max-height: 90dvh; }
118
+ .detail-head { margin-bottom: 16px; }
119
+ #detail-form input, #detail-form textarea { margin-bottom: 18px; }
120
+ .capture-space { height: 160px; }
121
+ }
122
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; } }
@@ -0,0 +1,86 @@
1
+ # Baseline generation notes
2
+
3
+ ## Scope and files
4
+
5
+ Fresh static consumer todo app named Daylight. Implementation files are `index.html`, `styles.css`, and `app.js`. No dependencies, build step, external assets, network calls, repository code, or browser tools were used.
6
+
7
+ Product behavior:
8
+
9
+ - Today shows incomplete tasks dated September 28, 2026 or earlier; older dates receive an overdue label.
10
+ - All tasks includes undated and future tasks as well.
11
+ - The capture field adds a task to the fixed benchmark date. Enter or Add submits it.
12
+ - Checkboxes complete or reopen tasks. Completed tasks are in an expandable section.
13
+ - Clicking a task title opens an accessible native dialog with title, optional date, and optional notes. Cancel and Escape abandon the current detail draft; save commits it.
14
+ - Saves wait for 500 ms by default. Controls for mutations are disabled while a save is pending. Success is reported in the inline status area.
15
+ - On failed save, persisted tasks are unchanged. The pending operation is retained for the inline Try again button. Add text and the open edit form remain available; edits can be corrected and resubmitted.
16
+ - Data persists in localStorage under `daylight-baseline-v1` on the current origin.
17
+
18
+ ## Local serving
19
+
20
+ From this output directory run:
21
+
22
+ ```sh
23
+ python3 -m http.server 8000 --bind 127.0.0.1
24
+ ```
25
+
26
+ Open `http://127.0.0.1:8000/`. The generator did not run a server or render the output. No network is needed by the application.
27
+
28
+ ## Evaluator hooks and exact reproduction
29
+
30
+ Hooks are only exposed on `window.__benchmark`; they have no product UI.
31
+
32
+ ```js
33
+ window.__benchmark.reset('seed');
34
+ window.__benchmark.reset('empty');
35
+ window.__benchmark.setSaveDelay(5000);
36
+ window.__benchmark.failNextSave();
37
+ window.__benchmark.getState();
38
+ ```
39
+
40
+ `reset` replaces local tasks, selects Today, collapses Completed, closes details, clears the capture field and status, cancels any in-flight completion, resets the delay to 500 ms, and clears pending injected failure. It renders immediately and returns a serializable state. The default reset mode is `seed`. `getState()` returns task data, fixed date, view, initial loading flag, pending and failed operations, feedback text/type, editing ID, and save delay.
41
+
42
+ Reproduction sequences:
43
+
44
+ 1. **Seed/default:** `reset('seed')`. Exact supplied 12 tasks, including one completed task, are restored. Today initially contains 11 incomplete items.
45
+ 2. **Empty:** `reset('empty')`. The empty state and capture field are visible. Enter a task and submit to leave the empty state.
46
+ 3. **Initial loading:** reload the document. The app shows a skeleton list and Loading badge for the first 500 ms. The previously saved data is rendered afterward. Calling reset during loading cancels that initial timer's effect.
47
+ 4. **Pending save:** `reset('seed'); setSaveDelay(5000)` using the methods above. Add a task or toggle a checkbox. For five seconds the status reports saving and mutation controls are disabled. Task data changes only after success.
48
+ 5. **Failed capture:** reset, invoke `failNextSave()`, type `Buy apples` and submit. After the delay, an inline failure with Try again appears. The title remains in the capture field; tasks remain unchanged. Click Try again without invoking the failure hook again. The task is added once and success is reported.
49
+ 6. **Failed completion:** reset, invoke `failNextSave()`, then check Book dentist appointment. After failure, the task stays incomplete. Try again completes it and makes it available under Completed.
50
+ 7. **Failed edit:** reset, open Book dentist appointment, alter notes, invoke `failNextSave()`, and Save changes. The dialog stays open with its draft and failure message. Try saving again retries the current form data. Successful save closes the dialog.
51
+ 8. **Future/undated details:** change a task date to September 29, 2026 or clear it and save. It leaves Today, remains in All tasks, and the success message identifies where to find it.
52
+ 9. **Persistence:** save a change and reload. Stored tasks remain on the same origin.
53
+
54
+ `failNextSave()` affects exactly the next started save, once. Subsequent retries succeed unless browser storage itself is unavailable. A call while a save is already pending affects the following operation. `setSaveDelay` affects later started saves. reset invalidates an outstanding timer so it cannot overwrite newly reset data.
55
+
56
+ ## Checks actually run
57
+
58
+ - `node --check app.js` — passed JavaScript syntax checking.
59
+ - Python HTML parsing — 31 unique element IDs, valid explicit label targets, and all 28 statically referenced JS element IDs present.
60
+ - Static CSS brace balance — passed. This is not a full CSS parser or visual check.
61
+ - Static HTML asset check — no external resource URLs.
62
+ - Node comparison of the embedded seed array against the provided `common/seed.json` — all 12 tasks exactly match every supplied field.
63
+ - `git status --short` in the owned output directory — not applicable; the temporary output directory is not a Git repository.
64
+
65
+ No browser, rendered visual review, viewport verification, keyboard trial, assistive technology trial, or browser interaction tests were performed. These remain for the parent evaluator.
66
+
67
+ ## Known limitations
68
+
69
+ - Storage is local to the browser origin; there is no backend or synchronization.
70
+ - A browser blocking localStorage writes will produce save failures. There is no separate fallback persistence mechanism.
71
+ - Today is deliberately fixed at September 28, 2026 for the benchmark; it does not follow the real calendar.
72
+ - No task deletion, task reordering, recurring tasks, or notifications were included in this brief.
73
+ - Initial loading lasts a fixed 500 ms; the delay hook controls saves, not initial loading.
74
+ - The UI was authored for the target widths but was not rendered or measured by the generator.
75
+
76
+ ## Actual sources read
77
+
78
+ 1. `${RUN_ROOT}/baseline/prompt.md`
79
+ 2. `${RUN_ROOT}/common/seed.json`
80
+ 3. The generator's own newly authored implementation, solely for static checks.
81
+
82
+ No AI Design Rules files, design skills, repository examples, sibling output, external references, or network sources were read.
83
+
84
+ ## Timing
85
+
86
+ Started: 2026-09-28 05:37:31 UTC (first prompt read). Completed and frozen: 2026-09-28 05:44:40 UTC. Actual elapsed time: approximately 7 minutes 9 seconds, within the authorized ten-minute window.
@@ -0,0 +1,303 @@
1
+ 'use strict';
2
+
3
+ (() => {
4
+ const TODAY = '2026-09-28';
5
+ const STORAGE_KEY = 'daylight-baseline-v1';
6
+ const SEED = [
7
+ { id: 't01', title: 'Book dentist appointment', notes: 'Ask about a morning appointment.', dueDate: TODAY, completed: false },
8
+ { id: 't02', title: 'Pick up parcel', notes: '', dueDate: TODAY, completed: false },
9
+ { id: 't03', title: 'Reply to Maya', notes: '', dueDate: TODAY, completed: false },
10
+ { id: 't04', title: 'Pay electricity bill', notes: '', dueDate: TODAY, completed: false },
11
+ { id: 't05', title: 'Fix bicycle light', notes: '', dueDate: TODAY, completed: false },
12
+ { id: 't06', title: 'Water the plants', notes: '', dueDate: TODAY, completed: false },
13
+ { id: 't07', title: 'Read chapter twelve', notes: '', dueDate: TODAY, completed: false },
14
+ { id: 't08', title: 'Plan weekend walk', notes: '', dueDate: TODAY, completed: false },
15
+ { id: 't09', title: 'Return library book', notes: '', dueDate: TODAY, completed: false },
16
+ { id: 't10', title: 'Take out recycling', notes: '', dueDate: TODAY, completed: false },
17
+ { id: 't11', title: 'Order coffee beans', notes: '', dueDate: TODAY, completed: false },
18
+ { id: 't12', title: 'Stretch for ten minutes', notes: '', dueDate: TODAY, completed: true }
19
+ ];
20
+ const $ = id => document.getElementById(id);
21
+ const clone = value => JSON.parse(JSON.stringify(value));
22
+ let tasks = clone(SEED);
23
+ let view = 'today';
24
+ let loading = true;
25
+ let saveDelay = 500;
26
+ let failNext = false;
27
+ let pending = null;
28
+ let failed = null;
29
+ let feedback = '';
30
+ let feedbackType = '';
31
+ let editingId = null;
32
+ let generation = 0;
33
+ let idCounter = 0;
34
+ let opener = null;
35
+
36
+ try {
37
+ const saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
38
+ if (Array.isArray(saved) && saved.every(t => t && typeof t.id === 'string' && typeof t.title === 'string' && typeof t.notes === 'string' && typeof t.dueDate === 'string' && typeof t.completed === 'boolean')) tasks = saved;
39
+ } catch (_) { /* A blocked store still permits an in-memory session. */ }
40
+
41
+ const belongsToday = task => Boolean(task.dueDate && task.dueDate <= TODAY);
42
+ const currentTasks = () => tasks.filter(task => view === 'all' || belongsToday(task));
43
+
44
+ function element(tag, className, text) {
45
+ const node = document.createElement(tag);
46
+ if (className) node.className = className;
47
+ if (text !== undefined) node.textContent = text;
48
+ return node;
49
+ }
50
+
51
+ function dateLabel(date) {
52
+ if (!date) return 'No date';
53
+ if (date === TODAY) return 'Today';
54
+ const label = new Date(`${date}T12:00:00`).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
55
+ return date < TODAY ? `Overdue · ${label}` : label;
56
+ }
57
+
58
+ function taskRow(task) {
59
+ const row = element('div', `task-row${task.completed ? ' is-complete' : ''}`);
60
+ row.dataset.taskId = task.id;
61
+ const label = element('label', 'task-check-label');
62
+ const check = element('input', 'task-checkbox');
63
+ check.type = 'checkbox';
64
+ check.checked = task.completed;
65
+ check.disabled = Boolean(pending);
66
+ check.setAttribute('aria-label', `${task.completed ? 'Mark incomplete' : 'Complete'}: ${task.title}`);
67
+ check.addEventListener('change', () => {
68
+ check.checked = task.completed;
69
+ save({ type: 'toggle', id: task.id, completed: !task.completed });
70
+ });
71
+ label.append(check);
72
+ const open = element('button', 'task-open');
73
+ open.type = 'button';
74
+ open.disabled = Boolean(pending);
75
+ open.setAttribute('aria-label', `Task details: ${task.title}`);
76
+ const text = element('span', 'task-text');
77
+ text.append(element('span', 'task-title', task.title));
78
+ if (view === 'all' || task.dueDate < TODAY) text.append(element('span', `task-meta${task.dueDate && task.dueDate < TODAY ? ' overdue' : ''}`, dateLabel(task.dueDate)));
79
+ open.append(text);
80
+ if (task.notes) {
81
+ const note = element('span', 'note-icon', '≡');
82
+ note.setAttribute('aria-label', 'Has notes');
83
+ open.append(note);
84
+ }
85
+ const arrow = element('span', 'row-arrow', '›');
86
+ arrow.setAttribute('aria-hidden', 'true');
87
+ open.append(arrow);
88
+ open.addEventListener('click', () => openDetails(task, open));
89
+ row.append(label, open);
90
+ return row;
91
+ }
92
+
93
+ function renderFeedback() {
94
+ const target = $('save-feedback');
95
+ target.replaceChildren();
96
+ target.className = `save-feedback ${feedbackType}`;
97
+ if (pending) {
98
+ const spinner = element('span', 'spinner');
99
+ spinner.setAttribute('aria-hidden', 'true');
100
+ target.append(spinner);
101
+ }
102
+ if (feedback) target.append(element('span', 'feedback-copy', feedback));
103
+ if (failed && !pending) {
104
+ const retry = element('button', 'retry-button', 'Try again');
105
+ retry.type = 'button';
106
+ retry.addEventListener('click', () => save(failed));
107
+ target.append(retry);
108
+ }
109
+ const detail = $('detail-feedback');
110
+ detail.className = `detail-feedback ${feedbackType}`;
111
+ detail.textContent = editingId && (pending?.type === 'edit' || failed?.type === 'edit') ? feedback : '';
112
+ $('add-button').disabled = Boolean(pending) || loading;
113
+ $('new-task').disabled = Boolean(pending) || loading;
114
+ $('add-button').firstChild.textContent = pending?.type === 'add' ? 'Adding…' : 'Add';
115
+ $('save-detail').disabled = Boolean(pending);
116
+ $('save-detail').textContent = pending?.type === 'edit' ? 'Saving…' : failed?.type === 'edit' ? 'Try saving again' : 'Save changes';
117
+ for (const id of ['detail-title', 'detail-date', 'detail-notes']) $(id).disabled = Boolean(pending);
118
+ $('close-detail').disabled = Boolean(pending);
119
+ $('cancel-detail').disabled = Boolean(pending);
120
+ }
121
+
122
+ function render() {
123
+ document.querySelectorAll('[data-view]').forEach(button => {
124
+ const active = button.dataset.view === view;
125
+ button.classList.toggle('active', active);
126
+ if (active) button.setAttribute('aria-current', 'page');
127
+ else button.removeAttribute('aria-current');
128
+ });
129
+ $('view-title').replaceChildren(document.createTextNode(view === 'today' ? 'Today' : 'All tasks'), element('span', 'title-dot', '.'));
130
+ $('view-description').textContent = view === 'today' ? 'A clear list for a lighter day.' : 'Everything you want to make time for.';
131
+ const visible = currentTasks();
132
+ const active = visible.filter(task => !task.completed);
133
+ const complete = visible.filter(task => task.completed);
134
+ $('today-count').textContent = tasks.filter(task => !task.completed && belongsToday(task)).length;
135
+ $('all-count').textContent = tasks.filter(task => !task.completed).length;
136
+ $('remaining-badge').textContent = loading ? 'Loading…' : `${active.length} left`;
137
+ $('list-count').textContent = loading ? '' : `${active.length} ${active.length === 1 ? 'task' : 'tasks'}`;
138
+ $('task-section').setAttribute('aria-busy', String(loading));
139
+ const list = $('task-list');
140
+ list.replaceChildren();
141
+ $('completed-list').replaceChildren();
142
+ if (loading) {
143
+ for (let i = 0; i < 5; i++) {
144
+ const row = element('div', 'skeleton-row');
145
+ row.setAttribute('aria-hidden', 'true');
146
+ row.append(element('span', 'skeleton-circle'), element('span', 'skeleton-line'));
147
+ list.append(row);
148
+ }
149
+ list.setAttribute('aria-label', 'Loading your tasks');
150
+ } else {
151
+ list.removeAttribute('aria-label');
152
+ active.forEach(task => list.append(taskRow(task)));
153
+ complete.forEach(task => $('completed-list').append(taskRow(task)));
154
+ }
155
+ $('completed-section').hidden = loading || complete.length === 0;
156
+ $('completed-count').textContent = complete.length;
157
+ $('empty-state').hidden = loading || active.length > 0;
158
+ $('empty-title').textContent = complete.length ? 'Look at you. All done.' : view === 'today' && tasks.length ? 'Today has a little room.' : 'A little room to breathe.';
159
+ $('empty-description').textContent = complete.length ? 'Your tasks are taken care of. Enjoy a little headspace, or add what’s next.' : 'Add a task and take the day one thing at a time.';
160
+ renderFeedback();
161
+ }
162
+
163
+ function applyOperation(operation) {
164
+ if (operation.type === 'add') return [...tasks, clone(operation.task)];
165
+ return tasks.map(task => {
166
+ if (task.id !== operation.id) return task;
167
+ return operation.type === 'toggle' ? { ...task, completed: operation.completed } : { ...task, ...operation.changes };
168
+ });
169
+ }
170
+
171
+ async function save(operation) {
172
+ if (pending || loading || !operation) return;
173
+ const shouldFail = failNext;
174
+ failNext = false;
175
+ const token = generation;
176
+ pending = clone(operation);
177
+ failed = null;
178
+ feedbackType = '';
179
+ feedback = operation.type === 'add' ? 'Adding your task…' : 'Saving your changes…';
180
+ render();
181
+ await new Promise(resolve => setTimeout(resolve, saveDelay));
182
+ if (token !== generation) return;
183
+ try {
184
+ if (shouldFail) throw new Error('Simulated save failure');
185
+ const nextTasks = applyOperation(operation);
186
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(nextTasks));
187
+ tasks = nextTasks;
188
+ pending = null;
189
+ feedbackType = 'success';
190
+ if (operation.type === 'add') {
191
+ $('new-task').value = '';
192
+ feedback = 'Task added to today.';
193
+ } else if (operation.type === 'toggle') {
194
+ feedback = operation.completed ? 'Task completed. Nicely done.' : 'Task marked incomplete.';
195
+ } else {
196
+ const date = operation.changes.dueDate;
197
+ feedback = view === 'today' && (!date || date > TODAY) ? 'Changes saved. Find this task in All tasks.' : 'Changes saved.';
198
+ closeDetails();
199
+ }
200
+ render();
201
+ if (operation.type === 'add') $('new-task').focus();
202
+ if (operation.type === 'toggle') {
203
+ const row = Array.from(document.querySelectorAll('.task-row')).find(node => node.dataset.taskId === operation.id);
204
+ if (operation.completed) $('completed-section').querySelector('summary').focus();
205
+ else row?.querySelector('input').focus();
206
+ }
207
+ } catch (_) {
208
+ pending = null;
209
+ failed = clone(operation);
210
+ feedbackType = 'error';
211
+ feedback = 'Couldn’t save. Your changes are still here. Try again.';
212
+ render();
213
+ if (operation.type === 'edit') $('save-detail').focus();
214
+ else $('save-feedback').querySelector('button')?.focus();
215
+ }
216
+ }
217
+
218
+ function openDetails(task, source) {
219
+ if (pending) return;
220
+ editingId = task.id;
221
+ opener = source;
222
+ $('detail-title').value = task.title;
223
+ $('detail-date').value = task.dueDate;
224
+ $('detail-notes').value = task.notes;
225
+ renderFeedback();
226
+ $('task-dialog').showModal();
227
+ $('detail-title').focus();
228
+ }
229
+
230
+ function closeDetails() {
231
+ if (pending) return;
232
+ $('task-dialog').close();
233
+ editingId = null;
234
+ if (opener?.isConnected) opener.focus();
235
+ else $('new-task').focus();
236
+ opener = null;
237
+ }
238
+
239
+ $('add-form').addEventListener('submit', event => {
240
+ event.preventDefault();
241
+ const title = $('new-task').value.trim();
242
+ if (!title) { $('new-task').value = ''; $('new-task').reportValidity(); return; }
243
+ save({ type: 'add', task: { id: `task-${Date.now()}-${++idCounter}`, title, notes: '', dueDate: TODAY, completed: false } });
244
+ });
245
+ $('detail-form').addEventListener('submit', event => {
246
+ event.preventDefault();
247
+ const title = $('detail-title').value.trim();
248
+ if (!title) { $('detail-title').value = ''; $('detail-title').reportValidity(); return; }
249
+ save({ type: 'edit', id: editingId, changes: { title, dueDate: $('detail-date').value, notes: $('detail-notes').value.trim() } });
250
+ });
251
+ $('close-detail').addEventListener('click', closeDetails);
252
+ $('cancel-detail').addEventListener('click', () => {
253
+ if (failed?.type === 'edit' && failed.id === editingId) { failed = null; feedback = ''; feedbackType = ''; }
254
+ closeDetails();
255
+ renderFeedback();
256
+ });
257
+ $('task-dialog').addEventListener('cancel', event => {
258
+ event.preventDefault();
259
+ if (!pending) $('cancel-detail').click();
260
+ });
261
+ $('empty-add').addEventListener('click', () => $('new-task').focus());
262
+ document.querySelectorAll('[data-view]').forEach(button => button.addEventListener('click', () => {
263
+ view = button.dataset.view;
264
+ render();
265
+ }));
266
+
267
+ window.__benchmark = {
268
+ reset(mode = 'seed') {
269
+ if (!['seed', 'empty'].includes(mode)) throw new Error('Use seed or empty');
270
+ generation++;
271
+ tasks = mode === 'seed' ? clone(SEED) : [];
272
+ view = 'today';
273
+ loading = false;
274
+ pending = null;
275
+ failed = null;
276
+ failNext = false;
277
+ saveDelay = 500;
278
+ feedback = '';
279
+ feedbackType = '';
280
+ editingId = null;
281
+ $('new-task').value = '';
282
+ $('task-dialog').close();
283
+ $('completed-section').open = false;
284
+ try { localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks)); } catch (_) { /* Reset still updates the current session. */ }
285
+ render();
286
+ return this.getState();
287
+ },
288
+ setSaveDelay(milliseconds) {
289
+ if (!Number.isFinite(milliseconds) || milliseconds < 0) throw new Error('Delay must be a nonnegative number');
290
+ saveDelay = milliseconds;
291
+ },
292
+ failNextSave() { failNext = true; },
293
+ getState() { return clone({ tasks, today: TODAY, view, loading, pending, failed, feedback, feedbackType, editingId, saveDelay }); }
294
+ };
295
+
296
+ render();
297
+ const initialGeneration = generation;
298
+ setTimeout(() => {
299
+ if (generation !== initialGeneration) return;
300
+ loading = false;
301
+ render();
302
+ }, 500);
303
+ })();
@@ -0,0 +1,11 @@
1
+ # Frozen generated output
2
+
3
+ - [HTML](index.html)
4
+ - [CSS](styles.css)
5
+ - [JavaScript](app.js)
6
+ - [Generation notes](GENERATION_NOTES.md)
7
+ - [Source hashes](source-hashes.json)
8
+ - [Runtime checks](runtime-checks.json)
9
+ - [Screenshot directory](screenshots/)
10
+
11
+ These are the generator's frozen files, copied byte-for-byte. No application code was fixed after generation. Run the HTTP server from the run root as described in [the run README](../README.md).