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.
- package/AGENTS.md +156 -0
- package/CHANGELOG.md +71 -0
- package/CODE_OF_CONDUCT.md +29 -0
- package/CONTRIBUTING.md +128 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/RELEASE_NOTES_v0.1.0.md +45 -0
- package/RELEASE_NOTES_v0.3.0.md +47 -0
- package/RELEASE_NOTES_v0.4.0.md +29 -0
- package/RELEASE_READINESS.md +107 -0
- package/ROADMAP.md +60 -0
- package/SECURITY.md +40 -0
- package/assets/logo.svg +9 -0
- package/benchmarks/README.md +19 -0
- package/benchmarks/chat.md +37 -0
- package/benchmarks/mobile-home-screen.md +38 -0
- package/benchmarks/notes-app.md +37 -0
- package/benchmarks/settings.md +37 -0
- package/benchmarks/shopping-list.md +37 -0
- package/benchmarks/todo-app.md +144 -0
- package/checklists/DESIGN_QA.md +102 -0
- package/checklists/GENERATED_INDEX.md +7 -0
- package/docs/AGENT_CONTEXT.md +111 -0
- package/docs/BENCHMARK.md +170 -0
- package/docs/DESIGNLINT_READINESS.md +110 -0
- package/docs/DESIGN_SYSTEM.md +23 -0
- package/docs/EVALUATION_RUBRIC.md +154 -0
- package/docs/GITHUB_SETUP.md +28 -0
- package/docs/GLOSSARY.md +45 -0
- package/docs/INDEX.md +109 -0
- package/docs/KNOWLEDGE_ENGINE.md +594 -0
- package/docs/MOBILE_FIRST.md +18 -0
- package/docs/NPM_RELEASE.md +43 -0
- package/docs/PATTERN_SPEC.md +113 -0
- package/docs/PHILOSOPHY.md +25 -0
- package/docs/PRODUCT_THINKING.md +26 -0
- package/docs/STYLE_GUIDE.md +49 -0
- package/docs/TRACEABILITY.md +45 -0
- package/evidence/README.md +64 -0
- package/evidence/TEMPLATE.md +46 -0
- package/evidence/chat/.gitkeep +1 -0
- package/evidence/mobile-home/.gitkeep +1 -0
- package/evidence/notes/.gitkeep +1 -0
- package/evidence/settings/.gitkeep +1 -0
- package/evidence/shopping/.gitkeep +1 -0
- package/evidence/todo/.gitkeep +1 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/EVALUATION.md +66 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/README.md +40 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/generated-output.md +163 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/metadata.json +38 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/notes.md +20 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/prompt.md +16 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/ai-design-rules/scores.md +18 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/baseline/generated-output.md +98 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/baseline/metadata.json +19 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/baseline/notes.md +19 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/baseline/prompt.md +9 -0
- package/evidence/todo/2026-06-25-codex-gpt-5/baseline/scores.md +18 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/EVALUATION.md +70 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/README.md +52 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/GENERATION_NOTES.md +133 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/app.js +289 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/context-preserving-preview.json +443 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/daily-home-surface.json +373 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/quick-capture.json +301 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/context/sources-read.txt +36 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/generated-output.md +11 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/index.html +64 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/launch-prompt.md +1 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/metadata.json +55 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/notes.md +15 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/parent-status-message.md +1 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/prompt.md +49 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/runtime-checks.json +776 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/scores.md +20 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-completed.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-default.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail-reduced-motion.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-detail.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-empty.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-keyboard-focus.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-save-error.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saved.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-saving.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/desktop-validation.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-completed.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-default.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail-reduced-motion.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-detail.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-empty.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-keyboard-focus.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-save-error.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saved.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-saving.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/screenshots/mobile-validation.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/source-hashes.json +5 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/ai-design-rules/styles.css +122 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/GENERATION_NOTES.md +86 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/app.js +303 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/generated-output.md +11 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/index.html +71 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/launch-prompt.md +1 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/metadata.json +55 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/notes.md +15 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/parent-status-message.md +1 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/prompt.md +49 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/runtime-checks.json +772 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/scores.md +20 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-completed.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-default.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail-reduced-motion.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-detail.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-empty.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-keyboard-focus.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-save-error.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saved.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-saving.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/desktop-validation.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-completed.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-default.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail-reduced-motion.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-detail.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-empty.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-keyboard-focus.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-save-error.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saved.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-saving.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/screenshots/mobile-validation.png +0 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/source-hashes.json +5 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/baseline/styles.css +9 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/capture-ai-design-rules.js +60 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/capture-baseline.js +60 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/brief.md +35 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge-files.json +127 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/knowledge.patch +890 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/review-context.json +550 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/seed.json +86 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/setup.json +47 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/common/technical-envelope.md +11 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/layout-metrics.json +133 -0
- package/evidence/todo/2026-09-28-codex-paired-todo/layout-probe.js +15 -0
- package/examples/GENERATED_INDEX.md +8 -0
- package/examples/INDEX.md +16 -0
- package/examples/README.md +15 -0
- package/examples/consequential-action-confirmation-reference.md +83 -0
- package/examples/todo-app-benchmark.md +117 -0
- package/examples/todo-reference/README.md +29 -0
- package/examples/todo-reference/app.js +154 -0
- package/examples/todo-reference/favicon.svg +4 -0
- package/examples/todo-reference/index.html +67 -0
- package/examples/todo-reference/review-evidence/2026-07-15/desktop-default.png +0 -0
- package/examples/todo-reference/review-evidence/2026-07-15/desktop-detail.png +0 -0
- package/examples/todo-reference/review-evidence/2026-07-15/mobile-detail.png +0 -0
- package/examples/todo-reference/review-evidence/2026-07-15/mobile-error.png +0 -0
- package/examples/todo-reference/styles.css +241 -0
- package/graph/GENERATED_GRAPH.md +71 -0
- package/observations/README.md +23 -0
- package/observations/accessibility/reduced-interaction-motion.md +49 -0
- package/observations/accessibility/textual-error-recovery.md +50 -0
- package/observations/accessibility/visible-keyboard-focus.md +49 -0
- package/observations/ai/agent-consequential-action-controls.md +56 -0
- package/observations/ai/agent-work-visibility-and-intervention.md +51 -0
- package/observations/ai/figma-agent-context-and-skills.md +52 -0
- package/observations/finance/high-risk-wallet-recovery-flows.md +55 -0
- package/observations/finance/market-interface-adaptive-trading-surfaces.md +55 -0
- package/observations/interaction/chrome-scoped-view-transitions.md +52 -0
- package/observations/interaction/figma-motion-as-system-capability.md +53 -0
- package/observations/mobile/mobile-bottom-sheet-choice-contracts.md +55 -0
- package/observations/performance/reserved-loading-space.md +51 -0
- package/observations/product-repos/pinned-ai-design-rules-integration.md +49 -0
- package/observations/visual/apple-liquid-glass-functional-layer.md +52 -0
- package/observations/visual/chrome-content-first-adaptive-ui.md +51 -0
- package/observations/visual/material-3-expressive-system.md +52 -0
- package/package.json +65 -0
- package/patterns/GENERATED_INDEX.md +13 -0
- package/patterns/INDEX.md +41 -0
- package/patterns/README.md +5 -0
- package/patterns/consequential-action-confirmation.md +127 -0
- package/patterns/context-preserving-preview.md +145 -0
- package/patterns/daily-home-surface.md +134 -0
- package/patterns/mobile-primary-action.md +110 -0
- package/patterns/object-status-list.md +114 -0
- package/patterns/progressive-detail.md +113 -0
- package/patterns/quick-capture.md +132 -0
- package/prompts/CONSEQUENTIAL_ACTION_REVIEW.md +100 -0
- package/prompts/DESIGN_EVIDENCE_REVIEW.md +49 -0
- package/prompts/GENERATED_INDEX.md +11 -0
- package/prompts/INDEX.md +24 -0
- package/prompts/PROTOTYPE_REVIEW.md +189 -0
- package/prompts/QUICK_CAPTURE_STATE_REVIEW.md +74 -0
- package/prompts/TODO_APP_BENCHMARK.md +88 -0
- package/registry/objects.json +568 -0
- package/registry/relationships.json +972 -0
- package/research/GENERATED_INDEX.md +18 -0
- package/research/INDEX.md +40 -0
- package/research/accessibility/keyboard-focus-and-interaction-motion.md +62 -0
- package/research/accessibility/textual-error-recovery.md +57 -0
- package/research/performance/reserved-loading-space.md +56 -0
- package/research/products/apple-reminders.md +59 -0
- package/research/products/arc.md +61 -0
- package/research/products/linear.md +62 -0
- package/research/products/telegram.md +61 -0
- package/research/products/things-3.md +60 -0
- package/research/ux/contextual-agentic-interface.md +68 -0
- package/research/ux/high-risk-operational-flows.md +89 -0
- package/research/ux/motion-as-state-continuity.md +71 -0
- package/research/visual/expressive-system-ui-2026.md +75 -0
- package/reviews/GENERATED_INDEX.md +9 -0
- package/reviews/README.md +25 -0
- package/reviews/consequential-action-confirmation-review.md +92 -0
- package/reviews/todo-app-benchmark-reference-review.md +86 -0
- package/reviews/todo-reference-fixture-review.md +77 -0
- package/rules/GENERATED_INDEX.md +23 -0
- package/rules/INDEX.md +37 -0
- package/rules/accessibility/A11Y-001.md +58 -0
- package/rules/accessibility/A11Y-002.md +55 -0
- package/rules/accessibility/A11Y-003.md +54 -0
- package/rules/accessibility/A11Y-004.md +54 -0
- package/rules/ia/IA-001.md +60 -0
- package/rules/ia/IA-002.md +61 -0
- package/rules/performance/PERF-001.md +55 -0
- package/rules/product/PRD-001.md +61 -0
- package/rules/product/PRD-002.md +61 -0
- package/rules/ux/UX-001.md +58 -0
- package/rules/ux/UX-002.md +61 -0
- package/rules/ux/UX-003.md +60 -0
- package/rules/ux/UX-004.md +61 -0
- package/rules/ux/UX-005.md +62 -0
- package/rules/ux/UX-006.md +69 -0
- package/rules/visual/VIS-001.md +59 -0
- package/rules/visual/VIS-002.md +59 -0
- package/schema/checklist.schema.json +21 -0
- package/schema/common.schema.json +178 -0
- package/schema/observation.schema.json +21 -0
- package/schema/pattern.schema.json +21 -0
- package/schema/prompt.schema.json +21 -0
- package/schema/reference-project.schema.json +21 -0
- package/schema/research.schema.json +21 -0
- package/schema/review.schema.json +21 -0
- package/schema/rule.schema.json +21 -0
- package/skills/README.md +48 -0
- package/skills/accessibility-reviewer/SKILL.md +101 -0
- package/skills/agent-context/SKILL.md +34 -0
- package/skills/agent-context/agents/openai.yaml +4 -0
- package/skills/design-evidence-researcher/SKILL.md +47 -0
- package/skills/design-evidence-researcher/agents/openai.yaml +4 -0
- package/skills/design-reviewer/SKILL.md +113 -0
- package/skills/design-system-architect/SKILL.md +101 -0
- package/skills/information-architect/SKILL.md +97 -0
- package/skills/interaction-designer/SKILL.md +104 -0
- package/skills/knowledge-graph-architect/SKILL.md +96 -0
- package/skills/mobile-ux-expert/SKILL.md +105 -0
- package/skills/motion-designer/SKILL.md +100 -0
- package/skills/performance-reviewer/SKILL.md +97 -0
- package/skills/product-designer/SKILL.md +104 -0
- package/skills/prompt-architect/SKILL.md +105 -0
- package/skills/reference-driven-design/SKILL.md +119 -0
- package/skills/ux-reviewer/SKILL.md +99 -0
- package/skills/visual-designer/SKILL.md +107 -0
- package/starter-kit/AGENTS.md +37 -0
- package/starter-kit/BOOTSTRAP.md +13 -0
- package/starter-kit/INSTALL_WITH_AGENT.md +133 -0
- package/starter-kit/PROJECT_INTEGRATION.md +60 -0
- package/starter-kit/README.md +41 -0
- package/starter-kit/benchmarks/BENCHMARK_CHECKLIST.md +10 -0
- package/starter-kit/docs/DESIGN_DECISIONS.md +22 -0
- package/starter-kit/docs/INFORMATION_ARCHITECTURE.md +25 -0
- package/starter-kit/docs/PERSONAS.md +14 -0
- package/starter-kit/docs/PRD.md +32 -0
- package/starter-kit/docs/USER_FLOWS.md +19 -0
- package/starter-kit/reviews/DESIGN_REVIEW.md +49 -0
- package/starter-kit/templates/FEATURE_TEMPLATE.md +36 -0
- package/starter-kit/templates/TASK_TEMPLATE.md +22 -0
- package/templates/CHECKLIST_TEMPLATE.md +43 -0
- package/templates/OBSERVATION_TEMPLATE.md +41 -0
- package/templates/PATTERN_TEMPLATE.md +100 -0
- package/templates/PROMPT_TEMPLATE.md +46 -0
- package/templates/PROTOTYPE_REVIEW.md +50 -0
- package/templates/REFERENCE_PROJECT_TEMPLATE.md +49 -0
- package/templates/REVIEW_TEMPLATE.md +55 -0
- package/templates/RULE_TEMPLATE.md +57 -0
- package/tools/cli.mjs +46 -0
- package/tools/context.mjs +342 -0
- package/tools/designlint.mjs +77 -0
- package/tools/generate-indexes.mjs +621 -0
- package/tools/init.mjs +99 -0
- package/tools/validate-benchmark-evidence.mjs +248 -0
- package/tools/validate-knowledge.mjs +481 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const SEED = [
|
|
3
|
+
{
|
|
4
|
+
"id": "t01",
|
|
5
|
+
"title": "Book dentist appointment",
|
|
6
|
+
"notes": "Ask about a morning appointment.",
|
|
7
|
+
"dueDate": "2026-09-28",
|
|
8
|
+
"completed": false
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "t02",
|
|
12
|
+
"title": "Pick up parcel",
|
|
13
|
+
"notes": "",
|
|
14
|
+
"dueDate": "2026-09-28",
|
|
15
|
+
"completed": false
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "t03",
|
|
19
|
+
"title": "Reply to Maya",
|
|
20
|
+
"notes": "",
|
|
21
|
+
"dueDate": "2026-09-28",
|
|
22
|
+
"completed": false
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "t04",
|
|
26
|
+
"title": "Pay electricity bill",
|
|
27
|
+
"notes": "",
|
|
28
|
+
"dueDate": "2026-09-28",
|
|
29
|
+
"completed": false
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "t05",
|
|
33
|
+
"title": "Fix bicycle light",
|
|
34
|
+
"notes": "",
|
|
35
|
+
"dueDate": "2026-09-28",
|
|
36
|
+
"completed": false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "t06",
|
|
40
|
+
"title": "Water the plants",
|
|
41
|
+
"notes": "",
|
|
42
|
+
"dueDate": "2026-09-28",
|
|
43
|
+
"completed": false
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "t07",
|
|
47
|
+
"title": "Read chapter twelve",
|
|
48
|
+
"notes": "",
|
|
49
|
+
"dueDate": "2026-09-28",
|
|
50
|
+
"completed": false
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "t08",
|
|
54
|
+
"title": "Plan weekend walk",
|
|
55
|
+
"notes": "",
|
|
56
|
+
"dueDate": "2026-09-28",
|
|
57
|
+
"completed": false
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "t09",
|
|
61
|
+
"title": "Return library book",
|
|
62
|
+
"notes": "",
|
|
63
|
+
"dueDate": "2026-09-28",
|
|
64
|
+
"completed": false
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "t10",
|
|
68
|
+
"title": "Take out recycling",
|
|
69
|
+
"notes": "",
|
|
70
|
+
"dueDate": "2026-09-28",
|
|
71
|
+
"completed": false
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "t11",
|
|
75
|
+
"title": "Order coffee beans",
|
|
76
|
+
"notes": "",
|
|
77
|
+
"dueDate": "2026-09-28",
|
|
78
|
+
"completed": false
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "t12",
|
|
82
|
+
"title": "Stretch for ten minutes",
|
|
83
|
+
"notes": "",
|
|
84
|
+
"dueDate": "2026-09-28",
|
|
85
|
+
"completed": true
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
const TODAY = '2026-09-28';
|
|
89
|
+
const STORE = 'daylight-adr-benchmark-v1';
|
|
90
|
+
const $ = id => document.getElementById(id);
|
|
91
|
+
const clone = value => JSON.parse(JSON.stringify(value));
|
|
92
|
+
let tasks = clone(SEED), view = 'today', loading = true, showCompleted = true;
|
|
93
|
+
let delay = 500, failNext = false, pending = null, epoch = 0, sequence = 0;
|
|
94
|
+
let selected = null, opener = null, sourceScroll = 0, failedOperation = null;
|
|
95
|
+
let drafts = {}, status = 'idle';
|
|
96
|
+
try {
|
|
97
|
+
const stored = JSON.parse(localStorage.getItem(STORE));
|
|
98
|
+
if (Array.isArray(stored) && stored.every(t => typeof t.id === 'string' && typeof t.title === 'string' && typeof t.notes === 'string' && typeof t.dueDate === 'string' && typeof t.completed === 'boolean')) tasks = stored;
|
|
99
|
+
} catch (_) { /* The app remains usable; a failed write gets explicit recovery. */ }
|
|
100
|
+
|
|
101
|
+
function message(text, kind = 'idle', retry = false) {
|
|
102
|
+
status = kind;
|
|
103
|
+
$('feedback-text').textContent = text;
|
|
104
|
+
$('feedback').className = 'feedback ' + kind;
|
|
105
|
+
$('retry').hidden = !retry;
|
|
106
|
+
}
|
|
107
|
+
function detailMessage(text, kind = '') {
|
|
108
|
+
$('detail-feedback').textContent = text;
|
|
109
|
+
$('detail-feedback').className = 'detail-feedback ' + kind;
|
|
110
|
+
}
|
|
111
|
+
function visibleTasks() { return tasks.filter(t => view === 'all' || t.dueDate === TODAY || (t.dueDate && t.dueDate < TODAY && !t.completed)); }
|
|
112
|
+
function dateLabel(date) {
|
|
113
|
+
if (!date) return 'No date';
|
|
114
|
+
if (date === TODAY) return 'Today';
|
|
115
|
+
return new Date(date + 'T12:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'});
|
|
116
|
+
}
|
|
117
|
+
function taskRow(task) {
|
|
118
|
+
const row = document.createElement('li');
|
|
119
|
+
row.className = 'task-row' + (task.completed ? ' is-completed' : '');
|
|
120
|
+
row.dataset.taskId = task.id;
|
|
121
|
+
if (pending?.id === task.id) row.setAttribute('aria-busy', 'true');
|
|
122
|
+
const check = document.createElement('button');
|
|
123
|
+
check.className = 'check';
|
|
124
|
+
check.setAttribute('role', 'checkbox');
|
|
125
|
+
check.setAttribute('aria-checked', String(task.completed));
|
|
126
|
+
check.setAttribute('aria-label', `${task.completed ? 'Mark incomplete' : 'Complete'}: ${task.title}`);
|
|
127
|
+
check.disabled = Boolean(pending);
|
|
128
|
+
const circle = document.createElement('span');
|
|
129
|
+
circle.className = 'check-circle'; circle.setAttribute('aria-hidden', 'true'); circle.textContent = task.completed ? '✓' : '';
|
|
130
|
+
check.append(circle);
|
|
131
|
+
check.addEventListener('click', () => save({kind:'complete', id:task.id, completed:!task.completed, title:task.title}));
|
|
132
|
+
const open = document.createElement('button');
|
|
133
|
+
open.className = 'task-open'; open.dataset.openId = task.id;
|
|
134
|
+
open.setAttribute('aria-label', `Details: ${task.title}`);
|
|
135
|
+
const text = document.createElement('span'); text.className = 'task-text';
|
|
136
|
+
const title = document.createElement('span'); title.className = 'task-title'; title.textContent = task.title;
|
|
137
|
+
text.append(title);
|
|
138
|
+
if (task.notes || view === 'all' || (task.dueDate && task.dueDate < TODAY)) {
|
|
139
|
+
const meta = document.createElement('span'); meta.className = 'task-meta';
|
|
140
|
+
meta.textContent = [task.dueDate && task.dueDate < TODAY && !task.completed ? 'Overdue · ' + dateLabel(task.dueDate) : view === 'all' ? dateLabel(task.dueDate) : '', task.notes ? 'Has notes' : ''].filter(Boolean).join(' · ');
|
|
141
|
+
text.append(meta);
|
|
142
|
+
}
|
|
143
|
+
const arrow = document.createElement('span'); arrow.className = 'row-arrow'; arrow.textContent = '›'; arrow.setAttribute('aria-hidden','true');
|
|
144
|
+
open.append(text,arrow); open.addEventListener('click', () => openDetail(task.id, open));
|
|
145
|
+
row.append(check,open); return row;
|
|
146
|
+
}
|
|
147
|
+
function render() {
|
|
148
|
+
const list = visibleTasks(); const active = list.filter(t => !t.completed); const done = list.filter(t => t.completed);
|
|
149
|
+
$('today-count').textContent = tasks.filter(t => !t.completed && t.dueDate && t.dueDate <= TODAY).length;
|
|
150
|
+
$('all-count').textContent = tasks.filter(t => !t.completed).length;
|
|
151
|
+
document.querySelectorAll('[data-view]').forEach(button => {
|
|
152
|
+
const activeView = button.dataset.view === view;
|
|
153
|
+
button.classList.toggle('active', activeView);
|
|
154
|
+
if (activeView) button.setAttribute('aria-current','page'); else button.removeAttribute('aria-current');
|
|
155
|
+
});
|
|
156
|
+
$('view-title').replaceChildren(document.createTextNode(view === 'today' ? 'Today' : 'All tasks'));
|
|
157
|
+
if (view === 'today') { const sun=document.createElement('span'); sun.className='sun'; sun.textContent='☀'; sun.setAttribute('aria-hidden','true'); $('view-title').append(sun); }
|
|
158
|
+
$('summary').textContent = loading ? 'Getting your day ready…' : active.length ? `${active.length} ${active.length === 1 ? 'task' : 'tasks'} to do. One thing at a time.` : done.length ? 'A good day, one small step at a time.' : 'A fresh start. Make room for what matters.';
|
|
159
|
+
$('remaining-count').textContent = loading ? '' : active.length;
|
|
160
|
+
$('task-region').setAttribute('aria-busy',String(loading));
|
|
161
|
+
$('task-list').replaceChildren(); $('completed-list').replaceChildren();
|
|
162
|
+
if (loading) {
|
|
163
|
+
for (let i=0;i<Math.max(1,active.length);i++) { const li=document.createElement('li'); li.className='task-row'; li.setAttribute('aria-hidden','true'); const line=document.createElement('span'); line.className='skeleton'; li.append(line); $('task-list').append(li); }
|
|
164
|
+
} else { active.forEach(task => $('task-list').append(taskRow(task))); done.forEach(task => $('completed-list').append(taskRow(task))); }
|
|
165
|
+
$('empty').hidden = loading || active.length > 0;
|
|
166
|
+
$('empty-title').textContent = done.length ? 'All clear for now' : view === 'today' && tasks.length ? 'Nothing planned for today' : 'A little room to begin';
|
|
167
|
+
$('empty-copy').textContent = done.length ? 'You’ve taken care of your list. Enjoy a little breathing room.' : 'Add a task below. One small thing is a good start.';
|
|
168
|
+
$('completed-section').hidden = loading || done.length === 0;
|
|
169
|
+
$('completed-count').textContent = done.length;
|
|
170
|
+
$('completed-toggle').setAttribute('aria-expanded',String(showCompleted));
|
|
171
|
+
$('completed-chevron').textContent = showCompleted ? '⌄' : '›';
|
|
172
|
+
$('completed-list').hidden = !showCompleted;
|
|
173
|
+
$('add-task').disabled = Boolean(pending) || loading;
|
|
174
|
+
$('task-title').readOnly = Boolean(pending) || loading;
|
|
175
|
+
$('add-task').textContent = pending?.kind === 'add' ? 'Saving…' : failedOperation?.kind === 'add' ? 'Retry' : 'Add';
|
|
176
|
+
$('save-detail').disabled = Boolean(pending);
|
|
177
|
+
for (const id of ['detail-title','detail-notes','detail-date']) $(id).readOnly = Boolean(pending?.kind === 'edit' && pending.id === selected);
|
|
178
|
+
}
|
|
179
|
+
function save(operation) {
|
|
180
|
+
if (pending || loading) return;
|
|
181
|
+
const token = epoch; const shouldFail = failNext; failNext = false;
|
|
182
|
+
pending = clone(operation); failedOperation = null;
|
|
183
|
+
message('Saving…', 'saving');
|
|
184
|
+
if (operation.kind === 'edit' && selected === operation.id) detailMessage('Saving your changes…');
|
|
185
|
+
render();
|
|
186
|
+
setTimeout(() => {
|
|
187
|
+
if (token !== epoch) return;
|
|
188
|
+
let updated = clone(tasks);
|
|
189
|
+
if (operation.kind === 'add') updated.unshift(operation.task);
|
|
190
|
+
if (operation.kind === 'complete') updated = updated.map(t => t.id === operation.id ? {...t, completed:operation.completed} : t);
|
|
191
|
+
if (operation.kind === 'edit') updated = updated.map(t => t.id === operation.id ? {...t,...operation.fields} : t);
|
|
192
|
+
try {
|
|
193
|
+
if (shouldFail) throw new Error('simulated');
|
|
194
|
+
localStorage.setItem(STORE,JSON.stringify(updated));
|
|
195
|
+
tasks = updated; pending = null; failedOperation = null;
|
|
196
|
+
if (operation.kind === 'add') {
|
|
197
|
+
$('task-title').value = ''; $('task-title').removeAttribute('aria-invalid');
|
|
198
|
+
message('Task added to Today.', 'success');
|
|
199
|
+
} else if (operation.kind === 'complete') {
|
|
200
|
+
message(operation.completed ? 'Task completed. You can uncheck it below.' : 'Task marked incomplete.', 'success');
|
|
201
|
+
} else {
|
|
202
|
+
delete drafts[operation.id];
|
|
203
|
+
message('Changes saved.', 'success');
|
|
204
|
+
if (selected === operation.id) detailMessage('Changes saved.', 'success');
|
|
205
|
+
}
|
|
206
|
+
render();
|
|
207
|
+
if (operation.kind === 'add' && !$('detail').open) $('task-title').focus({preventScroll:true});
|
|
208
|
+
if (operation.kind === 'complete' && !$('detail').open) {
|
|
209
|
+
const target = [...document.querySelectorAll('[data-task-id]')].find(el => el.dataset.taskId === operation.id);
|
|
210
|
+
const focusTarget = operation.completed && !showCompleted ? $('completed-toggle') : target?.querySelector('.check');
|
|
211
|
+
(focusTarget || $('task-title')).focus({preventScroll:true});
|
|
212
|
+
}
|
|
213
|
+
} catch (_) {
|
|
214
|
+
pending = null; failedOperation = operation;
|
|
215
|
+
message(operation.kind === 'add' ? 'Task wasn’t saved. Your title is kept. Try again.' : 'Changes weren’t saved. Try again.', 'error', operation.kind !== 'add');
|
|
216
|
+
if (operation.kind === 'edit' && selected === operation.id) detailMessage('Changes weren’t saved. Your edits are kept. Choose Save changes to retry.','error');
|
|
217
|
+
render();
|
|
218
|
+
}
|
|
219
|
+
}, delay);
|
|
220
|
+
}
|
|
221
|
+
$('capture').addEventListener('submit', event => {
|
|
222
|
+
event.preventDefault(); if (pending || loading) return;
|
|
223
|
+
const title = $('task-title').value.trim();
|
|
224
|
+
if (!title) { message('Task title: enter something you need to do.','error'); $('task-title').setAttribute('aria-invalid','true'); $('task-title').focus(); return; }
|
|
225
|
+
$('task-title').removeAttribute('aria-invalid');
|
|
226
|
+
save({kind:'add',task:{id:`new-${Date.now()}-${++sequence}`,title,notes:'',dueDate:TODAY,completed:false}});
|
|
227
|
+
});
|
|
228
|
+
$('task-title').addEventListener('input', () => {
|
|
229
|
+
$('task-title').removeAttribute('aria-invalid');
|
|
230
|
+
if (failedOperation?.kind === 'add') { failedOperation = null; $('add-task').textContent='Add'; }
|
|
231
|
+
});
|
|
232
|
+
$('retry').addEventListener('click',() => { if (failedOperation) save(failedOperation); });
|
|
233
|
+
$('empty-add').addEventListener('click',() => $('task-title').focus({preventScroll:true}));
|
|
234
|
+
$('completed-toggle').addEventListener('click',() => { showCompleted = !showCompleted; render(); $('completed-toggle').focus({preventScroll:true}); });
|
|
235
|
+
document.querySelectorAll('[data-view]').forEach(button => button.addEventListener('click',() => {view = button.dataset.view; render(); window.scrollTo(0,0);}));
|
|
236
|
+
function openDetail(id, source) {
|
|
237
|
+
const task = tasks.find(t => t.id === id); if (!task) return;
|
|
238
|
+
selected = id; opener = source; sourceScroll = window.scrollY;
|
|
239
|
+
const values = drafts[id] || task;
|
|
240
|
+
$('detail-title').value = values.title; $('detail-notes').value = values.notes; $('detail-date').value = values.dueDate;
|
|
241
|
+
$('detail-title').removeAttribute('aria-invalid');
|
|
242
|
+
detailMessage(drafts[id] ? 'Your unsaved edits are here.' : task.completed ? 'Completed. You can still update the details.' : 'Make a little space for the details.');
|
|
243
|
+
$('detail').showModal(); document.body.style.overflow='hidden';
|
|
244
|
+
$('close-detail').focus({preventScroll:true}); render();
|
|
245
|
+
}
|
|
246
|
+
function preserveDraft() {
|
|
247
|
+
if (!selected) return;
|
|
248
|
+
const task=tasks.find(t => t.id === selected);
|
|
249
|
+
const values={title:$('detail-title').value, notes:$('detail-notes').value, dueDate:$('detail-date').value};
|
|
250
|
+
if (task && (values.title!==task.title || values.notes!==task.notes || values.dueDate!==task.dueDate)) drafts[selected]=values;
|
|
251
|
+
else delete drafts[selected];
|
|
252
|
+
}
|
|
253
|
+
function closeDetail() {
|
|
254
|
+
preserveDraft(); const returnId=selected;
|
|
255
|
+
$('detail').close(); document.body.style.overflow=''; selected=null;
|
|
256
|
+
window.scrollTo(0,sourceScroll);
|
|
257
|
+
const target = opener?.isConnected ? opener : [...document.querySelectorAll('[data-open-id]')].find(el => el.dataset.openId===returnId);
|
|
258
|
+
(target || $('task-title')).focus({preventScroll:true});
|
|
259
|
+
}
|
|
260
|
+
$('close-detail').addEventListener('click',closeDetail); $('detail-cancel').addEventListener('click',closeDetail);
|
|
261
|
+
$('detail').addEventListener('cancel', event => {event.preventDefault(); closeDetail();});
|
|
262
|
+
$('detail').addEventListener('click', event => { if(event.target === $('detail')) {const r=$('detail').getBoundingClientRect(); if(event.clientX<r.left || event.clientX>r.right || event.clientY<r.top || event.clientY>r.bottom) closeDetail();} });
|
|
263
|
+
$('detail-form').addEventListener('input',preserveDraft);
|
|
264
|
+
$('detail-form').addEventListener('submit', event => {
|
|
265
|
+
event.preventDefault(); if(pending) return;
|
|
266
|
+
const title=$('detail-title').value.trim();
|
|
267
|
+
if(!title) {detailMessage('Task title: enter a name before saving.','error'); $('detail-title').setAttribute('aria-invalid','true'); $('detail-title').focus(); return;}
|
|
268
|
+
$('detail-title').removeAttribute('aria-invalid');
|
|
269
|
+
preserveDraft();
|
|
270
|
+
save({kind:'edit',id:selected,fields:{title,notes:$('detail-notes').value,dueDate:$('detail-date').value}});
|
|
271
|
+
});
|
|
272
|
+
window.__benchmark = {
|
|
273
|
+
reset(mode) {
|
|
274
|
+
if (!['seed','empty'].includes(mode)) throw new Error('Use seed or empty');
|
|
275
|
+
epoch++; pending=null; failedOperation=null; failNext=false; delay=500; sequence=0;
|
|
276
|
+
tasks=mode==='seed'?clone(SEED):[]; loading=false; view='today'; showCompleted=true; drafts={};
|
|
277
|
+
if($('detail').open) $('detail').close(); selected=null; opener=null; document.body.style.overflow='';
|
|
278
|
+
$('task-title').value=''; $('task-title').removeAttribute('aria-invalid');
|
|
279
|
+
try {localStorage.setItem(STORE,JSON.stringify(tasks));} catch (_) {}
|
|
280
|
+
message('Tasks stay on this device.'); render(); window.scrollTo(0,0);
|
|
281
|
+
return this.getState();
|
|
282
|
+
},
|
|
283
|
+
setSaveDelay(milliseconds) { delay=Math.max(0,Number(milliseconds)||0); },
|
|
284
|
+
failNextSave() { failNext=true; },
|
|
285
|
+
getState() { return clone({tasks,view,loading,selected,status,pending,failedOperation,saveDelay:delay,failNextSave:failNext,captureDraft:$('task-title').value,detailDrafts:drafts,showCompleted}); }
|
|
286
|
+
};
|
|
287
|
+
render();
|
|
288
|
+
const initialEpoch=epoch;
|
|
289
|
+
setTimeout(() => { if(initialEpoch===epoch) {loading=false;render();} },350);
|