opencode-knowledge-work 0.0.0-stage → 0.1.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 (472) hide show
  1. package/LICENSE +212 -0
  2. package/README.md +124 -2
  3. package/content/bio-research/.mcp.json +48 -0
  4. package/content/bio-research/CONNECTORS.md +23 -0
  5. package/content/bio-research/LICENSE +202 -0
  6. package/content/bio-research/README.md +83 -0
  7. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/LICENSE.txt +201 -0
  8. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/SKILL.md +280 -0
  9. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/references/asm_schema_overview.md +226 -0
  10. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/references/field_classification_guide.md +503 -0
  11. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/references/flattening_guide.md +254 -0
  12. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/references/supported_instruments.md +151 -0
  13. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/requirements.txt +26 -0
  14. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/scripts/convert_to_asm.py +543 -0
  15. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/scripts/export_parser.py +481 -0
  16. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/scripts/flatten_asm.py +254 -0
  17. package/content/bio-research/skills/bio-research-instrument-data-to-allotrope/scripts/validate_asm.py +1102 -0
  18. package/content/bio-research/skills/bio-research-nextflow-development/LICENSE.txt +201 -0
  19. package/content/bio-research/skills/bio-research-nextflow-development/SKILL.md +290 -0
  20. package/content/bio-research/skills/bio-research-nextflow-development/references/geo-sra-acquisition.md +416 -0
  21. package/content/bio-research/skills/bio-research-nextflow-development/references/installation.md +96 -0
  22. package/content/bio-research/skills/bio-research-nextflow-development/references/pipelines/atacseq.md +138 -0
  23. package/content/bio-research/skills/bio-research-nextflow-development/references/pipelines/rnaseq.md +118 -0
  24. package/content/bio-research/skills/bio-research-nextflow-development/references/pipelines/sarek.md +145 -0
  25. package/content/bio-research/skills/bio-research-nextflow-development/references/troubleshooting.md +137 -0
  26. package/content/bio-research/skills/bio-research-nextflow-development/scripts/check_environment.py +452 -0
  27. package/content/bio-research/skills/bio-research-nextflow-development/scripts/config/genomes.yaml +148 -0
  28. package/content/bio-research/skills/bio-research-nextflow-development/scripts/config/pipelines/atacseq.yaml +187 -0
  29. package/content/bio-research/skills/bio-research-nextflow-development/scripts/config/pipelines/rnaseq.yaml +147 -0
  30. package/content/bio-research/skills/bio-research-nextflow-development/scripts/config/pipelines/sarek.yaml +233 -0
  31. package/content/bio-research/skills/bio-research-nextflow-development/scripts/detect_data_type.py +300 -0
  32. package/content/bio-research/skills/bio-research-nextflow-development/scripts/generate_samplesheet.py +455 -0
  33. package/content/bio-research/skills/bio-research-nextflow-development/scripts/manage_genomes.py +521 -0
  34. package/content/bio-research/skills/bio-research-nextflow-development/scripts/sra_geo_fetch.py +732 -0
  35. package/content/bio-research/skills/bio-research-nextflow-development/scripts/utils/__init__.py +69 -0
  36. package/content/bio-research/skills/bio-research-nextflow-development/scripts/utils/file_discovery.py +189 -0
  37. package/content/bio-research/skills/bio-research-nextflow-development/scripts/utils/ncbi_utils.py +808 -0
  38. package/content/bio-research/skills/bio-research-nextflow-development/scripts/utils/sample_inference.py +290 -0
  39. package/content/bio-research/skills/bio-research-nextflow-development/scripts/utils/validators.py +256 -0
  40. package/content/bio-research/skills/bio-research-scientific-problem-selection/LICENSE.txt +201 -0
  41. package/content/bio-research/skills/bio-research-scientific-problem-selection/SKILL.md +269 -0
  42. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/01-intuition-pumps.md +264 -0
  43. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/02-risk-assessment.md +323 -0
  44. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/03-optimization-function.md +481 -0
  45. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/04-parameter-strategy.md +396 -0
  46. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/05-decision-tree.md +85 -0
  47. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/06-adversity-planning.md +123 -0
  48. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/07-problem-inversion.md +152 -0
  49. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/08-integration-synthesis.md +173 -0
  50. package/content/bio-research/skills/bio-research-scientific-problem-selection/references/09-meta-framework.md +504 -0
  51. package/content/bio-research/skills/bio-research-scvi-tools/LICENSE.txt +201 -0
  52. package/content/bio-research/skills/bio-research-scvi-tools/SKILL.md +155 -0
  53. package/content/bio-research/skills/bio-research-scvi-tools/references/atac_peakvi.md +398 -0
  54. package/content/bio-research/skills/bio-research-scvi-tools/references/batch_correction_sysvi.md +417 -0
  55. package/content/bio-research/skills/bio-research-scvi-tools/references/citeseq_totalvi.md +420 -0
  56. package/content/bio-research/skills/bio-research-scvi-tools/references/data_preparation.md +286 -0
  57. package/content/bio-research/skills/bio-research-scvi-tools/references/environment_setup.md +254 -0
  58. package/content/bio-research/skills/bio-research-scvi-tools/references/label_transfer.md +373 -0
  59. package/content/bio-research/skills/bio-research-scvi-tools/references/multiome_multivi.md +384 -0
  60. package/content/bio-research/skills/bio-research-scvi-tools/references/rna_velocity_velovi.md +410 -0
  61. package/content/bio-research/skills/bio-research-scvi-tools/references/scarches_mapping.md +401 -0
  62. package/content/bio-research/skills/bio-research-scvi-tools/references/scrna_integration.md +429 -0
  63. package/content/bio-research/skills/bio-research-scvi-tools/references/spatial_deconvolution.md +438 -0
  64. package/content/bio-research/skills/bio-research-scvi-tools/references/troubleshooting.md +420 -0
  65. package/content/bio-research/skills/bio-research-scvi-tools/scripts/cluster_embed.py +212 -0
  66. package/content/bio-research/skills/bio-research-scvi-tools/scripts/differential_expression.py +220 -0
  67. package/content/bio-research/skills/bio-research-scvi-tools/scripts/integrate_datasets.py +237 -0
  68. package/content/bio-research/skills/bio-research-scvi-tools/scripts/model_utils.py +634 -0
  69. package/content/bio-research/skills/bio-research-scvi-tools/scripts/prepare_data.py +169 -0
  70. package/content/bio-research/skills/bio-research-scvi-tools/scripts/train_model.py +370 -0
  71. package/content/bio-research/skills/bio-research-scvi-tools/scripts/transfer_labels.py +224 -0
  72. package/content/bio-research/skills/bio-research-scvi-tools/scripts/validate_adata.py +397 -0
  73. package/content/bio-research/skills/bio-research-single-cell-rna-qc/LICENSE.txt +201 -0
  74. package/content/bio-research/skills/bio-research-single-cell-rna-qc/SKILL.md +175 -0
  75. package/content/bio-research/skills/bio-research-single-cell-rna-qc/references/scverse_qc_guidelines.md +186 -0
  76. package/content/bio-research/skills/bio-research-single-cell-rna-qc/scripts/qc_analysis.py +232 -0
  77. package/content/bio-research/skills/bio-research-single-cell-rna-qc/scripts/qc_core.py +233 -0
  78. package/content/bio-research/skills/bio-research-single-cell-rna-qc/scripts/qc_plotting.py +235 -0
  79. package/content/bio-research/skills/bio-research-start/SKILL.md +79 -0
  80. package/content/customer-support/.mcp.json +40 -0
  81. package/content/customer-support/CONNECTORS.md +19 -0
  82. package/content/customer-support/LICENSE +202 -0
  83. package/content/customer-support/README.md +140 -0
  84. package/content/customer-support/skills/customer-support-customer-escalation/SKILL.md +248 -0
  85. package/content/customer-support/skills/customer-support-customer-research/SKILL.md +252 -0
  86. package/content/customer-support/skills/customer-support-draft-response/SKILL.md +418 -0
  87. package/content/customer-support/skills/customer-support-kb-article/SKILL.md +351 -0
  88. package/content/customer-support/skills/customer-support-ticket-triage/SKILL.md +275 -0
  89. package/content/data/.mcp.json +36 -0
  90. package/content/data/CONNECTORS.md +18 -0
  91. package/content/data/LICENSE +202 -0
  92. package/content/data/README.md +121 -0
  93. package/content/data/skills/data-analyze/SKILL.md +119 -0
  94. package/content/data/skills/data-build-dashboard/SKILL.md +924 -0
  95. package/content/data/skills/data-create-viz/SKILL.md +154 -0
  96. package/content/data/skills/data-data-context-extractor/SKILL.md +227 -0
  97. package/content/data/skills/data-data-context-extractor/references/domain-template.md +147 -0
  98. package/content/data/skills/data-data-context-extractor/references/example-output.md +198 -0
  99. package/content/data/skills/data-data-context-extractor/references/skill-template.md +148 -0
  100. package/content/data/skills/data-data-context-extractor/references/sql-dialects.md +121 -0
  101. package/content/data/skills/data-data-context-extractor/scripts/package_data_skill.py +126 -0
  102. package/content/data/skills/data-data-visualization/SKILL.md +305 -0
  103. package/content/data/skills/data-explore-data/SKILL.md +325 -0
  104. package/content/data/skills/data-sql-queries/SKILL.md +428 -0
  105. package/content/data/skills/data-statistical-analysis/SKILL.md +245 -0
  106. package/content/data/skills/data-validate-data/SKILL.md +383 -0
  107. package/content/data/skills/data-write-query/SKILL.md +122 -0
  108. package/content/design/.mcp.json +44 -0
  109. package/content/design/CONNECTORS.md +18 -0
  110. package/content/design/README.md +117 -0
  111. package/content/design/skills/design-accessibility-review/SKILL.md +128 -0
  112. package/content/design/skills/design-design-critique/SKILL.md +118 -0
  113. package/content/design/skills/design-design-handoff/SKILL.md +131 -0
  114. package/content/design/skills/design-design-system/SKILL.md +190 -0
  115. package/content/design/skills/design-research-synthesis/SKILL.md +92 -0
  116. package/content/design/skills/design-user-research/SKILL.md +41 -0
  117. package/content/design/skills/design-ux-copy/SKILL.md +107 -0
  118. package/content/engineering/.mcp.json +48 -0
  119. package/content/engineering/CONNECTORS.md +19 -0
  120. package/content/engineering/README.md +136 -0
  121. package/content/engineering/skills/engineering-architecture/SKILL.md +85 -0
  122. package/content/engineering/skills/engineering-code-review/SKILL.md +118 -0
  123. package/content/engineering/skills/engineering-debug/SKILL.md +95 -0
  124. package/content/engineering/skills/engineering-deploy-checklist/SKILL.md +78 -0
  125. package/content/engineering/skills/engineering-documentation/SKILL.md +49 -0
  126. package/content/engineering/skills/engineering-incident-response/SKILL.md +158 -0
  127. package/content/engineering/skills/engineering-standup/SKILL.md +75 -0
  128. package/content/engineering/skills/engineering-system-design/SKILL.md +42 -0
  129. package/content/engineering/skills/engineering-tech-debt/SKILL.md +32 -0
  130. package/content/engineering/skills/engineering-testing-strategy/SKILL.md +33 -0
  131. package/content/enterprise-search/.mcp.json +36 -0
  132. package/content/enterprise-search/CONNECTORS.md +21 -0
  133. package/content/enterprise-search/LICENSE +202 -0
  134. package/content/enterprise-search/README.md +157 -0
  135. package/content/enterprise-search/skills/enterprise-search-digest/SKILL.md +173 -0
  136. package/content/enterprise-search/skills/enterprise-search-knowledge-synthesis/SKILL.md +258 -0
  137. package/content/enterprise-search/skills/enterprise-search-search/SKILL.md +178 -0
  138. package/content/enterprise-search/skills/enterprise-search-search-strategy/SKILL.md +222 -0
  139. package/content/enterprise-search/skills/enterprise-search-source-management/SKILL.md +173 -0
  140. package/content/finance/.mcp.json +32 -0
  141. package/content/finance/CONNECTORS.md +20 -0
  142. package/content/finance/LICENSE +202 -0
  143. package/content/finance/README.md +92 -0
  144. package/content/finance/skills/finance-audit-support/SKILL.md +374 -0
  145. package/content/finance/skills/finance-close-management/SKILL.md +221 -0
  146. package/content/finance/skills/finance-financial-statements/SKILL.md +335 -0
  147. package/content/finance/skills/finance-journal-entry/SKILL.md +131 -0
  148. package/content/finance/skills/finance-journal-entry-prep/SKILL.md +186 -0
  149. package/content/finance/skills/finance-reconciliation/SKILL.md +175 -0
  150. package/content/finance/skills/finance-sox-testing/SKILL.md +218 -0
  151. package/content/finance/skills/finance-variance-analysis/SKILL.md +266 -0
  152. package/content/human-resources/.mcp.json +28 -0
  153. package/content/human-resources/CONNECTORS.md +19 -0
  154. package/content/human-resources/README.md +138 -0
  155. package/content/human-resources/skills/human-resources-comp-analysis/SKILL.md +92 -0
  156. package/content/human-resources/skills/human-resources-draft-offer/SKILL.md +82 -0
  157. package/content/human-resources/skills/human-resources-interview-prep/SKILL.md +37 -0
  158. package/content/human-resources/skills/human-resources-onboarding/SKILL.md +104 -0
  159. package/content/human-resources/skills/human-resources-org-planning/SKILL.md +28 -0
  160. package/content/human-resources/skills/human-resources-people-report/SKILL.md +99 -0
  161. package/content/human-resources/skills/human-resources-performance-review/SKILL.md +149 -0
  162. package/content/human-resources/skills/human-resources-policy-lookup/SKILL.md +94 -0
  163. package/content/human-resources/skills/human-resources-recruiting-pipeline/SKILL.md +31 -0
  164. package/content/legal/.mcp.json +36 -0
  165. package/content/legal/CONNECTORS.md +21 -0
  166. package/content/legal/LICENSE +202 -0
  167. package/content/legal/README.md +234 -0
  168. package/content/legal/skills/legal-brief/SKILL.md +208 -0
  169. package/content/legal/skills/legal-compliance-check/SKILL.md +274 -0
  170. package/content/legal/skills/legal-legal-response/SKILL.md +457 -0
  171. package/content/legal/skills/legal-legal-risk-assessment/SKILL.md +265 -0
  172. package/content/legal/skills/legal-meeting-briefing/SKILL.md +220 -0
  173. package/content/legal/skills/legal-review-contract/SKILL.md +358 -0
  174. package/content/legal/skills/legal-signature-request/SKILL.md +104 -0
  175. package/content/legal/skills/legal-triage-nda/SKILL.md +262 -0
  176. package/content/legal/skills/legal-vendor-check/SKILL.md +159 -0
  177. package/content/manifest.json +86 -0
  178. package/content/marketing/.mcp.json +60 -0
  179. package/content/marketing/CONNECTORS.md +20 -0
  180. package/content/marketing/LICENSE +202 -0
  181. package/content/marketing/README.md +90 -0
  182. package/content/marketing/skills/marketing-brand-review/SKILL.md +276 -0
  183. package/content/marketing/skills/marketing-campaign-plan/SKILL.md +308 -0
  184. package/content/marketing/skills/marketing-competitive-brief/SKILL.md +331 -0
  185. package/content/marketing/skills/marketing-content-creation/SKILL.md +157 -0
  186. package/content/marketing/skills/marketing-draft-content/SKILL.md +117 -0
  187. package/content/marketing/skills/marketing-email-sequence/SKILL.md +220 -0
  188. package/content/marketing/skills/marketing-performance-report/SKILL.md +392 -0
  189. package/content/marketing/skills/marketing-seo-audit/SKILL.md +190 -0
  190. package/content/operations/.mcp.json +32 -0
  191. package/content/operations/CONNECTORS.md +20 -0
  192. package/content/operations/README.md +135 -0
  193. package/content/operations/skills/operations-capacity-plan/SKILL.md +113 -0
  194. package/content/operations/skills/operations-change-request/SKILL.md +121 -0
  195. package/content/operations/skills/operations-compliance-tracking/SKILL.md +44 -0
  196. package/content/operations/skills/operations-process-doc/SKILL.md +82 -0
  197. package/content/operations/skills/operations-process-optimization/SKILL.md +39 -0
  198. package/content/operations/skills/operations-risk-assessment/SKILL.md +40 -0
  199. package/content/operations/skills/operations-runbook/SKILL.md +87 -0
  200. package/content/operations/skills/operations-status-report/SKILL.md +77 -0
  201. package/content/operations/skills/operations-vendor-review/SKILL.md +104 -0
  202. package/content/pdf-viewer/.mcp.json +8 -0
  203. package/content/pdf-viewer/CONNECTORS.md +15 -0
  204. package/content/pdf-viewer/LICENSE +202 -0
  205. package/content/pdf-viewer/README.md +57 -0
  206. package/content/pdf-viewer/commands/annotate.md +51 -0
  207. package/content/pdf-viewer/commands/fill-form.md +73 -0
  208. package/content/pdf-viewer/commands/open.md +34 -0
  209. package/content/pdf-viewer/commands/sign.md +54 -0
  210. package/content/pdf-viewer/skills/pdf-viewer-view-pdf/SKILL.md +147 -0
  211. package/content/product-management/.mcp.json +72 -0
  212. package/content/product-management/CONNECTORS.md +22 -0
  213. package/content/product-management/LICENSE +202 -0
  214. package/content/product-management/README.md +118 -0
  215. package/content/product-management/commands/brainstorm.md +120 -0
  216. package/content/product-management/skills/product-management-competitive-brief/SKILL.md +302 -0
  217. package/content/product-management/skills/product-management-metrics-review/SKILL.md +388 -0
  218. package/content/product-management/skills/product-management-product-brainstorming/SKILL.md +273 -0
  219. package/content/product-management/skills/product-management-roadmap-update/SKILL.md +266 -0
  220. package/content/product-management/skills/product-management-sprint-planning/SKILL.md +94 -0
  221. package/content/product-management/skills/product-management-stakeholder-update/SKILL.md +352 -0
  222. package/content/product-management/skills/product-management-synthesize-research/SKILL.md +312 -0
  223. package/content/product-management/skills/product-management-write-spec/SKILL.md +250 -0
  224. package/content/productivity/.mcp.json +44 -0
  225. package/content/productivity/CONNECTORS.md +18 -0
  226. package/content/productivity/LICENSE +202 -0
  227. package/content/productivity/README.md +97 -0
  228. package/content/productivity/skills/dashboard.html +3111 -0
  229. package/content/productivity/skills/productivity-memory-management/SKILL.md +323 -0
  230. package/content/productivity/skills/productivity-start/SKILL.md +158 -0
  231. package/content/productivity/skills/productivity-task-management/SKILL.md +91 -0
  232. package/content/productivity/skills/productivity-update/SKILL.md +168 -0
  233. package/content/sales/.mcp.json +100 -0
  234. package/content/sales/CONNECTORS.md +36 -0
  235. package/content/sales/LICENSE +202 -0
  236. package/content/sales/README.md +148 -0
  237. package/content/sales/skills/sales-account-context/SKILL.md +94 -0
  238. package/content/sales/skills/sales-account-plan/SKILL.md +95 -0
  239. package/content/sales/skills/sales-account-research/SKILL.md +99 -0
  240. package/content/sales/skills/sales-account-tiering/SKILL.md +101 -0
  241. package/content/sales/skills/sales-call-prep/SKILL.md +111 -0
  242. package/content/sales/skills/sales-call-summary/SKILL.md +111 -0
  243. package/content/sales/skills/sales-close-plan/SKILL.md +106 -0
  244. package/content/sales/skills/sales-competitive-intelligence/SKILL.md +102 -0
  245. package/content/sales/skills/sales-create-an-asset/SKILL.md +105 -0
  246. package/content/sales/skills/sales-crm-hygiene-check/SKILL.md +103 -0
  247. package/content/sales/skills/sales-customer-health/SKILL.md +108 -0
  248. package/content/sales/skills/sales-customer-voice/SKILL.md +94 -0
  249. package/content/sales/skills/sales-daily-briefing/SKILL.md +61 -0
  250. package/content/sales/skills/sales-deal-advance-gap/SKILL.md +102 -0
  251. package/content/sales/skills/sales-deal-review/SKILL.md +105 -0
  252. package/content/sales/skills/sales-deal-signals/SKILL.md +96 -0
  253. package/content/sales/skills/sales-deal-slip-scenario/SKILL.md +101 -0
  254. package/content/sales/skills/sales-draft-outreach/SKILL.md +116 -0
  255. package/content/sales/skills/sales-end-of-day/SKILL.md +114 -0
  256. package/content/sales/skills/sales-expansion-whitespace/SKILL.md +97 -0
  257. package/content/sales/skills/sales-forecast/SKILL.md +100 -0
  258. package/content/sales/skills/sales-handle-objection/SKILL.md +93 -0
  259. package/content/sales/skills/sales-inbox-sweep/SKILL.md +114 -0
  260. package/content/sales/skills/sales-lead-triage/SKILL.md +105 -0
  261. package/content/sales/skills/sales-log-activity/SKILL.md +111 -0
  262. package/content/sales/skills/sales-pipeline-review/SKILL.md +97 -0
  263. package/content/sales/skills/sales-renewal-radar/SKILL.md +95 -0
  264. package/content/sales/skills/sales-rep-context/SKILL.md +90 -0
  265. package/content/sales/skills/sales-route-lead/SKILL.md +111 -0
  266. package/content/sales/skills/sales-schedule-meeting/SKILL.md +99 -0
  267. package/content/sales/skills/sales-setup/SKILL.md +195 -0
  268. package/content/sales/skills/sales-stakeholder-map/SKILL.md +107 -0
  269. package/content/sales/skills/sales-team-pipeline/SKILL.md +105 -0
  270. package/content/sales/skills/sales-update-opportunity/SKILL.md +111 -0
  271. package/content/sales/skills/sales-weekly-wrap/SKILL.md +90 -0
  272. package/content/sales/skills/sales-win-loss-review/SKILL.md +88 -0
  273. package/content/small-business/.mcp.json +148 -0
  274. package/content/small-business/README.md +117 -0
  275. package/content/small-business/skills/small-business-ad-manager/SKILL.md +128 -0
  276. package/content/small-business/skills/small-business-ad-manager/reference/ad_copy.md +112 -0
  277. package/content/small-business/skills/small-business-ad-manager/reference/change_playbook.md +127 -0
  278. package/content/small-business/skills/small-business-ad-manager/reference/data_sources.md +100 -0
  279. package/content/small-business/skills/small-business-ad-manager/reference/diagnostics.md +89 -0
  280. package/content/small-business/skills/small-business-ad-manager/reference/gotchas.md +181 -0
  281. package/content/small-business/skills/small-business-ap-processor/SKILL.md +158 -0
  282. package/content/small-business/skills/small-business-ap-processor/reference/coding_rules.md +70 -0
  283. package/content/small-business/skills/small-business-ap-processor/reference/gotchas.md +131 -0
  284. package/content/small-business/skills/small-business-ap-processor/reference/intake_and_extraction.md +82 -0
  285. package/content/small-business/skills/small-business-ap-processor/reference/matching_and_exceptions.md +80 -0
  286. package/content/small-business/skills/small-business-ap-processor/reference/payment_run.md +77 -0
  287. package/content/small-business/skills/small-business-brand-style/SKILL.md +106 -0
  288. package/content/small-business/skills/small-business-build-agent/SKILL.md +105 -0
  289. package/content/small-business/skills/small-business-build-agent/reference/capture.md +133 -0
  290. package/content/small-business/skills/small-business-build-agent/reference/gotchas.md +204 -0
  291. package/content/small-business/skills/small-business-build-agent/reference/skill_authoring.md +165 -0
  292. package/content/small-business/skills/small-business-build-connector/SKILL.md +102 -0
  293. package/content/small-business/skills/small-business-build-connector/reference/discovery.md +120 -0
  294. package/content/small-business/skills/small-business-build-connector/reference/gotchas.md +237 -0
  295. package/content/small-business/skills/small-business-build-connector/reference/paths.md +106 -0
  296. package/content/small-business/skills/small-business-build-connector/reference/use-zapier.md +128 -0
  297. package/content/small-business/skills/small-business-business-pulse/SKILL.md +181 -0
  298. package/content/small-business/skills/small-business-business-pulse/reference/data_sources.md +173 -0
  299. package/content/small-business/skills/small-business-business-pulse/reference/gotchas.md +103 -0
  300. package/content/small-business/skills/small-business-business-pulse/reference/output_template.md +109 -0
  301. package/content/small-business/skills/small-business-business-pulse/reference/thresholds.md +71 -0
  302. package/content/small-business/skills/small-business-business-pulse/reference/v2_sources.md +63 -0
  303. package/content/small-business/skills/small-business-call-list/SKILL.md +82 -0
  304. package/content/small-business/skills/small-business-canva-creator/SKILL.md +448 -0
  305. package/content/small-business/skills/small-business-canva-creator/reference/canva-api.md +208 -0
  306. package/content/small-business/skills/small-business-canva-creator/reference/examples/boutique-brief-campaign.md +181 -0
  307. package/content/small-business/skills/small-business-canva-creator/reference/gotchas.md +380 -0
  308. package/content/small-business/skills/small-business-canva-creator/reference/hubspot-staging.md +137 -0
  309. package/content/small-business/skills/small-business-cash-flow-snapshot/SKILL.md +251 -0
  310. package/content/small-business/skills/small-business-cash-flow-snapshot/reference/examples/worked-example.md +92 -0
  311. package/content/small-business/skills/small-business-cash-flow-snapshot/reference/gotchas.md +62 -0
  312. package/content/small-business/skills/small-business-cash-flow-snapshot/reference/v2_sources.md +84 -0
  313. package/content/small-business/skills/small-business-close-month/SKILL.md +86 -0
  314. package/content/small-business/skills/small-business-content-strategy/SKILL.md +152 -0
  315. package/content/small-business/skills/small-business-content-strategy/reference/examples/retail-boutique-example.md +71 -0
  316. package/content/small-business/skills/small-business-content-strategy/reference/gotchas.md +104 -0
  317. package/content/small-business/skills/small-business-content-strategy/reference/v2_sources.md +40 -0
  318. package/content/small-business/skills/small-business-contract-review/SKILL.md +158 -0
  319. package/content/small-business/skills/small-business-contract-review/reference/docusign-fetch.md +32 -0
  320. package/content/small-business/skills/small-business-contract-review/reference/examples/flagged-summary-saas.md +97 -0
  321. package/content/small-business/skills/small-business-contract-review/reference/gmail-fetch.md +30 -0
  322. package/content/small-business/skills/small-business-contract-review/reference/gotchas.md +105 -0
  323. package/content/small-business/skills/small-business-contract-review/reference/m365-fetch.md +60 -0
  324. package/content/small-business/skills/small-business-crm-autopilot/SKILL.md +162 -0
  325. package/content/small-business/skills/small-business-crm-autopilot/reference/cleanup-checklist.md +82 -0
  326. package/content/small-business/skills/small-business-crm-autopilot/reference/examples/cleanup-deal.md +83 -0
  327. package/content/small-business/skills/small-business-crm-autopilot/reference/examples/log-call-happy-path.md +68 -0
  328. package/content/small-business/skills/small-business-crm-autopilot/reference/examples/log-email-happy-path.md +50 -0
  329. package/content/small-business/skills/small-business-crm-autopilot/reference/gotchas.md +110 -0
  330. package/content/small-business/skills/small-business-crm-autopilot/reference/hubspot-fields.md +62 -0
  331. package/content/small-business/skills/small-business-crm-autopilot/reference/lightweight_crm.md +125 -0
  332. package/content/small-business/skills/small-business-crm-autopilot/reference/salesforce-fields.md +122 -0
  333. package/content/small-business/skills/small-business-crm-autopilot/reference/standing_mode.md +144 -0
  334. package/content/small-business/skills/small-business-grant-rfp-writer/SKILL.md +162 -0
  335. package/content/small-business/skills/small-business-grant-rfp-writer/reference/drafting.md +129 -0
  336. package/content/small-business/skills/small-business-grant-rfp-writer/reference/go_no_go.md +125 -0
  337. package/content/small-business/skills/small-business-grant-rfp-writer/reference/gotchas.md +180 -0
  338. package/content/small-business/skills/small-business-grant-rfp-writer/reference/opportunity_sources.md +90 -0
  339. package/content/small-business/skills/small-business-grant-rfp-writer/reference/org_profile.md +81 -0
  340. package/content/small-business/skills/small-business-grow-pipeline/SKILL.md +92 -0
  341. package/content/small-business/skills/small-business-growth-pulse/SKILL.md +143 -0
  342. package/content/small-business/skills/small-business-growth-pulse/reference/data_sources.md +113 -0
  343. package/content/small-business/skills/small-business-growth-pulse/reference/gotchas.md +175 -0
  344. package/content/small-business/skills/small-business-growth-pulse/reference/output_template.md +122 -0
  345. package/content/small-business/skills/small-business-growth-pulse/reference/thresholds.md +110 -0
  346. package/content/small-business/skills/small-business-hiring-screener/SKILL.md +137 -0
  347. package/content/small-business/skills/small-business-hiring-screener/reference/candidate_comms.md +132 -0
  348. package/content/small-business/skills/small-business-hiring-screener/reference/fair_screening.md +103 -0
  349. package/content/small-business/skills/small-business-hiring-screener/reference/gotchas.md +183 -0
  350. package/content/small-business/skills/small-business-hiring-screener/reference/onboarding.md +82 -0
  351. package/content/small-business/skills/small-business-hiring-screener/reference/rubric.md +122 -0
  352. package/content/small-business/skills/small-business-inbox-manager/SKILL.md +130 -0
  353. package/content/small-business/skills/small-business-inbox-manager/reference/digest_format.md +110 -0
  354. package/content/small-business/skills/small-business-inbox-manager/reference/gotchas.md +220 -0
  355. package/content/small-business/skills/small-business-inbox-manager/reference/handoffs.md +77 -0
  356. package/content/small-business/skills/small-business-inbox-manager/reference/reply_drafting.md +116 -0
  357. package/content/small-business/skills/small-business-inbox-manager/reference/triage_rules.md +72 -0
  358. package/content/small-business/skills/small-business-inventory-planner/SKILL.md +126 -0
  359. package/content/small-business/skills/small-business-inventory-planner/reference/data_sources.md +94 -0
  360. package/content/small-business/skills/small-business-inventory-planner/reference/gotchas.md +153 -0
  361. package/content/small-business/skills/small-business-inventory-planner/reference/po_drafting.md +92 -0
  362. package/content/small-business/skills/small-business-inventory-planner/reference/seasonality.md +112 -0
  363. package/content/small-business/skills/small-business-inventory-planner/reference/velocity_and_reorder.md +154 -0
  364. package/content/small-business/skills/small-business-invoice-chase/SKILL.md +131 -0
  365. package/content/small-business/skills/small-business-invoice-chase/reference/examples/firm-reminder.md +28 -0
  366. package/content/small-business/skills/small-business-invoice-chase/reference/examples/gentle-reminder.md +27 -0
  367. package/content/small-business/skills/small-business-invoice-chase/reference/gotchas.md +75 -0
  368. package/content/small-business/skills/small-business-invoice-chase/reference/tone-matching.md +44 -0
  369. package/content/small-business/skills/small-business-invoice-chase/reference/v2_sources.md +78 -0
  370. package/content/small-business/skills/small-business-job-post-builder/SKILL.md +204 -0
  371. package/content/small-business/skills/small-business-job-post-builder/reference/docusign-routing.md +63 -0
  372. package/content/small-business/skills/small-business-job-post-builder/reference/examples/worked-example.md +110 -0
  373. package/content/small-business/skills/small-business-job-post-builder/reference/existing-document-merge.md +58 -0
  374. package/content/small-business/skills/small-business-job-post-builder/reference/gotchas.md +68 -0
  375. package/content/small-business/skills/small-business-job-post-builder/reference/interview-guide-structure.md +93 -0
  376. package/content/small-business/skills/small-business-job-post-builder/reference/job-post-structure.md +62 -0
  377. package/content/small-business/skills/small-business-job-post-builder/reference/offer-letter-template.md +145 -0
  378. package/content/small-business/skills/small-business-job-post-builder/reference/role-intake.md +73 -0
  379. package/content/small-business/skills/small-business-lead-finder/SKILL.md +119 -0
  380. package/content/small-business/skills/small-business-lead-finder/reference/gotchas.md +152 -0
  381. package/content/small-business/skills/small-business-lead-finder/reference/icp_method.md +97 -0
  382. package/content/small-business/skills/small-business-lead-finder/reference/output_template.md +124 -0
  383. package/content/small-business/skills/small-business-lead-finder/reference/scoring.md +92 -0
  384. package/content/small-business/skills/small-business-lead-finder/reference/sourcing.md +78 -0
  385. package/content/small-business/skills/small-business-lead-triage/SKILL.md +81 -0
  386. package/content/small-business/skills/small-business-lead-triage/reference/examples/happy-path-triage.md +68 -0
  387. package/content/small-business/skills/small-business-lead-triage/reference/gotchas.md +76 -0
  388. package/content/small-business/skills/small-business-lead-triage/reference/hubspot-scoring.md +169 -0
  389. package/content/small-business/skills/small-business-marketing-monday/SKILL.md +108 -0
  390. package/content/small-business/skills/small-business-monday-brief/SKILL.md +97 -0
  391. package/content/small-business/skills/small-business-month-end-prep/SKILL.md +331 -0
  392. package/content/small-business/skills/small-business-month-end-prep/reference/close-packet-format.md +127 -0
  393. package/content/small-business/skills/small-business-month-end-prep/reference/examples/pl-narrative.md +52 -0
  394. package/content/small-business/skills/small-business-month-end-prep/reference/gotchas.md +113 -0
  395. package/content/small-business/skills/small-business-month-end-prep/reference/paypal-settlements.md +110 -0
  396. package/content/small-business/skills/small-business-month-end-prep/reference/quickbooks-reconcile.md +71 -0
  397. package/content/small-business/skills/small-business-month-end-prep/reference/v2_sources.md +125 -0
  398. package/content/small-business/skills/small-business-month-end-prep/reference/xero-reconcile.md +175 -0
  399. package/content/small-business/skills/small-business-month-end-prep/reference/zoho-books-reconcile.md +168 -0
  400. package/content/small-business/skills/small-business-outreach-composer/SKILL.md +144 -0
  401. package/content/small-business/skills/small-business-outreach-composer/reference/gotchas.md +213 -0
  402. package/content/small-business/skills/small-business-outreach-composer/reference/output_template.md +147 -0
  403. package/content/small-business/skills/small-business-outreach-composer/reference/sequence_patterns.md +99 -0
  404. package/content/small-business/skills/small-business-outreach-composer/reference/slop_test.md +164 -0
  405. package/content/small-business/skills/small-business-pay-the-bills/SKILL.md +101 -0
  406. package/content/small-business/skills/small-business-payroll-prep/SKILL.md +139 -0
  407. package/content/small-business/skills/small-business-payroll-prep/reference/anomaly_rules.md +99 -0
  408. package/content/small-business/skills/small-business-payroll-prep/reference/books_sync.md +72 -0
  409. package/content/small-business/skills/small-business-payroll-prep/reference/gotchas.md +153 -0
  410. package/content/small-business/skills/small-business-payroll-prep/reference/run_sheet_format.md +90 -0
  411. package/content/small-business/skills/small-business-payroll-prep/reference/timesheet_intake.md +118 -0
  412. package/content/small-business/skills/small-business-plan-payroll/SKILL.md +88 -0
  413. package/content/small-business/skills/small-business-proposal-builder/SKILL.md +168 -0
  414. package/content/small-business/skills/small-business-proposal-builder/reference/discovery_extraction.md +138 -0
  415. package/content/small-business/skills/small-business-proposal-builder/reference/gotchas.md +184 -0
  416. package/content/small-business/skills/small-business-proposal-builder/reference/output_template.md +128 -0
  417. package/content/small-business/skills/small-business-proposal-builder/reference/pricing_method.md +125 -0
  418. package/content/small-business/skills/small-business-proposal-builder/reference/proposal_structure.md +120 -0
  419. package/content/small-business/skills/small-business-reactivate/SKILL.md +109 -0
  420. package/content/small-business/skills/small-business-report-builder/SKILL.md +142 -0
  421. package/content/small-business/skills/small-business-report-builder/reference/data_sources.md +168 -0
  422. package/content/small-business/skills/small-business-report-builder/reference/gotchas.md +155 -0
  423. package/content/small-business/skills/small-business-report-builder/reference/output_template.md +109 -0
  424. package/content/small-business/skills/small-business-report-builder/reference/report_spec.md +109 -0
  425. package/content/small-business/skills/small-business-report-builder/reference/saved_reports.md +50 -0
  426. package/content/small-business/skills/small-business-report-pack/SKILL.md +83 -0
  427. package/content/small-business/skills/small-business-restock/SKILL.md +88 -0
  428. package/content/small-business/skills/small-business-review-reputation/SKILL.md +130 -0
  429. package/content/small-business/skills/small-business-review-reputation/reference/churn-signals.md +110 -0
  430. package/content/small-business/skills/small-business-review-reputation/reference/examples/example-report.md +150 -0
  431. package/content/small-business/skills/small-business-review-reputation/reference/gotchas.md +164 -0
  432. package/content/small-business/skills/small-business-review-reputation/reference/response-patterns.md +123 -0
  433. package/content/small-business/skills/small-business-review-reputation/reference/sources.md +116 -0
  434. package/content/small-business/skills/small-business-seo-ai-visibility/SKILL.md +143 -0
  435. package/content/small-business/skills/small-business-seo-ai-visibility/reference/ai_visibility.md +173 -0
  436. package/content/small-business/skills/small-business-seo-ai-visibility/reference/audit_checklist.md +134 -0
  437. package/content/small-business/skills/small-business-seo-ai-visibility/reference/catalog_refresh.md +126 -0
  438. package/content/small-business/skills/small-business-seo-ai-visibility/reference/content_rewrite.md +113 -0
  439. package/content/small-business/skills/small-business-seo-ai-visibility/reference/gotchas.md +211 -0
  440. package/content/small-business/skills/small-business-smb-onboard/SKILL.md +82 -0
  441. package/content/small-business/skills/small-business-smb-onboard/reference/examples/happy-path.md +190 -0
  442. package/content/small-business/skills/small-business-smb-onboard/reference/gotchas.md +111 -0
  443. package/content/small-business/skills/small-business-smb-onboard/reference/onboard-checklist.md +99 -0
  444. package/content/small-business/skills/small-business-smb-onboard/reference/website-research.md +82 -0
  445. package/content/small-business/skills/small-business-smb-router/SKILL.md +216 -0
  446. package/content/small-business/skills/small-business-smb-router/reference/connector-map.md +96 -0
  447. package/content/small-business/skills/small-business-social-content-engine/SKILL.md +162 -0
  448. package/content/small-business/skills/small-business-social-content-engine/reference/calendar.md +116 -0
  449. package/content/small-business/skills/small-business-social-content-engine/reference/canva-api.md +208 -0
  450. package/content/small-business/skills/small-business-social-content-engine/reference/examples/okonkwo-campaign.md +190 -0
  451. package/content/small-business/skills/small-business-social-content-engine/reference/gotchas.md +447 -0
  452. package/content/small-business/skills/small-business-social-content-engine/reference/publishing.md +166 -0
  453. package/content/small-business/skills/small-business-social-content-engine/reference/shopify-assets.md +77 -0
  454. package/content/small-business/skills/small-business-speed-to-lead/SKILL.md +172 -0
  455. package/content/small-business/skills/small-business-speed-to-lead/reference/approval.md +61 -0
  456. package/content/small-business/skills/small-business-speed-to-lead/reference/gotchas.md +167 -0
  457. package/content/small-business/skills/small-business-speed-to-lead/reference/qualification.md +153 -0
  458. package/content/small-business/skills/small-business-speed-to-lead/reference/response_patterns.md +184 -0
  459. package/content/small-business/skills/small-business-tax-prep/SKILL.md +85 -0
  460. package/content/small-business/skills/small-business-tax-season-organizer/SKILL.md +210 -0
  461. package/content/small-business/skills/small-business-tax-season-organizer/reference/calculation-assumptions.md +177 -0
  462. package/content/small-business/skills/small-business-tax-season-organizer/reference/connector-queries.md +267 -0
  463. package/content/small-business/skills/small-business-tax-season-organizer/reference/examples/quarterly-estimate.md +92 -0
  464. package/content/small-business/skills/small-business-tax-season-organizer/reference/examples/year-end-1099.md +102 -0
  465. package/content/small-business/skills/small-business-tax-season-organizer/reference/gotchas.md +104 -0
  466. package/content/small-business/skills/small-business-tax-season-organizer/reference/output-formats.md +68 -0
  467. package/content/small-business/skills/small-business-ticket-deflector/SKILL.md +164 -0
  468. package/content/small-business/skills/small-business-ticket-deflector/reference/examples/deny-refund-request.md +98 -0
  469. package/content/small-business/skills/small-business-ticket-deflector/reference/examples/respond-refund-request.md +77 -0
  470. package/content/small-business/skills/small-business-ticket-deflector/reference/gotchas.md +57 -0
  471. package/index.ts +84 -0
  472. package/package.json +30 -3
@@ -0,0 +1,3111 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Productivity</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='4' y='2' width='24' height='28' rx='4' fill='%23faf9f5' stroke='%23D97757' stroke-width='2'/%3E%3Crect x='8' y='0' width='16' height='5' rx='2.5' fill='%23D97757'/%3E%3Cpath d='M9 13l3 3 5-5' fill='none' stroke='%23D97757' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='20' y='12' width='5' height='2' rx='1' fill='%23c4c4c2'/%3E%3Crect x='9' y='20' width='10' height='2' rx='1' fill='%23c4c4c2'/%3E%3Crect x='9' y='25' width='7' height='2' rx='1' fill='%23c4c4c2'/%3E%3C/svg%3E">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
11
+ <style>
12
+ * { box-sizing: border-box; margin: 0; padding: 0; }
13
+
14
+ :root {
15
+ --bg-primary: #faf9f5;
16
+ --bg-secondary: #F5F4EF;
17
+ --bg-card: #ffffff;
18
+ --text-primary: #141413;
19
+ --text-secondary: #5c5c5a;
20
+ --text-muted: #8c8c8a;
21
+ --accent: #D97757;
22
+ --accent-hover: #c4684a;
23
+ --border: #e5e4df;
24
+ --border-light: #eeeee9;
25
+ --shadow: rgba(20, 20, 19, 0.04);
26
+ --shadow-hover: rgba(20, 20, 19, 0.08);
27
+ }
28
+
29
+ body {
30
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
31
+ background: var(--bg-primary);
32
+ color: var(--text-primary);
33
+ height: 100vh;
34
+ padding: 20px 24px;
35
+ line-height: 1.5;
36
+ overflow: hidden;
37
+ display: flex;
38
+ flex-direction: column;
39
+ }
40
+
41
+ header {
42
+ display: flex;
43
+ justify-content: space-between;
44
+ align-items: center;
45
+ margin-bottom: 16px;
46
+ padding-bottom: 16px;
47
+ border-bottom: 1px solid var(--border);
48
+ flex-shrink: 0;
49
+ }
50
+
51
+ h1 {
52
+ font-size: 1.5rem;
53
+ font-weight: 600;
54
+ letter-spacing: -0.02em;
55
+ }
56
+
57
+ h2 { font-size: 1.1rem; font-weight: 600; margin-bottom: 12px; color: var(--text-secondary); }
58
+
59
+ .header-left {
60
+ display: flex;
61
+ align-items: flex-start;
62
+ gap: 0;
63
+ }
64
+
65
+ .view-toggle {
66
+ display: flex;
67
+ gap: 4px;
68
+ background: var(--bg-secondary);
69
+ padding: 4px;
70
+ border-radius: 8px;
71
+ margin-left: 16px;
72
+ }
73
+
74
+ .view-toggle button {
75
+ background: transparent;
76
+ border: none;
77
+ padding: 6px 14px;
78
+ font-size: 13px;
79
+ color: var(--text-muted);
80
+ border-radius: 6px;
81
+ }
82
+
83
+ .view-toggle button:hover {
84
+ color: var(--text-primary);
85
+ background: transparent;
86
+ }
87
+
88
+ .view-toggle button.active {
89
+ background: var(--bg-card);
90
+ color: var(--text-primary);
91
+ box-shadow: 0 1px 3px var(--shadow);
92
+ }
93
+
94
+ .buttons { display: flex; gap: 12px; }
95
+
96
+ button {
97
+ background: var(--bg-card);
98
+ color: var(--text-primary);
99
+ border: 1px solid var(--border);
100
+ padding: 10px 18px;
101
+ border-radius: 8px;
102
+ cursor: pointer;
103
+ font-size: 14px;
104
+ font-weight: 500;
105
+ font-family: inherit;
106
+ transition: all 0.15s ease;
107
+ }
108
+
109
+ button:hover {
110
+ background: var(--bg-secondary);
111
+ border-color: var(--text-muted);
112
+ }
113
+
114
+ button.primary {
115
+ background: var(--accent);
116
+ color: white;
117
+ border-color: var(--accent);
118
+ }
119
+
120
+ button.primary:hover {
121
+ background: var(--accent-hover);
122
+ border-color: var(--accent-hover);
123
+ }
124
+
125
+ button:disabled {
126
+ opacity: 0.4;
127
+ cursor: not-allowed;
128
+ }
129
+
130
+ /* ===== TASKS STYLES ===== */
131
+
132
+ .board {
133
+ display: flex;
134
+ gap: 24px;
135
+ overflow-x: auto;
136
+ overflow-y: hidden;
137
+ padding-bottom: 24px;
138
+ flex: 1;
139
+ min-height: 0;
140
+ }
141
+
142
+ .column {
143
+ background: var(--bg-secondary);
144
+ border-radius: 12px;
145
+ min-width: 340px;
146
+ max-width: 340px;
147
+ display: flex;
148
+ flex-direction: column;
149
+ height: 100%;
150
+ max-height: 100%;
151
+ }
152
+
153
+ .column-header {
154
+ padding: 16px 18px;
155
+ font-weight: 600;
156
+ font-size: 13px;
157
+ text-transform: uppercase;
158
+ letter-spacing: 0.05em;
159
+ color: var(--text-secondary);
160
+ display: flex;
161
+ justify-content: space-between;
162
+ align-items: center;
163
+ cursor: grab;
164
+ }
165
+
166
+ .column-header:active {
167
+ cursor: grabbing;
168
+ }
169
+
170
+ .column.dragging-column {
171
+ opacity: 0.5;
172
+ }
173
+
174
+ .column-drop-indicator {
175
+ width: 3px;
176
+ background: var(--accent);
177
+ border-radius: 2px;
178
+ margin: 0 -2px;
179
+ min-height: 100px;
180
+ }
181
+
182
+ .column-header .count {
183
+ background: var(--bg-card);
184
+ padding: 3px 10px;
185
+ border-radius: 12px;
186
+ font-size: 12px;
187
+ font-weight: 500;
188
+ color: var(--text-muted);
189
+ }
190
+
191
+ .cards {
192
+ flex: 1;
193
+ overflow-y: auto;
194
+ padding: 0 12px 12px;
195
+ min-height: 100px;
196
+ }
197
+
198
+ .cards.drag-over {
199
+ background: rgba(217, 119, 87, 0.05);
200
+ border-radius: 8px;
201
+ }
202
+
203
+ .task-card {
204
+ background: var(--bg-card);
205
+ border: 1px solid var(--border-light);
206
+ border-radius: 10px;
207
+ padding: 14px 16px;
208
+ margin-bottom: 10px;
209
+ cursor: grab;
210
+ transition: all 0.15s ease;
211
+ box-shadow: 0 1px 3px var(--shadow);
212
+ position: relative;
213
+ }
214
+
215
+ .task-card:hover {
216
+ box-shadow: 0 4px 12px var(--shadow-hover);
217
+ border-color: var(--border);
218
+ }
219
+
220
+ .task-card .add-on-hover {
221
+ display: none;
222
+ }
223
+
224
+ .task-card:hover .add-on-hover {
225
+ display: block;
226
+ }
227
+
228
+ .task-card .delete-btn {
229
+ display: none;
230
+ position: absolute;
231
+ top: 8px;
232
+ right: 8px;
233
+ background: transparent;
234
+ border: none;
235
+ color: var(--text-muted);
236
+ cursor: pointer;
237
+ padding: 4px 6px;
238
+ font-size: 14px;
239
+ line-height: 1;
240
+ border-radius: 4px;
241
+ }
242
+
243
+ .task-card:hover .delete-btn {
244
+ display: block;
245
+ }
246
+
247
+ .task-card .delete-btn:hover {
248
+ background: var(--bg-secondary);
249
+ color: var(--accent);
250
+ }
251
+
252
+ .task-card.dragging {
253
+ opacity: 0.5;
254
+ transform: rotate(2deg);
255
+ }
256
+
257
+ .card-title {
258
+ font-size: 14px;
259
+ font-weight: 500;
260
+ line-height: 1.5;
261
+ color: var(--text-primary);
262
+ }
263
+
264
+ .card-note {
265
+ font-size: 13px;
266
+ color: var(--text-muted);
267
+ margin-top: 6px;
268
+ line-height: 1.4;
269
+ overflow-wrap: break-word;
270
+ word-break: break-word;
271
+ }
272
+
273
+ .card-subtasks {
274
+ margin-top: 10px;
275
+ padding-top: 10px;
276
+ border-top: 1px solid var(--border-light);
277
+ font-size: 13px;
278
+ color: var(--text-secondary);
279
+ }
280
+
281
+ .subtask {
282
+ padding: 3px 0;
283
+ display: flex;
284
+ align-items: flex-start;
285
+ gap: 8px;
286
+ }
287
+
288
+ .checkbox {
289
+ width: 18px;
290
+ height: 18px;
291
+ min-width: 18px;
292
+ min-height: 18px;
293
+ flex-shrink: 0;
294
+ border: 2px solid var(--border);
295
+ border-radius: 5px;
296
+ display: inline-flex;
297
+ align-items: center;
298
+ justify-content: center;
299
+ vertical-align: middle;
300
+ cursor: pointer;
301
+ transition: all 0.15s ease;
302
+ background: var(--bg-card);
303
+ }
304
+
305
+ .checkbox:hover {
306
+ border-color: var(--accent);
307
+ }
308
+
309
+ .checkbox.checked {
310
+ background: var(--accent);
311
+ border-color: var(--accent);
312
+ }
313
+
314
+ .checkbox.checked::after {
315
+ content: '';
316
+ width: 5px;
317
+ height: 9px;
318
+ border: solid white;
319
+ border-width: 0 2px 2px 0;
320
+ transform: rotate(45deg);
321
+ margin-bottom: 2px;
322
+ }
323
+
324
+ .empty-state {
325
+ text-align: center;
326
+ padding: 80px 40px;
327
+ color: var(--text-muted);
328
+ }
329
+
330
+ .empty-state button {
331
+ font-size: 16px;
332
+ padding: 14px 28px;
333
+ background: var(--accent);
334
+ border: none;
335
+ color: white;
336
+ }
337
+
338
+ .empty-state button:hover {
339
+ background: var(--accent-hover);
340
+ }
341
+
342
+ .status-bar {
343
+ position: fixed;
344
+ bottom: 24px;
345
+ left: 50%;
346
+ transform: translateX(-50%);
347
+ background: var(--text-primary);
348
+ color: var(--bg-primary);
349
+ padding: 12px 24px;
350
+ border-radius: 10px;
351
+ font-size: 13px;
352
+ font-weight: 500;
353
+ opacity: 0;
354
+ transition: opacity 0.2s ease;
355
+ box-shadow: 0 4px 20px rgba(20, 20, 19, 0.15);
356
+ z-index: 200;
357
+ }
358
+
359
+ .status-bar.visible { opacity: 1; }
360
+
361
+ .add-card {
362
+ padding: 12px;
363
+ }
364
+
365
+ .add-card button {
366
+ width: 100%;
367
+ background: transparent;
368
+ border: 2px dashed var(--border);
369
+ color: var(--text-muted);
370
+ font-weight: 500;
371
+ }
372
+
373
+ .add-card button:hover {
374
+ border-color: var(--accent);
375
+ color: var(--accent);
376
+ background: transparent;
377
+ }
378
+
379
+ .new-task-input {
380
+ width: 100%;
381
+ background: var(--bg-card);
382
+ border: 2px solid var(--accent);
383
+ border-radius: 8px;
384
+ padding: 12px;
385
+ color: var(--text-primary);
386
+ font-size: 14px;
387
+ font-family: inherit;
388
+ resize: none;
389
+ }
390
+
391
+ .new-task-input:focus {
392
+ outline: none;
393
+ }
394
+
395
+ .new-task-input::placeholder {
396
+ color: var(--text-muted);
397
+ }
398
+
399
+ /* Scrollbar styling */
400
+ .cards::-webkit-scrollbar,
401
+ .list-view::-webkit-scrollbar {
402
+ width: 6px;
403
+ }
404
+
405
+ .cards::-webkit-scrollbar-track,
406
+ .list-view::-webkit-scrollbar-track {
407
+ background: transparent;
408
+ }
409
+
410
+ .cards::-webkit-scrollbar-thumb,
411
+ .list-view::-webkit-scrollbar-thumb {
412
+ background: var(--border);
413
+ border-radius: 3px;
414
+ }
415
+
416
+ .cards::-webkit-scrollbar-thumb:hover,
417
+ .list-view::-webkit-scrollbar-thumb:hover {
418
+ background: var(--text-muted);
419
+ }
420
+
421
+ /* List View Styles */
422
+ .list-view {
423
+ max-width: 800px;
424
+ margin: 0 auto;
425
+ flex: 1;
426
+ overflow-y: auto;
427
+ overflow-x: hidden;
428
+ min-height: 0;
429
+ }
430
+
431
+ .list-section {
432
+ margin-bottom: 28px;
433
+ }
434
+
435
+ .list-section.drag-over {
436
+ background: rgba(217, 119, 87, 0.05);
437
+ border-radius: 8px;
438
+ margin: 0 -12px 32px;
439
+ padding: 0 12px;
440
+ }
441
+
442
+ .list-section-header {
443
+ font-size: 13px;
444
+ font-weight: 600;
445
+ text-transform: uppercase;
446
+ letter-spacing: 0.05em;
447
+ color: var(--text-secondary);
448
+ padding: 4px 0;
449
+ margin-bottom: 6px;
450
+ display: flex;
451
+ justify-content: space-between;
452
+ align-items: center;
453
+ }
454
+
455
+ .list-section-header .section-title {
456
+ cursor: pointer;
457
+ }
458
+
459
+ .list-section-header .section-title:hover {
460
+ color: var(--text-primary);
461
+ }
462
+
463
+ .list-section-header .count {
464
+ font-size: 12px;
465
+ color: var(--text-muted);
466
+ font-weight: 500;
467
+ }
468
+
469
+ .list-item {
470
+ display: flex;
471
+ align-items: flex-start;
472
+ gap: 12px;
473
+ padding: 12px 14px;
474
+ margin-bottom: 6px;
475
+ background: var(--bg-card);
476
+ border: 1px solid var(--border-light);
477
+ cursor: grab;
478
+ position: relative;
479
+ border-radius: 10px;
480
+ transition: all 0.15s ease;
481
+ box-shadow: 0 1px 3px var(--shadow);
482
+ }
483
+
484
+ .list-item:active {
485
+ cursor: grabbing;
486
+ }
487
+
488
+ .list-item:hover {
489
+ box-shadow: 0 4px 12px var(--shadow-hover);
490
+ border-color: var(--border);
491
+ }
492
+
493
+ .list-item.dragging {
494
+ opacity: 0.5;
495
+ background: var(--bg-secondary);
496
+ }
497
+
498
+ .list-item .checkbox {
499
+ margin-top: 2px;
500
+ flex-shrink: 0;
501
+ }
502
+
503
+ .list-item-content {
504
+ flex: 1;
505
+ min-width: 0;
506
+ }
507
+
508
+ .list-item-title {
509
+ font-size: 15px;
510
+ color: var(--text-primary);
511
+ line-height: 1.4;
512
+ }
513
+
514
+ .list-item-title.checked {
515
+ color: var(--text-muted);
516
+ text-decoration: line-through;
517
+ }
518
+
519
+ .list-item-note {
520
+ font-size: 13px;
521
+ color: var(--text-muted);
522
+ margin-top: 4px;
523
+ cursor: pointer;
524
+ }
525
+
526
+ .list-item-note:hover {
527
+ color: var(--text-secondary);
528
+ }
529
+
530
+ .list-item-note.add-note {
531
+ font-style: italic;
532
+ display: none;
533
+ }
534
+
535
+ .list-item:hover .list-item-note.add-note {
536
+ display: block;
537
+ }
538
+
539
+ .list-item-subtasks {
540
+ margin-top: 8px;
541
+ padding-left: 2px;
542
+ }
543
+
544
+ .list-item-subtask {
545
+ display: flex;
546
+ align-items: flex-start;
547
+ gap: 8px;
548
+ padding: 3px 0;
549
+ font-size: 13px;
550
+ color: var(--text-secondary);
551
+ }
552
+
553
+ .list-item-subtask .checkbox {
554
+ width: 16px;
555
+ height: 16px;
556
+ min-width: 16px;
557
+ min-height: 16px;
558
+ margin-top: 1px;
559
+ }
560
+
561
+ .list-item-subtask span {
562
+ cursor: pointer;
563
+ }
564
+
565
+ .list-item-subtask span:hover {
566
+ color: var(--text-primary);
567
+ }
568
+
569
+ .list-item-add-subtask {
570
+ font-size: 13px;
571
+ color: var(--text-muted);
572
+ font-style: italic;
573
+ cursor: pointer;
574
+ padding: 3px 0;
575
+ padding-left: 24px;
576
+ display: none;
577
+ }
578
+
579
+ .list-item:hover .list-item-add-subtask {
580
+ display: block;
581
+ }
582
+
583
+ .list-item-add-subtask:hover {
584
+ color: var(--accent);
585
+ }
586
+
587
+ .list-item-section {
588
+ font-size: 11px;
589
+ color: var(--text-muted);
590
+ background: var(--bg-secondary);
591
+ padding: 2px 8px;
592
+ border-radius: 4px;
593
+ white-space: nowrap;
594
+ }
595
+
596
+ .list-item-actions {
597
+ display: none;
598
+ position: absolute;
599
+ top: 8px;
600
+ right: 8px;
601
+ }
602
+
603
+ .list-item:hover .list-item-actions {
604
+ display: flex;
605
+ gap: 4px;
606
+ }
607
+
608
+ .list-item-actions button {
609
+ background: transparent;
610
+ border: none;
611
+ color: var(--text-muted);
612
+ cursor: pointer;
613
+ padding: 4px 6px;
614
+ font-size: 14px;
615
+ line-height: 1;
616
+ border-radius: 4px;
617
+ }
618
+
619
+ .list-item-actions button:hover {
620
+ background: var(--bg-card);
621
+ color: var(--accent);
622
+ }
623
+
624
+ .list-drop-indicator {
625
+ height: 3px;
626
+ background: var(--accent);
627
+ border-radius: 2px;
628
+ margin: 2px -12px;
629
+ }
630
+
631
+ .list-add-section {
632
+ margin-top: 16px;
633
+ padding: 16px;
634
+ border: 2px dashed var(--border);
635
+ border-radius: 8px;
636
+ text-align: center;
637
+ cursor: pointer;
638
+ color: var(--text-muted);
639
+ font-size: 14px;
640
+ font-weight: 500;
641
+ transition: all 0.15s ease;
642
+ }
643
+
644
+ .list-add-section:hover {
645
+ border-color: var(--accent);
646
+ color: var(--accent);
647
+ }
648
+
649
+ .quick-add {
650
+ display: flex;
651
+ align-items: center;
652
+ gap: 12px;
653
+ padding: 12px 0;
654
+ }
655
+
656
+ .quick-add-input {
657
+ flex: 1;
658
+ background: transparent;
659
+ border: none;
660
+ font-size: 15px;
661
+ color: var(--text-primary);
662
+ font-family: inherit;
663
+ outline: none;
664
+ }
665
+
666
+ .quick-add-input::placeholder {
667
+ color: var(--text-muted);
668
+ }
669
+
670
+ .quick-add-section {
671
+ font-size: 12px;
672
+ color: var(--text-muted);
673
+ background: var(--bg-secondary);
674
+ padding: 4px 10px;
675
+ border-radius: 6px;
676
+ cursor: pointer;
677
+ border: 1px solid var(--border);
678
+ }
679
+
680
+ .quick-add-section:hover {
681
+ border-color: var(--accent);
682
+ }
683
+
684
+ .section-picker {
685
+ position: absolute;
686
+ background: var(--bg-card);
687
+ border: 1px solid var(--border);
688
+ border-radius: 8px;
689
+ box-shadow: 0 4px 20px var(--shadow-hover);
690
+ padding: 4px;
691
+ z-index: 100;
692
+ }
693
+
694
+ .section-picker button {
695
+ display: block;
696
+ width: 100%;
697
+ text-align: left;
698
+ padding: 8px 12px;
699
+ font-size: 13px;
700
+ background: transparent;
701
+ border: none;
702
+ border-radius: 6px;
703
+ }
704
+
705
+ .section-picker button:hover {
706
+ background: var(--bg-secondary);
707
+ }
708
+
709
+ /* ===== MEMORY STYLES ===== */
710
+
711
+ .memory-tabs {
712
+ display: flex;
713
+ gap: 4px;
714
+ margin-bottom: 12px;
715
+ flex-wrap: wrap;
716
+ background: var(--bg-secondary);
717
+ padding: 4px;
718
+ border-radius: 8px;
719
+ flex-shrink: 0;
720
+ }
721
+
722
+ .memory-tab {
723
+ background: transparent;
724
+ color: var(--text-muted);
725
+ padding: 8px 16px;
726
+ border: none;
727
+ border-radius: 6px;
728
+ cursor: pointer;
729
+ font-size: 13px;
730
+ font-weight: 500;
731
+ transition: all 0.15s ease;
732
+ text-transform: capitalize;
733
+ }
734
+
735
+ .memory-tab:hover { color: var(--text-primary); }
736
+
737
+ .memory-tab.active {
738
+ color: var(--text-primary);
739
+ background: var(--bg-card);
740
+ box-shadow: 0 1px 3px var(--shadow);
741
+ }
742
+
743
+ .memory-tab .count {
744
+ background: var(--bg-secondary);
745
+ padding: 2px 8px;
746
+ border-radius: 10px;
747
+ font-size: 11px;
748
+ margin-left: 6px;
749
+ color: var(--text-muted);
750
+ }
751
+
752
+ .memory-tab.active .count {
753
+ background: var(--border-light);
754
+ }
755
+
756
+ .memory-grid {
757
+ display: grid;
758
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
759
+ gap: 16px;
760
+ }
761
+
762
+ .memory-flat-table {
763
+ width: 100%;
764
+ border-collapse: collapse;
765
+ font-size: 13px;
766
+ }
767
+
768
+ .memory-flat-table th {
769
+ text-align: left;
770
+ padding: 10px 16px;
771
+ font-weight: 500;
772
+ color: var(--text-muted);
773
+ border-bottom: 1px solid var(--border);
774
+ font-size: 12px;
775
+ text-transform: uppercase;
776
+ letter-spacing: 0.03em;
777
+ }
778
+
779
+ .memory-flat-table td {
780
+ padding: 9px 16px;
781
+ border-bottom: 1px solid var(--border-light);
782
+ color: var(--text-primary);
783
+ line-height: 1.4;
784
+ }
785
+
786
+ .memory-flat-table td:first-child {
787
+ color: var(--text-muted);
788
+ font-weight: 500;
789
+ white-space: nowrap;
790
+ width: 120px;
791
+ }
792
+
793
+ .memory-flat-table tr:last-child td {
794
+ border-bottom: none;
795
+ }
796
+
797
+ .memory-flat-table tr:hover td {
798
+ background: var(--bg-secondary);
799
+ }
800
+
801
+ .memory-card {
802
+ background: var(--bg-card);
803
+ border: 1px solid var(--border-light);
804
+ border-radius: 10px;
805
+ padding: 16px;
806
+ cursor: pointer;
807
+ transition: all 0.15s ease;
808
+ box-shadow: 0 1px 3px var(--shadow);
809
+ }
810
+
811
+ .memory-card:hover {
812
+ border-color: var(--accent);
813
+ box-shadow: 0 4px 12px var(--shadow-hover);
814
+ }
815
+
816
+ .memory-card-title {
817
+ font-size: 15px;
818
+ font-weight: 600;
819
+ margin-bottom: 8px;
820
+ display: flex;
821
+ align-items: center;
822
+ gap: 8px;
823
+ color: var(--text-primary);
824
+ }
825
+
826
+ .memory-card-meta {
827
+ font-size: 13px;
828
+ color: var(--text-muted);
829
+ margin-bottom: 8px;
830
+ }
831
+
832
+ .memory-card-preview {
833
+ font-size: 13px;
834
+ color: var(--text-secondary);
835
+ line-height: 1.5;
836
+ overflow: hidden;
837
+ display: -webkit-box;
838
+ -webkit-line-clamp: 4;
839
+ -webkit-box-orient: vertical;
840
+ }
841
+
842
+ .memory-card-fields {
843
+ margin-top: 10px;
844
+ font-size: 12px;
845
+ }
846
+
847
+ .memory-card-field {
848
+ display: flex;
849
+ gap: 8px;
850
+ margin-bottom: 4px;
851
+ }
852
+
853
+ .memory-card-field-label {
854
+ color: var(--text-muted);
855
+ min-width: 80px;
856
+ }
857
+
858
+ .memory-card-field-value {
859
+ color: var(--text-secondary);
860
+ }
861
+
862
+ .file-card {
863
+ background: var(--bg-card);
864
+ border: 1px solid var(--border-light);
865
+ border-radius: 10px;
866
+ margin-bottom: 16px;
867
+ box-shadow: 0 1px 3px var(--shadow);
868
+ }
869
+
870
+ .file-card-header {
871
+ padding: 16px;
872
+ border-bottom: 1px solid var(--border-light);
873
+ display: flex;
874
+ justify-content: space-between;
875
+ align-items: center;
876
+ cursor: pointer;
877
+ }
878
+
879
+ .file-card-header:hover {
880
+ background: var(--bg-secondary);
881
+ }
882
+
883
+ .file-card-title {
884
+ font-size: 14px;
885
+ font-weight: 600;
886
+ color: var(--text-primary);
887
+ }
888
+
889
+ .file-card-content {
890
+ padding: 16px;
891
+ font-size: 13px;
892
+ line-height: 1.6;
893
+ white-space: pre-wrap;
894
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
895
+ overflow-y: auto;
896
+ display: none;
897
+ color: var(--text-secondary);
898
+ }
899
+
900
+ .file-card-content.expanded {
901
+ display: block;
902
+ }
903
+
904
+ .stats {
905
+ display: flex;
906
+ gap: 16px;
907
+ margin-bottom: 24px;
908
+ flex-wrap: wrap;
909
+ }
910
+
911
+ .stat {
912
+ background: var(--bg-card);
913
+ border: 1px solid var(--border-light);
914
+ border-radius: 10px;
915
+ padding: 16px 24px;
916
+ min-width: 100px;
917
+ box-shadow: 0 1px 3px var(--shadow);
918
+ }
919
+
920
+ .stat-value {
921
+ font-size: 28px;
922
+ font-weight: 600;
923
+ color: var(--accent);
924
+ }
925
+
926
+ .stat-label {
927
+ font-size: 12px;
928
+ color: var(--text-muted);
929
+ text-transform: capitalize;
930
+ margin-top: 4px;
931
+ }
932
+
933
+ .search-box {
934
+ background: var(--bg-card);
935
+ border: 1px solid var(--border);
936
+ border-radius: 8px;
937
+ padding: 10px 16px;
938
+ display: flex;
939
+ align-items: center;
940
+ gap: 10px;
941
+ margin-bottom: 20px;
942
+ }
943
+
944
+ .search-box input {
945
+ flex: 1;
946
+ background: transparent;
947
+ border: none;
948
+ color: var(--text-primary);
949
+ font-size: 14px;
950
+ font-family: inherit;
951
+ outline: none;
952
+ }
953
+
954
+ .search-box input::placeholder {
955
+ color: var(--text-muted);
956
+ }
957
+
958
+ .add-btn {
959
+ background: transparent;
960
+ border: 2px dashed var(--border);
961
+ color: var(--text-muted);
962
+ padding: 40px;
963
+ border-radius: 10px;
964
+ cursor: pointer;
965
+ transition: all 0.15s ease;
966
+ display: flex;
967
+ align-items: center;
968
+ justify-content: center;
969
+ font-size: 14px;
970
+ font-weight: 500;
971
+ }
972
+
973
+ .add-btn:hover {
974
+ border-color: var(--accent);
975
+ color: var(--accent);
976
+ }
977
+
978
+ .markdown-content {
979
+ line-height: 1.6;
980
+ color: var(--text-secondary);
981
+ }
982
+
983
+ .markdown-content h1 { font-size: 1.4em; margin: 16px 0 8px; color: var(--text-primary); }
984
+ .markdown-content h2 { font-size: 1.2em; margin: 14px 0 6px; color: var(--text-primary); }
985
+ .markdown-content h3 { font-size: 1.1em; margin: 12px 0 4px; color: var(--text-secondary); }
986
+ .markdown-content p { margin: 8px 0; }
987
+ .markdown-content ul, .markdown-content ol { margin: 8px 0 8px 20px; }
988
+ .markdown-content li { margin: 4px 0; }
989
+ .markdown-content code {
990
+ background: var(--bg-secondary);
991
+ padding: 2px 6px;
992
+ border-radius: 4px;
993
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
994
+ color: var(--text-primary);
995
+ }
996
+ .markdown-content pre {
997
+ background: var(--bg-secondary);
998
+ padding: 12px;
999
+ border-radius: 8px;
1000
+ overflow-x: auto;
1001
+ margin: 8px 0;
1002
+ border: 1px solid var(--border-light);
1003
+ }
1004
+ .markdown-content table {
1005
+ width: 100%;
1006
+ border-collapse: collapse;
1007
+ margin: 8px 0;
1008
+ font-size: 13px;
1009
+ }
1010
+ .markdown-content th, .markdown-content td {
1011
+ padding: 12px 16px;
1012
+ text-align: left;
1013
+ border-bottom: 1px solid var(--border-light);
1014
+ }
1015
+ .markdown-content th {
1016
+ background: var(--bg-secondary);
1017
+ font-weight: 600;
1018
+ font-size: 12px;
1019
+ text-transform: uppercase;
1020
+ letter-spacing: 0.05em;
1021
+ color: var(--text-secondary);
1022
+ }
1023
+
1024
+ /* Modal */
1025
+ .modal-overlay {
1026
+ display: none;
1027
+ position: fixed;
1028
+ top: 0;
1029
+ left: 0;
1030
+ right: 0;
1031
+ bottom: 0;
1032
+ background: rgba(20, 20, 19, 0.5);
1033
+ z-index: 100;
1034
+ align-items: center;
1035
+ justify-content: center;
1036
+ }
1037
+
1038
+ .modal-overlay.visible {
1039
+ display: flex;
1040
+ }
1041
+
1042
+ .modal {
1043
+ background: var(--bg-card);
1044
+ border-radius: 12px;
1045
+ width: 90%;
1046
+ max-width: 800px;
1047
+ max-height: 85vh;
1048
+ overflow: hidden;
1049
+ display: flex;
1050
+ flex-direction: column;
1051
+ box-shadow: 0 20px 60px rgba(20, 20, 19, 0.2);
1052
+ }
1053
+
1054
+ .modal-header {
1055
+ padding: 20px;
1056
+ border-bottom: 1px solid var(--border);
1057
+ display: flex;
1058
+ justify-content: space-between;
1059
+ align-items: center;
1060
+ }
1061
+
1062
+ .modal-header h3 {
1063
+ font-size: 18px;
1064
+ font-weight: 600;
1065
+ color: var(--text-primary);
1066
+ }
1067
+
1068
+ .modal-close {
1069
+ background: transparent;
1070
+ border: none;
1071
+ color: var(--text-muted);
1072
+ font-size: 24px;
1073
+ cursor: pointer;
1074
+ padding: 0;
1075
+ line-height: 1;
1076
+ }
1077
+
1078
+ .modal-close:hover { color: var(--text-primary); }
1079
+
1080
+ .modal-body {
1081
+ padding: 20px;
1082
+ overflow-y: auto;
1083
+ flex: 1;
1084
+ }
1085
+
1086
+ .modal-footer {
1087
+ padding: 16px 20px;
1088
+ border-top: 1px solid var(--border);
1089
+ display: flex;
1090
+ justify-content: flex-end;
1091
+ gap: 10px;
1092
+ }
1093
+
1094
+ .form-group {
1095
+ margin-bottom: 16px;
1096
+ }
1097
+
1098
+ .form-group label {
1099
+ display: block;
1100
+ font-size: 12px;
1101
+ font-weight: 600;
1102
+ color: var(--text-secondary);
1103
+ text-transform: uppercase;
1104
+ letter-spacing: 0.05em;
1105
+ margin-bottom: 6px;
1106
+ }
1107
+
1108
+ .form-group textarea {
1109
+ width: 100%;
1110
+ background: var(--bg-secondary);
1111
+ border: 1px solid var(--border);
1112
+ border-radius: 8px;
1113
+ padding: 12px;
1114
+ color: var(--text-primary);
1115
+ font-size: 13px;
1116
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1117
+ line-height: 1.5;
1118
+ min-height: 300px;
1119
+ resize: vertical;
1120
+ }
1121
+
1122
+ .form-group textarea:focus {
1123
+ outline: none;
1124
+ border-color: var(--accent);
1125
+ }
1126
+
1127
+ /* Tab panel containers */
1128
+ .tab-panel {
1129
+ display: none;
1130
+ flex: 1;
1131
+ min-height: 0;
1132
+ flex-direction: column;
1133
+ }
1134
+
1135
+ .tab-panel.active {
1136
+ display: flex;
1137
+ }
1138
+
1139
+ /* Memory panel scrollable area */
1140
+ .memory-content-area {
1141
+ flex: 1;
1142
+ overflow-y: auto;
1143
+ min-height: 0;
1144
+ }
1145
+
1146
+ .memory-content-area::-webkit-scrollbar {
1147
+ width: 6px;
1148
+ }
1149
+
1150
+ .memory-content-area::-webkit-scrollbar-track {
1151
+ background: transparent;
1152
+ }
1153
+
1154
+ .memory-content-area::-webkit-scrollbar-thumb {
1155
+ background: var(--border);
1156
+ border-radius: 3px;
1157
+ }
1158
+
1159
+ .memory-content-area::-webkit-scrollbar-thumb:hover {
1160
+ background: var(--text-muted);
1161
+ }
1162
+
1163
+ .file-card-content::-webkit-scrollbar,
1164
+ .modal-body::-webkit-scrollbar {
1165
+ width: 6px;
1166
+ }
1167
+
1168
+ .file-card-content::-webkit-scrollbar-track,
1169
+ .modal-body::-webkit-scrollbar-track {
1170
+ background: transparent;
1171
+ }
1172
+
1173
+ .file-card-content::-webkit-scrollbar-thumb,
1174
+ .modal-body::-webkit-scrollbar-thumb {
1175
+ background: var(--border);
1176
+ border-radius: 3px;
1177
+ }
1178
+
1179
+ .file-card-content::-webkit-scrollbar-thumb:hover,
1180
+ .modal-body::-webkit-scrollbar-thumb:hover {
1181
+ background: var(--text-muted);
1182
+ }
1183
+
1184
+ /* File path display */
1185
+ .file-path {
1186
+ font-size: 13px;
1187
+ color: var(--text-muted);
1188
+ margin-top: 2px;
1189
+ }
1190
+ </style>
1191
+ </head>
1192
+ <body>
1193
+ <header>
1194
+ <div class="header-left">
1195
+ <svg width="28" height="28" viewBox="0 0 32 32" style="margin-right: 12px; flex-shrink: 0;">
1196
+ <rect x="4" y="2" width="24" height="28" rx="4" fill="#faf9f5" stroke="#D97757" stroke-width="2"/>
1197
+ <rect x="8" y="0" width="16" height="5" rx="2.5" fill="#D97757"/>
1198
+ <path d="M9 13l3 3 5-5" fill="none" stroke="#D97757" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
1199
+ <rect x="20" y="12" width="5" height="2" rx="1" fill="#c4c4c2"/>
1200
+ <rect x="9" y="20" width="10" height="2" rx="1" fill="#c4c4c2"/>
1201
+ <rect x="9" y="25" width="7" height="2" rx="1" fill="#c4c4c2"/>
1202
+ </svg>
1203
+ <div>
1204
+ <h1>Productivity</h1>
1205
+ <div id="filePath" class="file-path"></div>
1206
+ </div>
1207
+ <div class="view-toggle" id="mainTabToggle">
1208
+ <button id="tasksTabBtn" class="active">Tasks</button>
1209
+ <button id="memoryTabBtn">Memory</button>
1210
+ </div>
1211
+ <div class="view-toggle" id="taskViewToggle">
1212
+ <button id="boardViewBtn" class="active">Board</button>
1213
+ <button id="listViewBtn">List</button>
1214
+ </div>
1215
+ </div>
1216
+ <div class="buttons">
1217
+ <button id="openTaskBtn">Select File</button>
1218
+ <button id="openMemoryBtn" style="display: none;">Open Folder</button>
1219
+ <button id="saveBtn" class="primary" disabled style="display: none;">Save</button>
1220
+ </div>
1221
+ </header>
1222
+
1223
+ <!-- TASKS TAB PANEL -->
1224
+ <div class="tab-panel active" id="tasksPanel">
1225
+ <div class="list-view" id="listView" style="display: none;"></div>
1226
+ <div class="board" id="board">
1227
+ <div class="empty-state" id="boardEmptyState" style="width: 100%;">
1228
+ <p style="margin-bottom: 24px; font-size: 18px; font-weight: 600; color: var(--text-primary);">Select your TASKS.md file</p>
1229
+ <button id="openBtnLarge">Select TASKS.md</button>
1230
+ </div>
1231
+ </div>
1232
+ </div>
1233
+
1234
+ <!-- MEMORY TAB PANEL -->
1235
+ <div class="tab-panel" id="memoryPanel">
1236
+ <div id="memoryEmptyState" class="empty-state">
1237
+ <p style="margin-bottom: 24px; font-size: 18px; font-weight: 600; color: var(--text-primary);">Select your project folder</p>
1238
+ <button id="openMemoryBtnLarge">Select Folder</button>
1239
+ </div>
1240
+ <div id="memoryMainContent" style="display: none; flex-direction: column; min-height: 0; flex: 1;">
1241
+ <div class="memory-tabs" id="memoryTabsContainer"></div>
1242
+ <div class="memory-content-area">
1243
+ <div id="memoryContentContainer"></div>
1244
+ </div>
1245
+ </div>
1246
+ </div>
1247
+
1248
+ <!-- MODAL (shared for memory) -->
1249
+ <div class="modal-overlay" id="modalOverlay">
1250
+ <div class="modal">
1251
+ <div class="modal-header">
1252
+ <h3 id="modalTitle">Edit</h3>
1253
+ <button class="modal-close" id="modalClose">&times;</button>
1254
+ </div>
1255
+ <div class="modal-body" id="modalBody"></div>
1256
+ <div class="modal-footer">
1257
+ <button id="modalCancel">Cancel</button>
1258
+ <button id="modalSave" class="primary">Save</button>
1259
+ </div>
1260
+ </div>
1261
+ </div>
1262
+
1263
+ <div class="status-bar" id="status"></div>
1264
+
1265
+ <script>
1266
+ // ===== SHARED STATE =====
1267
+ let activeMainTab = 'tasks'; // 'tasks' or 'memory'
1268
+
1269
+ const statusEl = document.getElementById('status');
1270
+ const filePathEl = document.getElementById('filePath');
1271
+
1272
+ function showStatus(msg) {
1273
+ statusEl.textContent = msg;
1274
+ statusEl.classList.add('visible');
1275
+ setTimeout(() => statusEl.classList.remove('visible'), 2000);
1276
+ }
1277
+
1278
+ // ===== MAIN TAB SWITCHING =====
1279
+ const tasksTabBtn = document.getElementById('tasksTabBtn');
1280
+ const memoryTabBtn = document.getElementById('memoryTabBtn');
1281
+ const tasksPanel = document.getElementById('tasksPanel');
1282
+ const memoryPanel = document.getElementById('memoryPanel');
1283
+ const taskViewToggle = document.getElementById('taskViewToggle');
1284
+ const openTaskBtn = document.getElementById('openTaskBtn');
1285
+ const openMemoryBtn = document.getElementById('openMemoryBtn');
1286
+ const saveBtn = document.getElementById('saveBtn');
1287
+
1288
+ function switchMainTab(tab) {
1289
+ activeMainTab = tab;
1290
+
1291
+ tasksTabBtn.classList.toggle('active', tab === 'tasks');
1292
+ memoryTabBtn.classList.toggle('active', tab === 'memory');
1293
+
1294
+ tasksPanel.classList.toggle('active', tab === 'tasks');
1295
+ memoryPanel.classList.toggle('active', tab === 'memory');
1296
+
1297
+ // Show/hide view toggle for tasks
1298
+ taskViewToggle.style.display = tab === 'tasks' ? 'flex' : 'none';
1299
+
1300
+ // Show/hide appropriate buttons
1301
+ openTaskBtn.style.display = tab === 'tasks' ? 'inline-flex' : 'none';
1302
+ openMemoryBtn.style.display = tab === 'memory' ? 'inline-flex' : 'none';
1303
+ saveBtn.style.display = tab === 'tasks' ? 'inline-flex' : 'none';
1304
+
1305
+ // Update file path display
1306
+ if (tab === 'tasks') {
1307
+ filePathEl.textContent = taskFileHandle ? taskFileName : '';
1308
+ } else {
1309
+ filePathEl.textContent = memoryDirHandle ? memoryDirHandle.name : '';
1310
+ }
1311
+ }
1312
+
1313
+ tasksTabBtn.addEventListener('click', () => switchMainTab('tasks'));
1314
+ memoryTabBtn.addEventListener('click', () => switchMainTab('memory'));
1315
+
1316
+ // ============================================================
1317
+ // ===== TASKS FUNCTIONALITY =====
1318
+ // ============================================================
1319
+
1320
+ let taskFileHandle = null;
1321
+ let taskFileName = '';
1322
+ let sections = [];
1323
+ let tasks = {};
1324
+ let hasChanges = false;
1325
+ let currentView = 'board';
1326
+ let quickAddSection = null;
1327
+
1328
+ const board = document.getElementById('board');
1329
+ const listView = document.getElementById('listView');
1330
+ const listViewBtn = document.getElementById('listViewBtn');
1331
+ const boardViewBtn = document.getElementById('boardViewBtn');
1332
+
1333
+ function taskSectionId(name) {
1334
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
1335
+ }
1336
+
1337
+ function parseTaskMarkdown(content) {
1338
+ const resultSections = [];
1339
+ const resultTasks = {};
1340
+ let currentSection = null;
1341
+ let currentSectionId = null;
1342
+
1343
+ const lines = content.split('\n');
1344
+ let currentTask = null;
1345
+
1346
+ for (const line of lines) {
1347
+ const headerMatch = line.match(/^## \*{0,2}(.+?)\*{0,2}$/);
1348
+ if (headerMatch) {
1349
+ if (currentTask && currentSectionId) {
1350
+ resultTasks[currentSectionId].push(currentTask);
1351
+ currentTask = null;
1352
+ }
1353
+
1354
+ const sectionName = headerMatch[1].trim();
1355
+ currentSectionId = taskSectionId(sectionName);
1356
+ currentSection = sectionName;
1357
+
1358
+ if (!resultTasks[currentSectionId]) {
1359
+ resultSections.push({ id: currentSectionId, name: sectionName });
1360
+ resultTasks[currentSectionId] = [];
1361
+ }
1362
+ } else if (currentSectionId && line.match(/^- \[[ xX]\]/)) {
1363
+ if (currentTask) {
1364
+ resultTasks[currentSectionId].push(currentTask);
1365
+ }
1366
+ const checked = line.match(/\[[xX]\]/) !== null;
1367
+ let text = line.replace(/^- \[[ xX]\]\s*/, '');
1368
+ let title = text;
1369
+ let note = '';
1370
+
1371
+ const boldMatch = text.match(/^\*\*(.+?)\*\*(.*)$/);
1372
+ if (boldMatch) {
1373
+ title = boldMatch[1];
1374
+ note = boldMatch[2].replace(/^\s*-\s*/, '').trim();
1375
+ }
1376
+
1377
+ currentTask = {
1378
+ id: Date.now() + Math.random(),
1379
+ title,
1380
+ note,
1381
+ checked,
1382
+ subtasks: [],
1383
+ section: currentSectionId
1384
+ };
1385
+ } else if (currentTask && line.match(/^\s+- \[[ xX]\]/)) {
1386
+ const checked = line.match(/\[[xX]\]/) !== null;
1387
+ const text = line.replace(/^\s+- \[[ xX]\]\s*/, '');
1388
+ currentTask.subtasks.push({ text, checked });
1389
+ }
1390
+ }
1391
+
1392
+ if (currentTask && currentSectionId) {
1393
+ resultTasks[currentSectionId].push(currentTask);
1394
+ }
1395
+
1396
+ return { sections: resultSections, tasks: resultTasks };
1397
+ }
1398
+
1399
+ function toMarkdown() {
1400
+ let md = '# Tasks\n';
1401
+
1402
+ sections.forEach((section, idx) => {
1403
+ md += `\n## ${section.name}\n`;
1404
+ const sectionTasks = tasks[section.id] || [];
1405
+ sectionTasks.forEach(t => {
1406
+ const checkbox = t.checked ? '[x]' : '[ ]';
1407
+ const note = t.note ? ` - ${t.note}` : '';
1408
+ md += `- ${checkbox} **${t.title}**${note}\n`;
1409
+ t.subtasks.forEach(st => {
1410
+ const stCheckbox = st.checked ? '[x]' : '[ ]';
1411
+ md += ` - ${stCheckbox} ${st.text}\n`;
1412
+ });
1413
+ });
1414
+ });
1415
+
1416
+ return md.trimEnd() + '\n';
1417
+ }
1418
+
1419
+ function createCard(task) {
1420
+ const card = document.createElement('div');
1421
+ card.className = 'task-card';
1422
+ card.draggable = true;
1423
+ card.dataset.id = task.id;
1424
+
1425
+ let html = `
1426
+ <div style="display: flex; align-items: flex-start; gap: 12px;">
1427
+ <button class="delete-btn" data-action="delete" title="Delete task">&times;</button>
1428
+ <span class="checkbox ${task.checked ? 'checked' : ''}" data-action="toggle"></span>
1429
+ <div class="card-title" data-action="edit-title">${escapeHtml(task.title)}</div>
1430
+ </div>
1431
+ `;
1432
+
1433
+ if (task.note) {
1434
+ html += `<div class="card-note" data-action="edit-note" style="cursor: pointer; margin-left: 30px;">${escapeHtml(task.note)}</div>`;
1435
+ } else {
1436
+ html += `<div class="card-note add-on-hover" data-action="edit-note" style="cursor: pointer; margin-left: 30px; font-style: italic;">+ Add note</div>`;
1437
+ }
1438
+
1439
+ if (task.subtasks.length > 0) {
1440
+ html += '<div class="card-subtasks" style="margin-left: 30px;">';
1441
+ task.subtasks.forEach((st, idx) => {
1442
+ html += `<div class="subtask">
1443
+ <span class="checkbox ${st.checked ? 'checked' : ''}" data-action="toggle-sub" data-idx="${idx}" style="width: 16px; height: 16px; min-width: 16px; min-height: 16px;"></span>
1444
+ <span data-action="edit-subtask" data-idx="${idx}" style="cursor: pointer;">${escapeHtml(st.text)}</span>
1445
+ </div>`;
1446
+ });
1447
+ html += `<div class="subtask add-on-hover" data-action="add-subtask" style="color: var(--text-muted); cursor: pointer; font-style: italic; padding-left: 24px;">+ Add subtask</div>`;
1448
+ html += '</div>';
1449
+ } else {
1450
+ html += `<div class="card-subtasks add-on-hover" style="margin-left: 30px;">
1451
+ <div class="subtask" data-action="add-subtask" style="color: var(--text-muted); cursor: pointer; font-style: italic;">+ Add subtask</div>
1452
+ </div>`;
1453
+ }
1454
+
1455
+ card.innerHTML = html;
1456
+
1457
+ card.addEventListener('dragstart', (e) => {
1458
+ card.classList.add('dragging');
1459
+ e.dataTransfer.setData('text/plain', task.id);
1460
+ });
1461
+
1462
+ card.addEventListener('dragend', () => {
1463
+ card.classList.remove('dragging');
1464
+ });
1465
+
1466
+ card.addEventListener('click', (e) => {
1467
+ const action = e.target.dataset.action;
1468
+ if (action === 'toggle') {
1469
+ task.checked = !task.checked;
1470
+ markChanged();
1471
+ renderTasks();
1472
+ } else if (action === 'toggle-sub') {
1473
+ const idx = parseInt(e.target.dataset.idx);
1474
+ task.subtasks[idx].checked = !task.subtasks[idx].checked;
1475
+ markChanged();
1476
+ renderTasks();
1477
+ } else if (action === 'edit-title') {
1478
+ startEditingTitle(e.target, task);
1479
+ } else if (action === 'edit-note') {
1480
+ startEditingNote(e.target, task);
1481
+ } else if (action === 'edit-subtask') {
1482
+ const idx = parseInt(e.target.dataset.idx);
1483
+ startEditingSubtask(e.target, task, idx);
1484
+ } else if (action === 'add-subtask') {
1485
+ startAddingSubtask(e.target, task);
1486
+ } else if (action === 'delete') {
1487
+ deleteTask(task);
1488
+ }
1489
+ });
1490
+
1491
+ return card;
1492
+ }
1493
+
1494
+ function startEditingTitle(titleEl, task) {
1495
+ const input = document.createElement('input');
1496
+ input.type = 'text';
1497
+ input.value = task.title;
1498
+ input.style.cssText = 'width: 100%; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 6px 10px; color: var(--text-primary); font-size: 14px; font-family: inherit; outline: none;';
1499
+
1500
+ titleEl.replaceWith(input);
1501
+ input.focus();
1502
+ input.select();
1503
+
1504
+ let saved = false;
1505
+ const saveEdit = () => {
1506
+ if (saved) return;
1507
+ saved = true;
1508
+ const newTitle = input.value.trim();
1509
+ if (newTitle && newTitle !== task.title) {
1510
+ task.title = newTitle;
1511
+ markChanged();
1512
+ }
1513
+ renderTasks();
1514
+ };
1515
+
1516
+ input.addEventListener('keydown', (e) => {
1517
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
1518
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
1519
+ });
1520
+ input.addEventListener('blur', saveEdit);
1521
+ }
1522
+
1523
+ function startEditingNote(noteEl, task) {
1524
+ const input = document.createElement('input');
1525
+ input.type = 'text';
1526
+ input.value = task.note || '';
1527
+ input.placeholder = 'Add a note...';
1528
+ input.style.cssText = 'width: 100%; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 4px 8px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
1529
+
1530
+ noteEl.replaceWith(input);
1531
+ input.focus();
1532
+
1533
+ let saved = false;
1534
+ const saveEdit = () => {
1535
+ if (saved) return;
1536
+ saved = true;
1537
+ task.note = input.value.trim();
1538
+ markChanged();
1539
+ renderTasks();
1540
+ };
1541
+
1542
+ input.addEventListener('keydown', (e) => {
1543
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
1544
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
1545
+ });
1546
+ input.addEventListener('blur', saveEdit);
1547
+ }
1548
+
1549
+ function startEditingSubtask(subtaskEl, task, idx) {
1550
+ const input = document.createElement('input');
1551
+ input.type = 'text';
1552
+ input.value = task.subtasks[idx].text;
1553
+ input.style.cssText = 'width: calc(100% - 30px); background: var(--bg-card); border: 2px solid var(--accent); border-radius: 4px; padding: 2px 6px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
1554
+
1555
+ subtaskEl.replaceWith(input);
1556
+ input.focus();
1557
+ input.select();
1558
+
1559
+ let saved = false;
1560
+ const saveEdit = () => {
1561
+ if (saved) return;
1562
+ saved = true;
1563
+ const newText = input.value.trim();
1564
+ if (newText) { task.subtasks[idx].text = newText; }
1565
+ else { task.subtasks.splice(idx, 1); }
1566
+ markChanged();
1567
+ renderTasks();
1568
+ };
1569
+
1570
+ input.addEventListener('keydown', (e) => {
1571
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
1572
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
1573
+ });
1574
+ input.addEventListener('blur', saveEdit);
1575
+ }
1576
+
1577
+ function startAddingSubtask(el, task) {
1578
+ const input = document.createElement('input');
1579
+ input.type = 'text';
1580
+ input.placeholder = 'New subtask...';
1581
+ input.style.cssText = 'width: calc(100% - 10px); background: var(--bg-card); border: 2px solid var(--accent); border-radius: 4px; padding: 2px 6px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
1582
+
1583
+ el.replaceWith(input);
1584
+ input.focus();
1585
+
1586
+ let saved = false;
1587
+ const saveEdit = () => {
1588
+ if (saved) return;
1589
+ saved = true;
1590
+ const text = input.value.trim();
1591
+ if (text) { task.subtasks.push({ text, checked: false }); markChanged(); }
1592
+ renderTasks();
1593
+ };
1594
+
1595
+ input.addEventListener('keydown', (e) => {
1596
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
1597
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
1598
+ });
1599
+ input.addEventListener('blur', saveEdit);
1600
+ }
1601
+
1602
+ function startEditingColumnTitle(titleEl, colId) {
1603
+ const section = sections.find(s => s.id === colId);
1604
+ if (!section) return;
1605
+
1606
+ const input = document.createElement('input');
1607
+ input.type = 'text';
1608
+ input.value = section.name;
1609
+ input.style.cssText = 'width: 180px; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 4px 10px; color: var(--text-primary); font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; font-family: inherit; outline: none;';
1610
+
1611
+ titleEl.replaceWith(input);
1612
+ input.focus();
1613
+ input.select();
1614
+
1615
+ let saved = false;
1616
+ const saveEdit = () => {
1617
+ if (saved) return;
1618
+ saved = true;
1619
+ const newName = input.value.trim();
1620
+ if (newName && newName !== section.name) {
1621
+ const oldId = section.id;
1622
+ section.name = newName;
1623
+ const newId = taskSectionId(newName);
1624
+ if (newId !== oldId) {
1625
+ tasks[newId] = tasks[oldId] || [];
1626
+ delete tasks[oldId];
1627
+ tasks[newId].forEach(t => t.section = newId);
1628
+ section.id = newId;
1629
+ }
1630
+ markChanged();
1631
+ }
1632
+ renderTasks();
1633
+ };
1634
+
1635
+ input.addEventListener('keydown', (e) => {
1636
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
1637
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
1638
+ });
1639
+ input.addEventListener('blur', saveEdit);
1640
+ }
1641
+
1642
+ function createColumn(id, title, items) {
1643
+ const col = document.createElement('div');
1644
+ col.className = 'column';
1645
+ col.innerHTML = `
1646
+ <div class="column-header">
1647
+ <span class="column-title" data-section-id="${id}" style="cursor: pointer;">${escapeHtml(title)}</span>
1648
+ <span class="count">${items.length}</span>
1649
+ </div>
1650
+ <div class="cards" data-column="${id}"></div>
1651
+ <div class="add-card">
1652
+ <button data-add="${id}">+ Add task</button>
1653
+ </div>
1654
+ `;
1655
+
1656
+ col.querySelector('.column-title').addEventListener('click', (e) => {
1657
+ if (!col.dragging) { startEditingColumnTitle(e.target, id); }
1658
+ });
1659
+
1660
+ const header = col.querySelector('.column-header');
1661
+ header.draggable = true;
1662
+
1663
+ header.addEventListener('dragstart', (e) => {
1664
+ e.stopPropagation();
1665
+ col.classList.add('dragging-column');
1666
+ e.dataTransfer.setData('text/column', id);
1667
+ e.dataTransfer.effectAllowed = 'move';
1668
+ });
1669
+
1670
+ header.addEventListener('dragend', () => {
1671
+ col.classList.remove('dragging-column');
1672
+ board.querySelectorAll('.column-drop-indicator').forEach(el => el.remove());
1673
+ });
1674
+
1675
+ col.addEventListener('dragover', (e) => {
1676
+ if (e.dataTransfer.types.includes('text/column')) {
1677
+ e.preventDefault();
1678
+ e.stopPropagation();
1679
+ board.querySelectorAll('.column-drop-indicator').forEach(el => el.remove());
1680
+ const indicator = document.createElement('div');
1681
+ indicator.className = 'column-drop-indicator';
1682
+ const rect = col.getBoundingClientRect();
1683
+ if (e.clientX < rect.left + rect.width / 2) { col.before(indicator); }
1684
+ else { col.after(indicator); }
1685
+ }
1686
+ });
1687
+
1688
+ col.addEventListener('drop', (e) => {
1689
+ if (e.dataTransfer.types.includes('text/column')) {
1690
+ e.preventDefault();
1691
+ e.stopPropagation();
1692
+ const fromId = e.dataTransfer.getData('text/column');
1693
+ const toId = id;
1694
+ if (fromId !== toId) {
1695
+ const rect = col.getBoundingClientRect();
1696
+ const insertBefore = e.clientX < rect.left + rect.width / 2;
1697
+ moveSection(fromId, toId, insertBefore);
1698
+ }
1699
+ board.querySelectorAll('.column-drop-indicator').forEach(el => el.remove());
1700
+ }
1701
+ });
1702
+
1703
+ const cardsContainer = col.querySelector('.cards');
1704
+ items.forEach(task => { cardsContainer.appendChild(createCard(task)); });
1705
+
1706
+ const getDropPosition = (e) => {
1707
+ const allCards = [...cardsContainer.querySelectorAll('.task-card')];
1708
+ const visibleCards = allCards.filter(c => !c.classList.contains('dragging'));
1709
+ let insertBeforeCard = null;
1710
+ let dropIndex = visibleCards.length;
1711
+ for (let i = 0; i < visibleCards.length; i++) {
1712
+ const rect = visibleCards[i].getBoundingClientRect();
1713
+ if (e.clientY < rect.top + rect.height / 2) {
1714
+ insertBeforeCard = visibleCards[i];
1715
+ dropIndex = i;
1716
+ break;
1717
+ }
1718
+ }
1719
+ return { insertBeforeCard, dropIndex };
1720
+ };
1721
+
1722
+ const showDropIndicator = (e) => {
1723
+ col.querySelectorAll('.drop-indicator').forEach(el => el.remove());
1724
+ const { insertBeforeCard } = getDropPosition(e);
1725
+ const indicator = document.createElement('div');
1726
+ indicator.className = 'drop-indicator';
1727
+ indicator.style.cssText = 'height: 3px; background: var(--accent); border-radius: 2px; margin: 5px 0;';
1728
+ if (insertBeforeCard) { cardsContainer.insertBefore(indicator, insertBeforeCard); }
1729
+ else { cardsContainer.appendChild(indicator); }
1730
+ };
1731
+
1732
+ col.addEventListener('dragover', (e) => {
1733
+ e.preventDefault();
1734
+ cardsContainer.classList.add('drag-over');
1735
+ showDropIndicator(e);
1736
+ });
1737
+
1738
+ col.addEventListener('dragleave', (e) => {
1739
+ if (!col.contains(e.relatedTarget)) {
1740
+ cardsContainer.classList.remove('drag-over');
1741
+ col.querySelectorAll('.drop-indicator').forEach(el => el.remove());
1742
+ }
1743
+ });
1744
+
1745
+ col.addEventListener('drop', (e) => {
1746
+ e.preventDefault();
1747
+ cardsContainer.classList.remove('drag-over');
1748
+ col.querySelectorAll('.drop-indicator').forEach(el => el.remove());
1749
+ const taskId = parseFloat(e.dataTransfer.getData('text/plain'));
1750
+ const { dropIndex } = getDropPosition(e);
1751
+ moveTask(taskId, id, dropIndex);
1752
+ });
1753
+
1754
+ col.querySelector(`[data-add="${id}"]`).addEventListener('click', () => {
1755
+ addNewTask(id, col.querySelector('.cards'));
1756
+ });
1757
+
1758
+ return col;
1759
+ }
1760
+
1761
+ function addNewTask(sectionId, container) {
1762
+ const existing = container.querySelector('.new-task-input');
1763
+ if (existing) return;
1764
+
1765
+ const input = document.createElement('textarea');
1766
+ input.className = 'new-task-input';
1767
+ input.placeholder = 'What needs to be done?';
1768
+ input.rows = 2;
1769
+ container.appendChild(input);
1770
+ input.focus();
1771
+
1772
+ input.addEventListener('keydown', (e) => {
1773
+ if (e.key === 'Enter' && !e.shiftKey) {
1774
+ e.preventDefault();
1775
+ const title = input.value.trim();
1776
+ if (title) {
1777
+ if (!tasks[sectionId]) tasks[sectionId] = [];
1778
+ tasks[sectionId].push({
1779
+ id: Date.now() + Math.random(),
1780
+ title,
1781
+ note: '',
1782
+ checked: false,
1783
+ subtasks: [],
1784
+ section: sectionId
1785
+ });
1786
+ markChanged();
1787
+ renderTasks();
1788
+ } else { input.remove(); }
1789
+ } else if (e.key === 'Escape') { input.remove(); }
1790
+ });
1791
+
1792
+ input.addEventListener('blur', () => { setTimeout(() => input.remove(), 100); });
1793
+ }
1794
+
1795
+ function moveSection(fromId, toId, insertBefore) {
1796
+ const fromIdx = sections.findIndex(s => s.id === fromId);
1797
+ const toIdx = sections.findIndex(s => s.id === toId);
1798
+ if (fromIdx === -1 || toIdx === -1) return;
1799
+ const [section] = sections.splice(fromIdx, 1);
1800
+ let newIdx = sections.findIndex(s => s.id === toId);
1801
+ if (!insertBefore) newIdx++;
1802
+ sections.splice(newIdx, 0, section);
1803
+ markChanged();
1804
+ renderTasks();
1805
+ }
1806
+
1807
+ function moveTask(taskId, toSectionId, dropIndex = -1) {
1808
+ let task = null;
1809
+ for (const section of sections) {
1810
+ const sectionTasks = tasks[section.id] || [];
1811
+ const idx = sectionTasks.findIndex(t => t.id === taskId);
1812
+ if (idx !== -1) {
1813
+ task = sectionTasks.splice(idx, 1)[0];
1814
+ break;
1815
+ }
1816
+ }
1817
+ if (!task) return;
1818
+ task.section = toSectionId;
1819
+ if (!tasks[toSectionId]) tasks[toSectionId] = [];
1820
+ if (dropIndex >= 0 && dropIndex <= tasks[toSectionId].length) {
1821
+ tasks[toSectionId].splice(dropIndex, 0, task);
1822
+ } else {
1823
+ tasks[toSectionId].push(task);
1824
+ }
1825
+ markChanged();
1826
+ renderTasks();
1827
+ }
1828
+
1829
+ function deleteTask(task) {
1830
+ if (!confirm(`Delete "${task.title}"?`)) return;
1831
+ for (const section of sections) {
1832
+ const sectionTasks = tasks[section.id] || [];
1833
+ const idx = sectionTasks.findIndex(t => t.id === task.id);
1834
+ if (idx !== -1) { sectionTasks.splice(idx, 1); break; }
1835
+ }
1836
+ markChanged();
1837
+ renderTasks();
1838
+ }
1839
+
1840
+ let saveTimeout = null;
1841
+ let lastModified = 0;
1842
+ let watchInterval = null;
1843
+ let isSaving = false;
1844
+
1845
+ function markChanged() {
1846
+ hasChanges = true;
1847
+ saveBtn.disabled = false;
1848
+ if (saveTimeout) clearTimeout(saveTimeout);
1849
+ saveTimeout = setTimeout(autoSave, 500);
1850
+ }
1851
+
1852
+ async function autoSave() {
1853
+ if (!taskFileHandle || !hasChanges || isSaving) return;
1854
+ isSaving = true;
1855
+ try {
1856
+ const writable = await taskFileHandle.createWritable();
1857
+ const content = toMarkdown();
1858
+ await writable.write(content);
1859
+ await writable.close();
1860
+ const file = await taskFileHandle.getFile();
1861
+ lastModified = file.lastModified;
1862
+ hasChanges = false;
1863
+ saveBtn.disabled = true;
1864
+ showStatus('Saved');
1865
+ } catch (e) {
1866
+ showStatus('Save failed: ' + e.message);
1867
+ }
1868
+ isSaving = false;
1869
+ }
1870
+
1871
+ async function checkForExternalChanges() {
1872
+ if (!taskFileHandle || hasChanges || isSaving) return;
1873
+ try {
1874
+ const file = await taskFileHandle.getFile();
1875
+ if (file.lastModified > lastModified) {
1876
+ lastModified = file.lastModified;
1877
+ const content = await file.text();
1878
+ const result = parseTaskMarkdown(content);
1879
+ sections = result.sections;
1880
+ tasks = result.tasks;
1881
+ renderTasks();
1882
+ showStatus('Reloaded');
1883
+ }
1884
+ } catch (e) { console.log('Watch error:', e); }
1885
+ }
1886
+
1887
+ function startWatching() {
1888
+ if (watchInterval) clearInterval(watchInterval);
1889
+ watchInterval = setInterval(checkForExternalChanges, 1000);
1890
+ }
1891
+
1892
+ function stopWatching() {
1893
+ if (watchInterval) { clearInterval(watchInterval); watchInterval = null; }
1894
+ }
1895
+
1896
+ function renderTasks() {
1897
+ if (currentView === 'board') { renderBoard(); }
1898
+ else { renderList(); }
1899
+ }
1900
+
1901
+ function renderBoard() {
1902
+ board.innerHTML = '';
1903
+ sections.forEach(section => {
1904
+ const sectionTasks = tasks[section.id] || [];
1905
+ board.appendChild(createColumn(section.id, section.name, sectionTasks));
1906
+ });
1907
+
1908
+ const addSectionBtn = document.createElement('div');
1909
+ addSectionBtn.className = 'column';
1910
+ addSectionBtn.style.cssText = 'background: transparent; border: 2px dashed var(--border); display: flex; align-items: center; justify-content: center; cursor: pointer; min-height: 120px;';
1911
+ addSectionBtn.innerHTML = '<span style="color: var(--text-muted); font-size: 14px; font-weight: 500;">+ Add Section</span>';
1912
+ addSectionBtn.addEventListener('click', () => startAddingSection(addSectionBtn));
1913
+ board.appendChild(addSectionBtn);
1914
+ }
1915
+
1916
+ function renderList() {
1917
+ listView.innerHTML = '';
1918
+
1919
+ if (!quickAddSection && sections.length > 0) {
1920
+ quickAddSection = sections[0].id;
1921
+ }
1922
+
1923
+ // Quick add at top
1924
+ const quickAdd = document.createElement('div');
1925
+ quickAdd.className = 'quick-add';
1926
+ quickAdd.style.cssText = 'border-bottom: 2px solid var(--border); margin-bottom: 24px; padding-bottom: 16px;';
1927
+
1928
+ const sectionName = sections.find(s => s.id === quickAddSection)?.name || 'Select section';
1929
+ quickAdd.innerHTML = `
1930
+ <span class="checkbox" style="opacity: 0.3;"></span>
1931
+ <input type="text" class="quick-add-input" placeholder="Add a task..." id="quickAddInput">
1932
+ <span class="quick-add-section" id="quickAddSectionBtn">${escapeHtml(sectionName)}</span>
1933
+ `;
1934
+ listView.appendChild(quickAdd);
1935
+
1936
+ const quickInput = document.getElementById('quickAddInput');
1937
+ const sectionBtn = document.getElementById('quickAddSectionBtn');
1938
+
1939
+ quickInput.addEventListener('keydown', (e) => {
1940
+ if (e.key === 'Enter' && quickInput.value.trim()) {
1941
+ const title = quickInput.value.trim();
1942
+ if (!tasks[quickAddSection]) tasks[quickAddSection] = [];
1943
+ tasks[quickAddSection].unshift({
1944
+ id: Date.now() + Math.random(),
1945
+ title,
1946
+ note: '',
1947
+ checked: false,
1948
+ subtasks: [],
1949
+ section: quickAddSection
1950
+ });
1951
+ quickInput.value = '';
1952
+ markChanged();
1953
+ renderTasks();
1954
+ setTimeout(() => document.getElementById('quickAddInput')?.focus(), 10);
1955
+ }
1956
+ });
1957
+
1958
+ sectionBtn.addEventListener('click', (e) => { showSectionPicker(e.target); });
1959
+
1960
+ // Render each section
1961
+ sections.forEach(section => {
1962
+ const sectionTasks = tasks[section.id] || [];
1963
+ const sectionEl = document.createElement('div');
1964
+ sectionEl.className = 'list-section';
1965
+ sectionEl.dataset.sectionId = section.id;
1966
+
1967
+ const header = document.createElement('div');
1968
+ header.className = 'list-section-header';
1969
+ header.innerHTML = `
1970
+ <span class="section-title" data-section-id="${section.id}">${escapeHtml(section.name)}</span>
1971
+ <span class="count">${sectionTasks.length}</span>
1972
+ `;
1973
+
1974
+ header.querySelector('.section-title').addEventListener('click', (e) => {
1975
+ startEditingListSectionTitle(e.target, section);
1976
+ });
1977
+
1978
+ sectionEl.appendChild(header);
1979
+
1980
+ const tasksContainer = document.createElement('div');
1981
+ tasksContainer.className = 'list-tasks-container';
1982
+ tasksContainer.dataset.sectionId = section.id;
1983
+
1984
+ sectionTasks.forEach(task => {
1985
+ tasksContainer.appendChild(createListItem(task, section));
1986
+ });
1987
+
1988
+ sectionEl.appendChild(tasksContainer);
1989
+
1990
+ // Drag-and-drop handlers for section
1991
+ const getDropPosition = (e, container) => {
1992
+ const items = [...container.querySelectorAll('.list-item:not(.dragging)')];
1993
+ let insertBeforeEl = null;
1994
+ let dropIndex = items.length;
1995
+ for (let i = 0; i < items.length; i++) {
1996
+ const rect = items[i].getBoundingClientRect();
1997
+ if (e.clientY < rect.top + rect.height / 2) {
1998
+ insertBeforeEl = items[i];
1999
+ dropIndex = i;
2000
+ break;
2001
+ }
2002
+ }
2003
+ return { insertBeforeEl, dropIndex };
2004
+ };
2005
+
2006
+ const showDropIndicator = (e) => {
2007
+ tasksContainer.querySelectorAll('.list-drop-indicator').forEach(el => el.remove());
2008
+ const { insertBeforeEl } = getDropPosition(e, tasksContainer);
2009
+ const indicator = document.createElement('div');
2010
+ indicator.className = 'list-drop-indicator';
2011
+ if (insertBeforeEl) { tasksContainer.insertBefore(indicator, insertBeforeEl); }
2012
+ else { tasksContainer.appendChild(indicator); }
2013
+ };
2014
+
2015
+ sectionEl.addEventListener('dragover', (e) => {
2016
+ e.preventDefault();
2017
+ sectionEl.classList.add('drag-over');
2018
+ showDropIndicator(e);
2019
+ });
2020
+
2021
+ sectionEl.addEventListener('dragleave', (e) => {
2022
+ if (!sectionEl.contains(e.relatedTarget)) {
2023
+ sectionEl.classList.remove('drag-over');
2024
+ tasksContainer.querySelectorAll('.list-drop-indicator').forEach(el => el.remove());
2025
+ }
2026
+ });
2027
+
2028
+ sectionEl.addEventListener('drop', (e) => {
2029
+ e.preventDefault();
2030
+ sectionEl.classList.remove('drag-over');
2031
+ tasksContainer.querySelectorAll('.list-drop-indicator').forEach(el => el.remove());
2032
+ const taskId = parseFloat(e.dataTransfer.getData('text/plain'));
2033
+ if (!taskId) return;
2034
+ const { dropIndex } = getDropPosition(e, tasksContainer);
2035
+ moveTask(taskId, section.id, dropIndex);
2036
+ });
2037
+
2038
+ listView.appendChild(sectionEl);
2039
+ });
2040
+
2041
+ // Add Section button
2042
+ const addSectionBtn = document.createElement('div');
2043
+ addSectionBtn.className = 'list-add-section';
2044
+ addSectionBtn.textContent = '+ Add Section';
2045
+ addSectionBtn.addEventListener('click', () => { startAddingListSection(addSectionBtn); });
2046
+ listView.appendChild(addSectionBtn);
2047
+ }
2048
+
2049
+ function startEditingListSectionTitle(titleEl, section) {
2050
+ const input = document.createElement('input');
2051
+ input.type = 'text';
2052
+ input.value = section.name;
2053
+ input.style.cssText = 'width: 200px; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 4px 10px; color: var(--text-primary); font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; font-family: inherit; outline: none;';
2054
+
2055
+ titleEl.replaceWith(input);
2056
+ input.focus();
2057
+ input.select();
2058
+
2059
+ let saved = false;
2060
+ const saveEdit = () => {
2061
+ if (saved) return;
2062
+ saved = true;
2063
+ const newName = input.value.trim();
2064
+ if (newName && newName !== section.name) {
2065
+ const oldId = section.id;
2066
+ section.name = newName;
2067
+ const newId = taskSectionId(newName);
2068
+ if (newId !== oldId) {
2069
+ tasks[newId] = tasks[oldId] || [];
2070
+ delete tasks[oldId];
2071
+ tasks[newId].forEach(t => t.section = newId);
2072
+ section.id = newId;
2073
+ }
2074
+ markChanged();
2075
+ }
2076
+ renderTasks();
2077
+ };
2078
+
2079
+ input.addEventListener('keydown', (e) => {
2080
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
2081
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2082
+ });
2083
+ input.addEventListener('blur', saveEdit);
2084
+ }
2085
+
2086
+ function startAddingListSection(btn) {
2087
+ const input = document.createElement('input');
2088
+ input.type = 'text';
2089
+ input.placeholder = 'Section name...';
2090
+ input.style.cssText = 'width: 100%; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 8px; padding: 12px 16px; color: var(--text-primary); font-size: 14px; font-family: inherit; outline: none; text-align: left;';
2091
+
2092
+ btn.innerHTML = '';
2093
+ btn.style.border = '2px solid var(--accent)';
2094
+ btn.style.cursor = 'default';
2095
+ btn.appendChild(input);
2096
+ input.focus();
2097
+
2098
+ let saved = false;
2099
+ const saveSection = () => {
2100
+ if (saved) return;
2101
+ saved = true;
2102
+ const name = input.value.trim();
2103
+ if (name) {
2104
+ const id = taskSectionId(name);
2105
+ if (!tasks[id]) {
2106
+ sections.push({ id, name });
2107
+ tasks[id] = [];
2108
+ markChanged();
2109
+ }
2110
+ }
2111
+ renderTasks();
2112
+ };
2113
+
2114
+ input.addEventListener('keydown', (e) => {
2115
+ if (e.key === 'Enter') { e.preventDefault(); saveSection(); }
2116
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2117
+ });
2118
+ input.addEventListener('blur', saveSection);
2119
+ }
2120
+
2121
+ function createListItem(task, section) {
2122
+ const item = document.createElement('div');
2123
+ item.className = 'list-item';
2124
+ item.draggable = true;
2125
+ item.dataset.taskId = task.id;
2126
+
2127
+ item.addEventListener('dragstart', (e) => {
2128
+ item.classList.add('dragging');
2129
+ e.dataTransfer.setData('text/plain', task.id);
2130
+ e.dataTransfer.effectAllowed = 'move';
2131
+ });
2132
+
2133
+ item.addEventListener('dragend', () => {
2134
+ item.classList.remove('dragging');
2135
+ document.querySelectorAll('.list-drop-indicator').forEach(el => el.remove());
2136
+ document.querySelectorAll('.list-section.drag-over').forEach(el => el.classList.remove('drag-over'));
2137
+ });
2138
+
2139
+ const checkbox = document.createElement('span');
2140
+ checkbox.className = `checkbox ${task.checked ? 'checked' : ''}`;
2141
+ checkbox.addEventListener('click', (e) => {
2142
+ e.stopPropagation();
2143
+ task.checked = !task.checked;
2144
+ markChanged();
2145
+ renderTasks();
2146
+ });
2147
+
2148
+ const content = document.createElement('div');
2149
+ content.className = 'list-item-content';
2150
+
2151
+ const title = document.createElement('div');
2152
+ title.className = `list-item-title ${task.checked ? 'checked' : ''}`;
2153
+ title.textContent = task.title;
2154
+ title.addEventListener('click', (e) => {
2155
+ e.stopPropagation();
2156
+ startEditingListItem(title, task);
2157
+ });
2158
+ content.appendChild(title);
2159
+
2160
+ if (task.note) {
2161
+ const note = document.createElement('div');
2162
+ note.className = 'list-item-note';
2163
+ note.textContent = task.note;
2164
+ note.addEventListener('click', (e) => {
2165
+ e.stopPropagation();
2166
+ startEditingListNote(note, task);
2167
+ });
2168
+ content.appendChild(note);
2169
+ } else {
2170
+ const addNote = document.createElement('div');
2171
+ addNote.className = 'list-item-note add-note';
2172
+ addNote.textContent = '+ Add note';
2173
+ addNote.addEventListener('click', (e) => {
2174
+ e.stopPropagation();
2175
+ startEditingListNote(addNote, task);
2176
+ });
2177
+ content.appendChild(addNote);
2178
+ }
2179
+
2180
+ if (task.subtasks && task.subtasks.length > 0) {
2181
+ const subtasksContainer = document.createElement('div');
2182
+ subtasksContainer.className = 'list-item-subtasks';
2183
+
2184
+ task.subtasks.forEach((st, idx) => {
2185
+ const subtaskEl = document.createElement('div');
2186
+ subtaskEl.className = 'list-item-subtask';
2187
+
2188
+ const stCheckbox = document.createElement('span');
2189
+ stCheckbox.className = `checkbox ${st.checked ? 'checked' : ''}`;
2190
+ stCheckbox.addEventListener('click', (e) => {
2191
+ e.stopPropagation();
2192
+ st.checked = !st.checked;
2193
+ markChanged();
2194
+ renderTasks();
2195
+ });
2196
+
2197
+ const stText = document.createElement('span');
2198
+ stText.textContent = st.text;
2199
+ if (st.checked) {
2200
+ stText.style.textDecoration = 'line-through';
2201
+ stText.style.color = 'var(--text-muted)';
2202
+ }
2203
+ stText.addEventListener('click', (e) => {
2204
+ e.stopPropagation();
2205
+ startEditingListSubtask(stText, task, idx);
2206
+ });
2207
+
2208
+ subtaskEl.appendChild(stCheckbox);
2209
+ subtaskEl.appendChild(stText);
2210
+ subtasksContainer.appendChild(subtaskEl);
2211
+ });
2212
+
2213
+ content.appendChild(subtasksContainer);
2214
+ }
2215
+
2216
+ const addSubtask = document.createElement('div');
2217
+ addSubtask.className = 'list-item-add-subtask';
2218
+ addSubtask.textContent = '+ Add subtask';
2219
+ addSubtask.addEventListener('click', (e) => {
2220
+ e.stopPropagation();
2221
+ startAddingListSubtask(addSubtask, task);
2222
+ });
2223
+ content.appendChild(addSubtask);
2224
+
2225
+ const actions = document.createElement('div');
2226
+ actions.className = 'list-item-actions';
2227
+ actions.innerHTML = '<button title="Delete task">&times;</button>';
2228
+ actions.querySelector('button').addEventListener('click', (e) => {
2229
+ e.stopPropagation();
2230
+ deleteTask(task);
2231
+ });
2232
+
2233
+ item.appendChild(checkbox);
2234
+ item.appendChild(content);
2235
+ item.appendChild(actions);
2236
+
2237
+ return item;
2238
+ }
2239
+
2240
+ function startEditingListNote(noteEl, task) {
2241
+ const input = document.createElement('input');
2242
+ input.type = 'text';
2243
+ input.value = task.note || '';
2244
+ input.placeholder = 'Add a note...';
2245
+ input.style.cssText = 'width: 100%; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 4px 8px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
2246
+
2247
+ noteEl.replaceWith(input);
2248
+ input.focus();
2249
+
2250
+ let saved = false;
2251
+ const saveEdit = () => {
2252
+ if (saved) return;
2253
+ saved = true;
2254
+ task.note = input.value.trim();
2255
+ markChanged();
2256
+ renderTasks();
2257
+ };
2258
+
2259
+ input.addEventListener('keydown', (e) => {
2260
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
2261
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2262
+ });
2263
+ input.addEventListener('blur', saveEdit);
2264
+ }
2265
+
2266
+ function startEditingListSubtask(subtaskEl, task, idx) {
2267
+ const input = document.createElement('input');
2268
+ input.type = 'text';
2269
+ input.value = task.subtasks[idx].text;
2270
+ input.style.cssText = 'width: calc(100% - 30px); background: var(--bg-card); border: 2px solid var(--accent); border-radius: 4px; padding: 2px 6px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
2271
+
2272
+ subtaskEl.replaceWith(input);
2273
+ input.focus();
2274
+ input.select();
2275
+
2276
+ let saved = false;
2277
+ const saveEdit = () => {
2278
+ if (saved) return;
2279
+ saved = true;
2280
+ const newText = input.value.trim();
2281
+ if (newText) { task.subtasks[idx].text = newText; }
2282
+ else { task.subtasks.splice(idx, 1); }
2283
+ markChanged();
2284
+ renderTasks();
2285
+ };
2286
+
2287
+ input.addEventListener('keydown', (e) => {
2288
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
2289
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2290
+ });
2291
+ input.addEventListener('blur', saveEdit);
2292
+ }
2293
+
2294
+ function startAddingListSubtask(el, task) {
2295
+ const input = document.createElement('input');
2296
+ input.type = 'text';
2297
+ input.placeholder = 'New subtask...';
2298
+ input.style.cssText = 'width: calc(100% - 10px); background: var(--bg-card); border: 2px solid var(--accent); border-radius: 4px; padding: 2px 6px; color: var(--text-primary); font-size: 13px; font-family: inherit; outline: none;';
2299
+
2300
+ el.replaceWith(input);
2301
+ input.focus();
2302
+
2303
+ let saved = false;
2304
+ const saveEdit = () => {
2305
+ if (saved) return;
2306
+ saved = true;
2307
+ const text = input.value.trim();
2308
+ if (text) {
2309
+ if (!task.subtasks) task.subtasks = [];
2310
+ task.subtasks.push({ text, checked: false });
2311
+ markChanged();
2312
+ }
2313
+ renderTasks();
2314
+ };
2315
+
2316
+ input.addEventListener('keydown', (e) => {
2317
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
2318
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2319
+ });
2320
+ input.addEventListener('blur', saveEdit);
2321
+ }
2322
+
2323
+ function startEditingListItem(titleEl, task) {
2324
+ const input = document.createElement('input');
2325
+ input.type = 'text';
2326
+ input.value = task.title;
2327
+ input.style.cssText = 'width: 100%; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 6px; padding: 8px 12px; color: var(--text-primary); font-size: 15px; font-family: inherit; outline: none;';
2328
+
2329
+ titleEl.replaceWith(input);
2330
+ input.focus();
2331
+ input.select();
2332
+
2333
+ let saved = false;
2334
+ const saveEdit = () => {
2335
+ if (saved) return;
2336
+ saved = true;
2337
+ const newTitle = input.value.trim();
2338
+ if (newTitle && newTitle !== task.title) {
2339
+ task.title = newTitle;
2340
+ markChanged();
2341
+ }
2342
+ renderTasks();
2343
+ };
2344
+
2345
+ input.addEventListener('keydown', (e) => {
2346
+ if (e.key === 'Enter') { e.preventDefault(); saveEdit(); }
2347
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2348
+ });
2349
+ input.addEventListener('blur', saveEdit);
2350
+ }
2351
+
2352
+ function showSectionPicker(anchorEl) {
2353
+ document.querySelectorAll('.section-picker').forEach(el => el.remove());
2354
+ const picker = document.createElement('div');
2355
+ picker.className = 'section-picker';
2356
+ const rect = anchorEl.getBoundingClientRect();
2357
+ picker.style.top = (rect.bottom + 4) + 'px';
2358
+ picker.style.right = (window.innerWidth - rect.right) + 'px';
2359
+
2360
+ sections.forEach(section => {
2361
+ const btn = document.createElement('button');
2362
+ btn.textContent = section.name;
2363
+ btn.addEventListener('click', () => {
2364
+ quickAddSection = section.id;
2365
+ picker.remove();
2366
+ renderTasks();
2367
+ setTimeout(() => document.getElementById('quickAddInput')?.focus(), 10);
2368
+ });
2369
+ picker.appendChild(btn);
2370
+ });
2371
+
2372
+ document.body.appendChild(picker);
2373
+ setTimeout(() => {
2374
+ document.addEventListener('click', function closeHandler(e) {
2375
+ if (!picker.contains(e.target)) {
2376
+ picker.remove();
2377
+ document.removeEventListener('click', closeHandler);
2378
+ }
2379
+ });
2380
+ }, 10);
2381
+ }
2382
+
2383
+ function switchTaskView(view) {
2384
+ currentView = view;
2385
+ if (view === 'list') {
2386
+ listView.style.display = 'block';
2387
+ board.style.display = 'none';
2388
+ listViewBtn.classList.add('active');
2389
+ boardViewBtn.classList.remove('active');
2390
+ } else {
2391
+ listView.style.display = 'none';
2392
+ board.style.display = 'flex';
2393
+ listViewBtn.classList.remove('active');
2394
+ boardViewBtn.classList.add('active');
2395
+ }
2396
+ renderTasks();
2397
+ }
2398
+
2399
+ listViewBtn.addEventListener('click', () => switchTaskView('list'));
2400
+ boardViewBtn.addEventListener('click', () => switchTaskView('board'));
2401
+
2402
+ function startAddingSection(btn) {
2403
+ const input = document.createElement('input');
2404
+ input.type = 'text';
2405
+ input.placeholder = 'Section name...';
2406
+ input.style.cssText = 'width: 220px; background: var(--bg-card); border: 2px solid var(--accent); border-radius: 8px; padding: 10px 14px; color: var(--text-primary); font-size: 14px; font-family: inherit; outline: none;';
2407
+
2408
+ btn.innerHTML = '';
2409
+ btn.style.cssText = 'background: var(--bg-secondary); border: 2px dashed var(--accent); display: flex; align-items: center; justify-content: center; cursor: default; min-height: 120px; min-width: 340px; border-radius: 12px;';
2410
+ btn.appendChild(input);
2411
+ input.focus();
2412
+
2413
+ let saved = false;
2414
+ const saveSection = () => {
2415
+ if (saved) return;
2416
+ saved = true;
2417
+ const name = input.value.trim();
2418
+ if (name) {
2419
+ const id = taskSectionId(name);
2420
+ if (!tasks[id]) {
2421
+ sections.push({ id, name });
2422
+ tasks[id] = [];
2423
+ markChanged();
2424
+ }
2425
+ }
2426
+ renderTasks();
2427
+ };
2428
+
2429
+ input.addEventListener('keydown', (e) => {
2430
+ if (e.key === 'Enter') { e.preventDefault(); saveSection(); }
2431
+ else if (e.key === 'Escape') { saved = true; renderTasks(); }
2432
+ });
2433
+ input.addEventListener('blur', saveSection);
2434
+ }
2435
+
2436
+ async function openTaskFile() {
2437
+ try {
2438
+ [taskFileHandle] = await window.showOpenFilePicker({
2439
+ types: [{ description: 'Markdown', accept: { 'text/markdown': ['.md'] } }]
2440
+ });
2441
+ const file = await taskFileHandle.getFile();
2442
+ const content = await file.text();
2443
+ lastModified = file.lastModified;
2444
+ const result = parseTaskMarkdown(content);
2445
+ sections = result.sections;
2446
+ tasks = result.tasks;
2447
+ switchTaskView('board');
2448
+ startWatching();
2449
+ taskFileName = file.name;
2450
+ filePathEl.textContent = file.name;
2451
+ showStatus('Loaded ' + file.name);
2452
+ } catch (e) {
2453
+ if (e.name !== 'AbortError') { showStatus('Error: ' + e.message); }
2454
+ }
2455
+ }
2456
+
2457
+ openTaskBtn.addEventListener('click', openTaskFile);
2458
+ document.getElementById('openBtnLarge')?.addEventListener('click', openTaskFile);
2459
+
2460
+ saveBtn.addEventListener('click', async () => {
2461
+ if (!taskFileHandle) return;
2462
+ try {
2463
+ const writable = await taskFileHandle.createWritable();
2464
+ await writable.write(toMarkdown());
2465
+ await writable.close();
2466
+ hasChanges = false;
2467
+ saveBtn.disabled = true;
2468
+ showStatus('Saved');
2469
+ } catch (e) { showStatus('Error: ' + e.message); }
2470
+ });
2471
+
2472
+ window.addEventListener('beforeunload', (e) => {
2473
+ if (hasChanges) { e.preventDefault(); e.returnValue = ''; }
2474
+ });
2475
+
2476
+ // ============================================================
2477
+ // ===== MEMORY FUNCTIONALITY =====
2478
+ // ============================================================
2479
+
2480
+ let memoryDirHandle = null;
2481
+ let memoryData = {
2482
+ claudeMd: null,
2483
+ memoryFiles: [],
2484
+ memoryDirs: {}
2485
+ };
2486
+
2487
+ const memoryEmptyState = document.getElementById('memoryEmptyState');
2488
+ const memoryMainContent = document.getElementById('memoryMainContent');
2489
+ const memoryTabsContainer = document.getElementById('memoryTabsContainer');
2490
+ const memoryContentContainer = document.getElementById('memoryContentContainer');
2491
+ const modalOverlay = document.getElementById('modalOverlay');
2492
+
2493
+ function escapeHtml(text) {
2494
+ return String(text || '')
2495
+ .replace(/&/g, '&amp;')
2496
+ .replace(/</g, '&lt;')
2497
+ .replace(/>/g, '&gt;')
2498
+ .replace(/"/g, '&quot;')
2499
+ .replace(/'/g, '&#39;');
2500
+ }
2501
+
2502
+ function parseMemoryMarkdown(content) {
2503
+ const parsed = {
2504
+ title: '',
2505
+ fields: {},
2506
+ sections: {},
2507
+ tables: [],
2508
+ rawContent: content
2509
+ };
2510
+
2511
+ const lines = content.split('\n');
2512
+ let currentSection = '_intro';
2513
+ parsed.sections[currentSection] = [];
2514
+
2515
+ for (let i = 0; i < lines.length; i++) {
2516
+ const line = lines[i];
2517
+
2518
+ if (line.match(/^# /)) {
2519
+ parsed.title = line.replace(/^# /, '').trim();
2520
+ continue;
2521
+ }
2522
+
2523
+ if (line.match(/^## /)) {
2524
+ currentSection = line.replace(/^## /, '').trim();
2525
+ parsed.sections[currentSection] = [];
2526
+ continue;
2527
+ }
2528
+
2529
+ const kvMatch = line.match(/^\*\*(.+?):\*\*\s*(.*)$/);
2530
+ if (kvMatch) {
2531
+ parsed.fields[kvMatch[1]] = kvMatch[2];
2532
+ continue;
2533
+ }
2534
+
2535
+ parsed.sections[currentSection].push(line);
2536
+ }
2537
+
2538
+ for (const key in parsed.sections) {
2539
+ parsed.sections[key] = parsed.sections[key].join('\n').trim();
2540
+ }
2541
+
2542
+ const tableRegex = /\|(.+)\|\n\|[-| ]+\|\n((?:\|.+\|\n?)+)/g;
2543
+ let match;
2544
+ while ((match = tableRegex.exec(content)) !== null) {
2545
+ const headers = match[1].split('|').map(h => h.trim()).filter(h => h);
2546
+ const rowLines = match[2].trim().split('\n');
2547
+ const rows = rowLines.map(row => row.split('|').map(c => c.trim()).filter(c => c));
2548
+ parsed.tables.push({ headers, rows });
2549
+ }
2550
+
2551
+ return parsed;
2552
+ }
2553
+
2554
+ function getPreview(content, maxLength = 150) {
2555
+ let preview = content
2556
+ .replace(/^#+ .+$/gm, '')
2557
+ .replace(/\*\*(.+?):\*\*.*/g, '')
2558
+ .replace(/\|.+\|/g, '')
2559
+ .replace(/[-=]{3,}/g, '')
2560
+ .trim()
2561
+ .substring(0, maxLength);
2562
+ if (content.length > maxLength) preview += '...';
2563
+ return preview;
2564
+ }
2565
+
2566
+ function getDisplayName(filename) {
2567
+ return filename
2568
+ .replace('.md', '')
2569
+ .split(/[-_]/)
2570
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
2571
+ .join(' ');
2572
+ }
2573
+
2574
+ async function loadMemoryDirectory() {
2575
+ try {
2576
+ memoryDirHandle = await window.showDirectoryPicker();
2577
+
2578
+ memoryData = { claudeMd: null, memoryFiles: [], memoryDirs: {} };
2579
+
2580
+ try {
2581
+ const claudeFileHandle = await memoryDirHandle.getFileHandle('CLAUDE.md');
2582
+ const file = await claudeFileHandle.getFile();
2583
+ memoryData.claudeMd = { content: await file.text(), fileHandle: claudeFileHandle };
2584
+ } catch (e) { console.log('No CLAUDE.md found'); }
2585
+
2586
+ try {
2587
+ const memoryDir = await memoryDirHandle.getDirectoryHandle('memory');
2588
+ for await (const entry of memoryDir.values()) {
2589
+ if (entry.kind === 'file' && entry.name.endsWith('.md')) {
2590
+ const file = await entry.getFile();
2591
+ memoryData.memoryFiles.push({
2592
+ name: entry.name,
2593
+ content: await file.text(),
2594
+ fileHandle: entry
2595
+ });
2596
+ } else if (entry.kind === 'directory') {
2597
+ memoryData.memoryDirs[entry.name] = [];
2598
+ const subDirHandle = entry;
2599
+ for await (const subEntry of subDirHandle.values()) {
2600
+ if (subEntry.kind === 'file' && subEntry.name.endsWith('.md')) {
2601
+ const file = await subEntry.getFile();
2602
+ const content = await file.text();
2603
+ memoryData.memoryDirs[entry.name].push({
2604
+ name: subEntry.name,
2605
+ content: content,
2606
+ fileHandle: subEntry,
2607
+ dirHandle: subDirHandle,
2608
+ parsed: parseMemoryMarkdown(content)
2609
+ });
2610
+ }
2611
+ }
2612
+ }
2613
+ }
2614
+ } catch (e) { console.log('No memory/ directory found'); }
2615
+
2616
+ renderMemory();
2617
+ memoryEmptyState.style.display = 'none';
2618
+ memoryMainContent.style.display = 'flex';
2619
+ filePathEl.textContent = memoryDirHandle.name;
2620
+ showStatus('Loaded memory from ' + memoryDirHandle.name);
2621
+
2622
+ } catch (e) {
2623
+ if (e.name !== 'AbortError') { showStatus('Error: ' + e.message); }
2624
+ }
2625
+ }
2626
+
2627
+ function renderMemory() {
2628
+ renderMemoryTabs();
2629
+ renderMemoryContent();
2630
+ }
2631
+
2632
+ function renderMemoryTabs() {
2633
+ let html = '';
2634
+
2635
+ if (memoryData.claudeMd) {
2636
+ html += `<button class="memory-tab active" data-tab="overview">Overview</button>`;
2637
+ }
2638
+
2639
+ for (const file of memoryData.memoryFiles) {
2640
+ const name = file.name.replace('.md', '');
2641
+ html += `<button class="memory-tab${!memoryData.claudeMd && memoryData.memoryFiles[0] === file ? ' active' : ''}" data-tab="file-${escapeHtml(name)}">${escapeHtml(name)}</button>`;
2642
+ }
2643
+
2644
+ for (const dirName of Object.keys(memoryData.memoryDirs).sort()) {
2645
+ const files = memoryData.memoryDirs[dirName];
2646
+ let count;
2647
+ if (dirName === 'context') {
2648
+ count = 0;
2649
+ for (const file of files) {
2650
+ const p = file.parsed;
2651
+ count += Object.keys(p.fields).length;
2652
+ for (const table of p.tables) {
2653
+ count += table.rows.length;
2654
+ }
2655
+ for (const [sName, sContent] of Object.entries(p.sections)) {
2656
+ if (sContent && sName !== '_intro') {
2657
+ count += sContent.split('\n').filter(l => l.trim() && !l.trim().startsWith('|')).length;
2658
+ }
2659
+ }
2660
+ }
2661
+ } else {
2662
+ count = files.length;
2663
+ }
2664
+ html += `<button class="memory-tab" data-tab="dir-${escapeHtml(dirName)}">${escapeHtml(dirName)} <span class="count">${count}</span></button>`;
2665
+ }
2666
+
2667
+ memoryTabsContainer.innerHTML = html;
2668
+
2669
+ memoryTabsContainer.querySelectorAll('.memory-tab').forEach(tab => {
2670
+ tab.addEventListener('click', () => {
2671
+ memoryTabsContainer.querySelectorAll('.memory-tab').forEach(t => t.classList.remove('active'));
2672
+ tab.classList.add('active');
2673
+ renderMemoryContent();
2674
+ });
2675
+ });
2676
+ }
2677
+
2678
+ function renderMemoryContent() {
2679
+ const activeTab = memoryTabsContainer.querySelector('.memory-tab.active');
2680
+ if (!activeTab) return;
2681
+
2682
+ const tabId = activeTab.dataset.tab;
2683
+
2684
+ if (tabId === 'overview') { renderMemoryOverview(); }
2685
+ else if (tabId.startsWith('file-')) {
2686
+ const fileName = tabId.replace('file-', '') + '.md';
2687
+ renderMemoryFile(fileName);
2688
+ } else if (tabId.startsWith('dir-')) {
2689
+ const dirName = tabId.replace('dir-', '');
2690
+ renderMemoryDirectory(dirName);
2691
+ }
2692
+ }
2693
+
2694
+ function renderMemoryOverview() {
2695
+ if (!memoryData.claudeMd) return;
2696
+
2697
+ let statsHtml = '<div class="stats">';
2698
+ for (const [dirName, files] of Object.entries(memoryData.memoryDirs)) {
2699
+ let count;
2700
+ if (dirName === 'context') {
2701
+ count = 0;
2702
+ for (const file of files) {
2703
+ const p = file.parsed;
2704
+ count += Object.keys(p.fields).length;
2705
+ for (const table of p.tables) { count += table.rows.length; }
2706
+ for (const [sName, sContent] of Object.entries(p.sections)) {
2707
+ if (sContent && sName !== '_intro') {
2708
+ count += sContent.split('\n').filter(l => l.trim() && !l.trim().startsWith('|')).length;
2709
+ }
2710
+ }
2711
+ }
2712
+ } else {
2713
+ count = files.length;
2714
+ }
2715
+ statsHtml += `
2716
+ <div class="stat">
2717
+ <div class="stat-value">${count}</div>
2718
+ <div class="stat-label">${escapeHtml(dirName)}</div>
2719
+ </div>
2720
+ `;
2721
+ }
2722
+ statsHtml += '</div>';
2723
+
2724
+ const claudeContent = renderMarkdownToHtml(memoryData.claudeMd.content);
2725
+
2726
+ memoryContentContainer.innerHTML = `
2727
+ ${statsHtml}
2728
+ <div class="file-card">
2729
+ <div class="file-card-header" onclick="this.nextElementSibling.classList.toggle('expanded'); this.querySelector('.toggle').textContent = this.nextElementSibling.classList.contains('expanded') ? '\u2212' : '+'">
2730
+ <span class="file-card-title">CLAUDE.md</span>
2731
+ <span class="toggle" style="color: var(--text-muted);">&minus;</span>
2732
+ </div>
2733
+ <div class="file-card-content expanded markdown-content">${claudeContent}</div>
2734
+ </div>
2735
+ <button onclick="openEditModal('CLAUDE.md', 'claudeMd')" style="margin-top: 10px;">Edit CLAUDE.md</button>
2736
+ `;
2737
+ }
2738
+
2739
+ function renderMemoryFile(fileName) {
2740
+ const file = memoryData.memoryFiles.find(f => f.name === fileName);
2741
+ if (!file) return;
2742
+
2743
+ const content = renderMarkdownToHtml(file.content);
2744
+
2745
+ memoryContentContainer.innerHTML = `
2746
+ <div class="file-card">
2747
+ <div class="file-card-header">
2748
+ <span class="file-card-title">${escapeHtml(fileName)}</span>
2749
+ </div>
2750
+ <div class="file-card-content expanded markdown-content">${content}</div>
2751
+ </div>
2752
+ <button id="editMemoryFileBtn" style="margin-top: 10px;">Edit ${escapeHtml(fileName)}</button>
2753
+ `;
2754
+
2755
+ document.getElementById('editMemoryFileBtn').addEventListener('click', () => openEditModal(fileName, 'memoryFile'));
2756
+ }
2757
+
2758
+ function renderMemoryDirectory(dirName) {
2759
+ const files = memoryData.memoryDirs[dirName] || [];
2760
+
2761
+ // Context directory uses flat list view
2762
+ if (dirName === 'context') {
2763
+ renderMemoryDirectoryFlat(dirName, files);
2764
+ return;
2765
+ }
2766
+
2767
+ // All other directories use card grid view
2768
+ let html = `
2769
+ <div class="search-box">
2770
+ <span style="color: var(--text-muted);">&#128269;</span>
2771
+ <input type="text" placeholder="Search ${escapeHtml(dirName)}..." id="dirSearch">
2772
+ </div>
2773
+ <div class="memory-grid" id="dirGrid">
2774
+ `;
2775
+
2776
+ for (const file of files) {
2777
+ const p = file.parsed;
2778
+ const title = p.title || getDisplayName(file.name);
2779
+
2780
+ let fieldsHtml = '';
2781
+ const fieldEntries = Object.entries(p.fields).slice(0, 3);
2782
+ if (fieldEntries.length > 0) {
2783
+ fieldsHtml = '<div class="memory-card-fields">';
2784
+ for (const [key, value] of fieldEntries) {
2785
+ fieldsHtml += `
2786
+ <div class="memory-card-field">
2787
+ <span class="memory-card-field-label">${escapeHtml(key)}</span>
2788
+ <span class="memory-card-field-value">${escapeHtml(value)}</span>
2789
+ </div>
2790
+ `;
2791
+ }
2792
+ fieldsHtml += '</div>';
2793
+ }
2794
+
2795
+ let preview = '';
2796
+ for (const [sectionName, sectionContent] of Object.entries(p.sections)) {
2797
+ if (sectionContent && sectionName !== '_intro') {
2798
+ preview = getPreview(sectionContent, 100);
2799
+ break;
2800
+ }
2801
+ }
2802
+ if (!preview) preview = getPreview(p.rawContent, 100);
2803
+
2804
+ html += `
2805
+ <div class="memory-card" data-file-name="${escapeHtml(file.name)}" data-search="${escapeHtml((title + ' ' + JSON.stringify(p.fields) + ' ' + p.rawContent).toLowerCase())}">
2806
+ <div class="memory-card-title">${escapeHtml(title)}</div>
2807
+ ${fieldsHtml}
2808
+ <div class="memory-card-preview">${escapeHtml(preview)}</div>
2809
+ </div>
2810
+ `;
2811
+ }
2812
+
2813
+ html += `
2814
+ <div class="add-btn">
2815
+ + Add to ${escapeHtml(dirName)}
2816
+ </div>
2817
+ </div>`;
2818
+
2819
+ memoryContentContainer.innerHTML = html;
2820
+
2821
+ document.getElementById('dirSearch').addEventListener('input', (e) => filterMemoryDirectory(dirName, e.target.value));
2822
+ memoryContentContainer.querySelectorAll('.memory-card').forEach(cardEl => {
2823
+ cardEl.addEventListener('click', () => openFileModal(dirName, cardEl.dataset.fileName));
2824
+ });
2825
+ memoryContentContainer.querySelector('.add-btn').addEventListener('click', () => openNewFileModal(dirName));
2826
+ }
2827
+
2828
+ function renderMemoryDirectoryFlat(dirName, files) {
2829
+ let html = `
2830
+ <div class="search-box">
2831
+ <span style="color: var(--text-muted);">&#128269;</span>
2832
+ <input type="text" placeholder="Search ${escapeHtml(dirName)}..." id="dirSearch">
2833
+ </div>
2834
+ <div id="dirGrid">
2835
+ `;
2836
+
2837
+ for (const file of files) {
2838
+ const p = file.parsed;
2839
+
2840
+ // Render fields as a key-value table
2841
+ const fieldEntries = Object.entries(p.fields);
2842
+ if (fieldEntries.length > 0) {
2843
+ html += `<div class="file-card" style="margin-bottom: 16px;"><table class="memory-flat-table"><tbody>`;
2844
+ for (const [key, value] of fieldEntries) {
2845
+ html += `<tr data-search="${escapeHtml((key + ' ' + value).toLowerCase())}"><td>${escapeHtml(key)}</td><td>${escapeHtml(value)}</td></tr>`;
2846
+ }
2847
+ html += `</tbody></table></div>`;
2848
+ }
2849
+
2850
+ // Render parsed tables (teams, tools, etc.) as proper HTML tables
2851
+ for (const table of p.tables) {
2852
+ html += `<div class="file-card" style="margin-bottom: 16px;"><table class="memory-flat-table"><thead><tr>`;
2853
+ for (const h of table.headers) {
2854
+ html += `<th>${escapeHtml(h)}</th>`;
2855
+ }
2856
+ html += `</tr></thead><tbody>`;
2857
+ for (const row of table.rows) {
2858
+ const searchData = row.join(' ').toLowerCase();
2859
+ html += `<tr data-search="${escapeHtml(searchData)}">`;
2860
+ for (const cell of row) {
2861
+ html += `<td>${escapeHtml(cell)}</td>`;
2862
+ }
2863
+ html += `</tr>`;
2864
+ }
2865
+ html += `</tbody></table></div>`;
2866
+ }
2867
+
2868
+ // Render non-table section content as list items
2869
+ for (const [sectionName, sectionContent] of Object.entries(p.sections)) {
2870
+ if (!sectionContent || sectionName === '_intro') continue;
2871
+ const lines = sectionContent.split('\n').filter(l => l.trim() && !l.trim().startsWith('|'));
2872
+ if (lines.length === 0) continue;
2873
+ html += `<div class="file-card" style="margin-bottom: 16px;"><table class="memory-flat-table"><thead><tr><th colspan="2">${escapeHtml(sectionName)}</th></tr></thead><tbody>`;
2874
+ for (const line of lines) {
2875
+ const cleanLine = line.replace(/^[-*]\s*/, '').replace(/\*\*(.+?)\*\*/g, '$1').trim();
2876
+ if (!cleanLine) continue;
2877
+ html += `<tr data-search="${escapeHtml((sectionName + ' ' + cleanLine).toLowerCase())}"><td colspan="2">${escapeHtml(cleanLine)}</td></tr>`;
2878
+ }
2879
+ html += `</tbody></table></div>`;
2880
+ }
2881
+ }
2882
+
2883
+ html += `</div>`;
2884
+ memoryContentContainer.innerHTML = html;
2885
+
2886
+ document.getElementById('dirSearch').addEventListener('input', (e) => filterMemoryDirectory(dirName, e.target.value));
2887
+ }
2888
+
2889
+ function filterMemoryDirectory(dirName, searchTerm) {
2890
+ const grid = document.getElementById('dirGrid');
2891
+ const items = grid.querySelectorAll('.memory-card, tr[data-search]');
2892
+ const search = searchTerm.toLowerCase();
2893
+ items.forEach(item => {
2894
+ const searchData = item.dataset.search || '';
2895
+ item.style.display = (!search || searchData.includes(search)) ? '' : 'none';
2896
+ });
2897
+ }
2898
+
2899
+ function renderMarkdownToHtml(md) {
2900
+ let html = escapeHtml(md);
2901
+
2902
+ html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>');
2903
+ html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>');
2904
+ html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>');
2905
+ html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
2906
+ html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
2907
+ html = html.replace(/```[\s\S]*?```/g, match => {
2908
+ const code = match.replace(/```\w*\n?/g, '');
2909
+ return '<pre><code>' + code + '</code></pre>';
2910
+ });
2911
+ html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
2912
+ html = html.replace(/(\|.+\|\n\|[-| ]+\|\n(?:\|.+\|\n?)+)/g, match => {
2913
+ const lines = match.trim().split('\n');
2914
+ const headers = lines[0].split('|').filter(c => c.trim());
2915
+ const rows = lines.slice(2).map(row => row.split('|').filter(c => c.trim()));
2916
+ let table = '<table><thead><tr>';
2917
+ headers.forEach(h => table += `<th>${h.trim()}</th>`);
2918
+ table += '</tr></thead><tbody>';
2919
+ rows.forEach(row => {
2920
+ table += '<tr>';
2921
+ row.forEach(cell => table += `<td>${cell.trim()}</td>`);
2922
+ table += '</tr>';
2923
+ });
2924
+ table += '</tbody></table>';
2925
+ return table;
2926
+ });
2927
+ html = html.replace(/^- (.+)$/gm, '<li>$1</li>');
2928
+ html = html.replace(/(<li>.*<\/li>\n?)+/g, '<ul>$&</ul>');
2929
+ html = html.replace(/^(?!<[hupol]|<li|<table|<pre)(.+)$/gm, '<p>$1</p>');
2930
+ html = html.replace(/<p><\/p>/g, '');
2931
+ html = html.replace(/<p>\s*<\/p>/g, '');
2932
+
2933
+ return html;
2934
+ }
2935
+
2936
+ // Modal functions
2937
+ function openFileModal(dirName, fileName) {
2938
+ const files = memoryData.memoryDirs[dirName];
2939
+ const file = files.find(f => f.name === fileName);
2940
+ if (!file) return;
2941
+
2942
+ document.getElementById('modalTitle').textContent = getDisplayName(fileName);
2943
+ document.getElementById('modalBody').innerHTML = `
2944
+ <div class="markdown-content" style="margin-bottom: 20px;">
2945
+ ${renderMarkdownToHtml(file.content)}
2946
+ </div>
2947
+ <div class="form-group">
2948
+ <label>Edit Raw Markdown</label>
2949
+ <textarea id="editContent">${escapeHtml(file.content)}</textarea>
2950
+ </div>
2951
+ `;
2952
+
2953
+ modalOverlay.classList.add('visible');
2954
+ modalOverlay.dataset.type = 'dirFile';
2955
+ modalOverlay.dataset.dirName = dirName;
2956
+ modalOverlay.dataset.fileName = fileName;
2957
+ }
2958
+
2959
+ function openNewFileModal(dirName) {
2960
+ document.getElementById('modalTitle').textContent = `Add to ${dirName}`;
2961
+
2962
+ let template = '# New Entry\n\n';
2963
+ const existingFiles = memoryData.memoryDirs[dirName];
2964
+ if (existingFiles && existingFiles.length > 0) {
2965
+ const sample = existingFiles[0].parsed;
2966
+ for (const key of Object.keys(sample.fields)) {
2967
+ template += `**${key}:** \n`;
2968
+ }
2969
+ template += '\n';
2970
+ for (const section of Object.keys(sample.sections)) {
2971
+ if (section !== '_intro') {
2972
+ template += `## ${section}\n\n`;
2973
+ }
2974
+ }
2975
+ }
2976
+
2977
+ document.getElementById('modalBody').innerHTML = `
2978
+ <div class="form-group">
2979
+ <label>Filename (without .md)</label>
2980
+ <input type="text" id="newFileName" placeholder="my-new-entry" style="width: 100%; background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; color: var(--text-primary); font-size: 14px; font-family: inherit; margin-bottom: 16px;">
2981
+ </div>
2982
+ <div class="form-group">
2983
+ <label>Content</label>
2984
+ <textarea id="editContent">${escapeHtml(template)}</textarea>
2985
+ </div>
2986
+ `;
2987
+
2988
+ modalOverlay.classList.add('visible');
2989
+ modalOverlay.dataset.type = 'newDirFile';
2990
+ modalOverlay.dataset.dirName = dirName;
2991
+ }
2992
+
2993
+ function openEditModal(fileName, type) {
2994
+ let content = '';
2995
+ if (type === 'claudeMd') {
2996
+ content = memoryData.claudeMd.content;
2997
+ } else if (type === 'memoryFile') {
2998
+ const file = memoryData.memoryFiles.find(f => f.name === fileName);
2999
+ if (file) content = file.content;
3000
+ }
3001
+
3002
+ document.getElementById('modalTitle').textContent = `Edit ${fileName}`;
3003
+ document.getElementById('modalBody').innerHTML = `
3004
+ <div class="form-group">
3005
+ <label>Content</label>
3006
+ <textarea id="editContent" style="min-height: 400px;">${escapeHtml(content)}</textarea>
3007
+ </div>
3008
+ `;
3009
+
3010
+ modalOverlay.classList.add('visible');
3011
+ modalOverlay.dataset.type = type;
3012
+ modalOverlay.dataset.fileName = fileName;
3013
+ }
3014
+
3015
+ function closeModal() {
3016
+ modalOverlay.classList.remove('visible');
3017
+ }
3018
+
3019
+ async function saveModal() {
3020
+ const type = modalOverlay.dataset.type;
3021
+ const content = document.getElementById('editContent').value;
3022
+
3023
+ try {
3024
+ if (type === 'claudeMd') {
3025
+ memoryData.claudeMd.content = content;
3026
+ const writable = await memoryData.claudeMd.fileHandle.createWritable();
3027
+ await writable.write(content);
3028
+ await writable.close();
3029
+ showStatus('Saved CLAUDE.md');
3030
+
3031
+ } else if (type === 'memoryFile') {
3032
+ const fileName = modalOverlay.dataset.fileName;
3033
+ const file = memoryData.memoryFiles.find(f => f.name === fileName);
3034
+ if (file) {
3035
+ file.content = content;
3036
+ const writable = await file.fileHandle.createWritable();
3037
+ await writable.write(content);
3038
+ await writable.close();
3039
+ showStatus('Saved ' + fileName);
3040
+ }
3041
+
3042
+ } else if (type === 'dirFile') {
3043
+ const dirName = modalOverlay.dataset.dirName;
3044
+ const fileName = modalOverlay.dataset.fileName;
3045
+ const files = memoryData.memoryDirs[dirName];
3046
+ const file = files.find(f => f.name === fileName);
3047
+ if (file) {
3048
+ file.content = content;
3049
+ file.parsed = parseMemoryMarkdown(content);
3050
+ const writable = await file.fileHandle.createWritable();
3051
+ await writable.write(content);
3052
+ await writable.close();
3053
+ showStatus('Saved ' + fileName);
3054
+ }
3055
+
3056
+ } else if (type === 'newDirFile') {
3057
+ const dirName = modalOverlay.dataset.dirName;
3058
+ let fileName = document.getElementById('newFileName').value.trim();
3059
+ if (!fileName) { showStatus('Please enter a filename'); return; }
3060
+ if (!fileName.endsWith('.md')) fileName += '.md';
3061
+
3062
+ const memoryDir = await memoryDirHandle.getDirectoryHandle('memory');
3063
+ const subDir = await memoryDir.getDirectoryHandle(dirName, { create: true });
3064
+ const fileHandle = await subDir.getFileHandle(fileName, { create: true });
3065
+
3066
+ const writable = await fileHandle.createWritable();
3067
+ await writable.write(content);
3068
+ await writable.close();
3069
+
3070
+ memoryData.memoryDirs[dirName].push({
3071
+ name: fileName,
3072
+ content: content,
3073
+ fileHandle: fileHandle,
3074
+ dirHandle: subDir,
3075
+ parsed: parseMemoryMarkdown(content)
3076
+ });
3077
+
3078
+ showStatus('Created ' + fileName);
3079
+ }
3080
+
3081
+ closeModal();
3082
+ renderMemory();
3083
+
3084
+ } catch (e) {
3085
+ showStatus('Error saving: ' + e.message);
3086
+ }
3087
+ }
3088
+
3089
+ // Memory event listeners
3090
+ openMemoryBtn.addEventListener('click', loadMemoryDirectory);
3091
+ document.getElementById('openMemoryBtnLarge').addEventListener('click', loadMemoryDirectory);
3092
+ document.getElementById('modalClose').addEventListener('click', closeModal);
3093
+ document.getElementById('modalCancel').addEventListener('click', closeModal);
3094
+ document.getElementById('modalSave').addEventListener('click', saveModal);
3095
+
3096
+ modalOverlay.addEventListener('click', (e) => {
3097
+ if (e.target === modalOverlay) closeModal();
3098
+ });
3099
+
3100
+ document.addEventListener('keydown', (e) => {
3101
+ if (e.key === 'Escape') closeModal();
3102
+ });
3103
+
3104
+ // Expose functions to onclick handlers
3105
+ window.openFileModal = openFileModal;
3106
+ window.openNewFileModal = openNewFileModal;
3107
+ window.openEditModal = openEditModal;
3108
+ window.filterMemoryDirectory = filterMemoryDirectory;
3109
+ </script>
3110
+ </body>
3111
+ </html>